Files
wehub-resource-sync 1d1286fadb
Build / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:18:38 +08:00

146 lines
5.1 KiB
Swift

import AppKit
import FinderSync
import Foundation
import OSLog
import SwiftUI
import MacToolsPluginKit
public final class RightClickPluginFactory: NSObject, MacToolsPluginBundleFactory {
public static func makeProvider(context: PluginRuntimeContext) throws -> any PluginProvider {
RightClickPluginProvider(context: context)
}
}
@MainActor
private struct RightClickPluginProvider: PluginProvider {
let context: PluginRuntimeContext
func makePlugins() -> [any MacToolsPlugin] {
[RightClickPlugin(localization: PluginLocalization(bundle: context.resourceBundle))]
}
}
private enum RightClickPermissionID {
static let finderExtension = "finder-extension"
}
@MainActor
final class RightClickPlugin: MacToolsPlugin {
let metadata: PluginMetadata
var onStateChange: (() -> Void)?
var requestPermissionGuidance: ((String) -> Void)?
var shortcutBindingResolver: ((String) -> ShortcutBinding?)?
private let localization: PluginLocalization
private let logger = Logger(
subsystem: Bundle.main.bundleIdentifier ?? "cc.ggbond.mactools",
category: "RightClickPlugin"
)
private var activationObserver: NSObjectProtocol?
init(localization: PluginLocalization = PluginLocalization(bundle: .main)) {
self.localization = localization
self.metadata = PluginMetadata(
id: "right-click",
title: localization.string("metadata.title", defaultValue: "右键工具"),
iconName: "contextualmenu.and.cursorarrow",
iconTint: Color(nsColor: .systemTeal),
order: 74,
defaultDescription: localization.string(
"metadata.description",
defaultValue: "在 Finder 右键菜单中快速新建文件/文件夹、复制路径、在终端打开、用应用打开"
)
)
}
func activate(context _: PluginRuntimeContext) {
RightClickConfigurationStore.setMenuEnabled(true)
guard activationObserver == nil else {
return
}
activationObserver = NotificationCenter.default.addObserver(
forName: NSApplication.didBecomeActiveNotification,
object: nil,
queue: .main
) { [weak self] _ in
Task { @MainActor [weak self] in
self?.onStateChange?()
}
}
}
func deactivate(reason: PluginDeactivationReason) {
if reason.requiresStateCleanup {
RightClickConfigurationStore.setMenuEnabled(false)
}
if let activationObserver {
NotificationCenter.default.removeObserver(activationObserver)
self.activationObserver = nil
}
}
var permissionRequirements: [PluginPermissionRequirement] {
[
PluginPermissionRequirement(
id: RightClickPermissionID.finderExtension,
kind: .automation,
title: localization.string("permission.finderExtension.title", defaultValue: "Finder 扩展"),
description: localization.string(
"permission.finderExtension.description",
defaultValue: "在系统设置中打开 MacTools 右键工具。"
)
)
]
}
var configuration: PluginConfiguration? {
PluginConfiguration(
description: localization.string(
"configuration.description",
defaultValue: "选择 Finder 右键菜单显示哪些项,并管理「用应用打开」的应用列表。"
),
prefersFullHeight: false
) { [localization] _ in
AnyView(RightClickMenuSettingsView(localization: localization))
}
}
func permissionState(for permissionID: String) -> PluginPermissionState {
guard permissionID == RightClickPermissionID.finderExtension else {
return PluginPermissionState(isGranted: true, footnote: nil)
}
let enabled = FIFinderSyncController.isExtensionEnabled
return PluginPermissionState(
isGranted: enabled,
footnote: localization.string(
"permission.finderExtension.footnote",
defaultValue: "入口:登录项与扩展 > Finder 扩展。"
),
statusText: enabled
? localization.string("permission.finderExtension.status.enabled", defaultValue: "已启用")
: localization.string("permission.finderExtension.status.disabled", defaultValue: "未启用"),
statusSystemImage: enabled ? "checkmark.circle.fill" : "exclamationmark.triangle.fill",
statusTone: enabled ? .positive : .caution
)
}
func handlePermissionAction(id permissionID: String) {
guard permissionID == RightClickPermissionID.finderExtension else {
return
}
openExtensionManagementInterface()
}
private func openExtensionManagementInterface() {
logger.info("Opening Finder extension management interface")
FIFinderSyncController.showExtensionManagementInterface()
onStateChange?()
}
}