From 91a02b020eacba4590a1326caa83f6c1df129fb1 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 18 Jun 2022 12:17:59 +0800 Subject: [PATCH 001/122] misc: simplify proxy helper creation --- .../Managers/PrivilegedHelperManager.swift | 39 ++++++------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/ClashX/General/Managers/PrivilegedHelperManager.swift b/ClashX/General/Managers/PrivilegedHelperManager.swift index 03bc72b14..8eaa1be91 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager.swift @@ -112,39 +112,24 @@ class PrivilegedHelperManager { return .success } - private func helperConnection() -> NSXPCConnection? { - // Check that the connection is valid before trying to do an inter process call to helper - if connection == nil { - connection = NSXPCConnection(machServiceName: PrivilegedHelperManager.machServiceName, options: NSXPCConnection.Options.privileged) - connection?.remoteObjectInterface = NSXPCInterface(with: ProxyConfigRemoteProcessProtocol.self) - connection?.invalidationHandler = { - [weak self] in - guard let self = self else { return } - self.connection?.invalidationHandler = nil - OperationQueue.main.addOperation { - self.connection = nil - self._helper = nil - Logger.log("XPC Connection Invalidated") - } - } - connection?.resume() - } - return connection - } + func helper(failture: (() -> Void)? = nil) -> ProxyConfigRemoteProcessProtocol? { - if _helper == nil { - guard let newHelper = helperConnection()?.remoteObjectProxyWithErrorHandler({ error in - Logger.log("Helper connection was closed with error: \(error)") - failture?() - }) as? ProxyConfigRemoteProcessProtocol else { return nil } - _helper = newHelper + connection = NSXPCConnection(machServiceName: PrivilegedHelperManager.machServiceName, options: NSXPCConnection.Options.privileged) + connection?.remoteObjectInterface = NSXPCInterface(with: ProxyConfigRemoteProcessProtocol.self) + connection?.invalidationHandler = { + Logger.log("XPC Connection Invalidated") } - return _helper + connection?.resume() + guard let helper = connection?.remoteObjectProxyWithErrorHandler({ error in + Logger.log("Helper connection was closed with error: \(error)") + failture?() + }) as? ProxyConfigRemoteProcessProtocol else { return nil } + return helper } + var timer: Timer? private func getHelperStatus(callback:@escaping ((Bool)->Void)) { - var called = false let reply:((Bool)->Void) = { installed in From 6df282db2346c3ae7a7915f6f6555a3357d66fbf Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 19 Jun 2022 14:20:02 +0800 Subject: [PATCH 002/122] misc: reduce alert --- ClashX/AppDelegate.swift | 3 ++- ClashX/Extensions/NSUserNotificationCenter+Extension.swift | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 7d255cbe7..da39a1dde 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -424,6 +424,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } else { ConfigManager.shared.isRunning = false proxyModeMenuItem.isEnabled = false + Logger.log(string,level: .error) NSUserNotificationCenter.default.postConfigErrorNotice(msg: string) } Logger.log("Start proxy done") @@ -804,7 +805,7 @@ extension AppDelegate { UserDefaults.standard.removePersistentDomain(forName: domain) UserDefaults.standard.synchronize() } - NSUserNotificationCenter.default.post(title: "Fail on launch protect", info: "You origin Config has been renamed") + NSUserNotificationCenter.default.post(title: "Fail on launch protect", info: "You origin Config has been renamed",notiOnly: false) } DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + Double(Int64(5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: { x.set(0, forKey: "launch_fail_times") diff --git a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift index 4fef6be01..587664d41 100644 --- a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift +++ b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift @@ -10,7 +10,7 @@ import Cocoa import UserNotifications extension NSUserNotificationCenter { - func post(title: String, info: String, identifier: String? = nil, notiOnly: Bool = false) { + func post(title: String, info: String, identifier: String? = nil, notiOnly: Bool = true) { if #available(OSX 10.14, *) { let notificationCenter = UNUserNotificationCenter.current() notificationCenter.delegate = UserNotificationCenterDelegate.shared @@ -129,7 +129,7 @@ extension NSUserNotificationCenter { func postSpeedTestFinishNotice() { post(title: NSLocalizedString("Benchmark", comment: ""), - info: NSLocalizedString("Benchmark Finished!", comment: "")) + info: NSLocalizedString("Benchmark Finished!", comment: ""),notiOnly: false) } func postProxyChangeByOtherAppNotice() { From c6c74dd88855f200f0ecfaf1297be0aec907c92f Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 19 Jun 2022 14:48:24 +0800 Subject: [PATCH 003/122] misc: swift lint # Conflicts: # ClashX.xcodeproj/project.pbxproj # ClashX/General/ApiRequest.swift # ClashX/General/Managers/ProManager.swift # ClashX/General/Managers/Settings.swift # ClashX/General/Managers/TunManager.swift # ClashX/General/Utils/NetworkChangeNotifier.swift # ClashX/ViewControllers/ClashWebViewContoller.swift --- .swiftlint.yml | 19 ++++++++ ClashX.xcodeproj/project.pbxproj | 8 ++-- .../xcshareddata/xcschemes/ClashX.xcscheme | 20 +++++++- ClashX/AppDelegate.swift | 47 +++++++++---------- ClashX/Basic/Logger.swift | 2 +- ClashX/Basic/NSImage+extension.swift | 2 +- .../NSUserNotificationCenter+Extension.swift | 41 ++++++++-------- .../General/Managers/AutoUpgardeManager.swift | 4 +- .../Managers/ClashResourceManager.swift | 5 +- ClashX/General/Managers/ConfigManager.swift | 4 +- ...CloudManager.swift => ICloudManager.swift} | 6 +-- ClashX/General/Managers/MenuItemFactory.swift | 12 ++--- .../PrivilegedHelperManager+Legacy.swift | 8 ++-- .../Managers/PrivilegedHelperManager.swift | 20 ++++---- .../Managers/RemoteConfigManager.swift | 8 ++-- .../Managers/RemoteControlManager.swift | 5 +- ClashX/General/Managers/Settings.swift | 6 +-- ClashX/General/Utils/ClashStatusTool.swift | 2 +- ClashX/General/Utils/JSBridgeHandler.swift | 6 +-- .../General/Utils/NetworkChangeNotifier.swift | 8 ++-- ClashX/Macro/Notification.swift | 2 +- ClashX/Models/ClashProxy.swift | 10 ++-- ClashX/Models/ClashRule.swift | 2 +- ClashX/Vendor/UserDefaultWrapper.swift | 5 +- .../ViewControllers/AboutViewController.swift | 2 +- .../ExternalControlViewController.swift | 2 +- ClashX/Views/ProxyGroupMenu.swift | 2 +- ClashX/Views/ProxyGroupMenuItemView.swift | 2 +- .../Views/ProxyGroupSpeedTestMenuItem.swift | 8 ++-- ClashX/Views/ProxyMenuItem.swift | 4 +- ...emoteConfigUpdateIntervalSettingView.swift | 4 +- ClashX/Views/StatusItemView.swift | 3 +- 32 files changed, 153 insertions(+), 126 deletions(-) create mode 100644 .swiftlint.yml rename ClashX/General/Managers/{iCloudManager.swift => ICloudManager.swift} (97%) diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 000000000..4d10440fa --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,19 @@ +# By default, SwiftLint uses a set of sensible default rules you can adjust: +disabled_rules: # rule identifiers turned on by default to exclude from running + - colon + - identifier_name + - force_cast + - closure_parameter_position + - file_length + - large_tuple + - type_body_length +included: # paths to include during linting. `--path` is ignored if present. + - ClashX +excluded: # paths to ignore during linting. Takes precedence over `included`. + - ClashX/Vendor + - Pods +analyzer_rules: # Rules run by `swiftlint analyze` + - explicit_self +# implicitly +line_length: 300 +# reporter: "xcode" diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 946d2c005..03a417df1 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -53,7 +53,7 @@ 49B445162457CDF000B27E3E /* ClashStatusTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B445152457CDF000B27E3E /* ClashStatusTool.swift */; }; 49B4575D244F4A2A00463C39 /* PrivilegedHelperManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */; }; 49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */; }; - 49BB31E7246853EA008A4CB0 /* iCloudManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BB31E6246853EA008A4CB0 /* iCloudManager.swift */; }; + 49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BB31E6246853EA008A4CB0 /* ICloudManager.swift */; }; 49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BC061B212931F4005A0FE7 /* AboutViewController.swift */; }; 49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */; }; 49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */; }; @@ -176,7 +176,7 @@ 49B445152457CDF000B27E3E /* ClashStatusTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashStatusTool.swift; sourceTree = ""; }; 49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivilegedHelperManager.swift; sourceTree = ""; }; 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrivilegedHelperManager+Legacy.swift"; sourceTree = ""; }; - 49BB31E6246853EA008A4CB0 /* iCloudManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudManager.swift; sourceTree = ""; }; + 49BB31E6246853EA008A4CB0 /* ICloudManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICloudManager.swift; sourceTree = ""; }; 49BC061B212931F4005A0FE7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashProxy.swift; sourceTree = ""; }; 49CF3B1D20CD7463001EBF94 /* ClashX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClashX.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -299,7 +299,7 @@ F9E754CF239CC21F00CEE7CC /* WebPortalManager.swift */, 49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */, 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */, - 49BB31E6246853EA008A4CB0 /* iCloudManager.swift */, + 49BB31E6246853EA008A4CB0 /* ICloudManager.swift */, 499ADAFC2498CC5900C488FE /* RemoteControlManager.swift */, 4929F67E258CE04700A435F6 /* Settings.swift */, ); @@ -751,7 +751,7 @@ F976275C23634DF8000EDEFE /* LoginServiceKit.swift in Sources */, 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */, F92D0B2E236D35C000575E15 /* ProxyItemView.swift in Sources */, - 49BB31E7246853EA008A4CB0 /* iCloudManager.swift in Sources */, + 49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */, 49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */, F92D0B2C236C7C3600575E15 /* MenuItemBaseView.swift in Sources */, 499ADAFD2498CC5900C488FE /* RemoteControlManager.swift in Sources */, diff --git a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme index 5fe716f38..dcd83e90e 100644 --- a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme +++ b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme @@ -1,10 +1,28 @@ + version = "1.7"> + + + + + + + + + + () - + let groups = proxyResp.proxyGroups.filter({$0.type.isAutoGroup}) for group in groups { - group.all?.compactMap{ + group.all?.compactMap { proxyResp.proxiesMap[$0]?.enclosingProvider?.name - }.forEach{ + }.forEach { providers.insert($0) } } - + for group in groups { Logger.log("Start auto health check for group \(group.name)") ApiRequest.healthCheck(proxy: group.name) } - + for provider in providers { Logger.log("Start auto health check for provider \(provider)") ApiRequest.healthCheck(proxy: provider) @@ -590,7 +588,7 @@ extension AppDelegate { } let config = ConfigManager.shared.currentConfig?.copy() config?.mode = mode - ApiRequest.updateOutBoundMode(mode: mode) { success in + ApiRequest.updateOutBoundMode(mode: mode) { _ in ConfigManager.shared.currentConfig = config ConfigManager.selectOutBoundMode = mode MenuItemFactory.recreateProxyMenuItems() @@ -697,8 +695,8 @@ extension AppDelegate { extension AppDelegate { @IBAction func openConfigFolder(_ sender: Any) { - if iCloudManager.shared.isICloudEnable() { - iCloudManager.shared.getUrl() { + if ICloudManager.shared.isICloudEnable() { + ICloudManager.shared.getUrl { url in if let url = url { NSWorkspace.shared.open(url) @@ -728,7 +726,7 @@ extension AppDelegate { @IBAction func actionUpdateRemoteConfig(_ sender: Any) { RemoteConfigManager.shared.updateCheck(ignoreTimeLimit: true, showNotification: true) } - + @IBAction func actionSetUpdateInterval(_ sender: Any) { RemoteConfigManager.showAdd() } @@ -747,6 +745,7 @@ extension AppDelegate { @IBAction func actionUpdateProxyGroupMenu(_ sender: Any) { ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu updateExperimentalFeatureStatus() + print("211") MenuItemFactory.recreateProxyMenuItems() } @@ -780,10 +779,10 @@ extension AppDelegate { #else DispatchQueue.main.asyncAfter(deadline: .now() + 5) { AppCenter.start(withAppSecret: "dce6e9a3-b6e3-4fd2-9f2d-35c767a99663", services: [ - Analytics.self, + Analytics.self ]) } - + #endif } @@ -805,7 +804,7 @@ extension AppDelegate { UserDefaults.standard.removePersistentDomain(forName: domain) UserDefaults.standard.synchronize() } - NSUserNotificationCenter.default.post(title: "Fail on launch protect", info: "You origin Config has been renamed",notiOnly: false) + NSUserNotificationCenter.default.post(title: "Fail on launch protect", info: "You origin Config has been renamed", notiOnly: false) } DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + Double(Int64(5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: { x.set(0, forKey: "launch_fail_times") @@ -842,8 +841,8 @@ extension AppDelegate { } } - if iCloudManager.shared.isICloudEnable() { - iCloudManager.shared.getConfigFilesList { list in + if ICloudManager.shared.isICloudEnable() { + ICloudManager.shared.getConfigFilesList { list in action(list) } } else { diff --git a/ClashX/Basic/Logger.swift b/ClashX/Basic/Logger.swift index 063a8c93d..233cd0349 100644 --- a/ClashX/Basic/Logger.swift +++ b/ClashX/Basic/Logger.swift @@ -16,7 +16,7 @@ class Logger { #if DEBUG DDLog.add(DDOSLogger.sharedInstance) #endif - //default time zone is "UTC" + // default time zone is "UTC" let dataFormatter = DateFormatter() dataFormatter.setLocalizedDateFormatFromTemplate("YYYY/MM/dd HH:mm:ss:SSS") fileLogger.logFormatter = DDLogFileFormatterDefault.init(dateFormatter: dataFormatter) diff --git a/ClashX/Basic/NSImage+extension.swift b/ClashX/Basic/NSImage+extension.swift index 0daecfb3a..610b4e68a 100644 --- a/ClashX/Basic/NSImage+extension.swift +++ b/ClashX/Basic/NSImage+extension.swift @@ -16,7 +16,7 @@ extension NSImage { color.set() - let imageRect = NSRect(origin: NSZeroPoint, size: image.size) + let imageRect = NSRect(origin: NSPoint.zero, size: image.size) imageRect.fill(using: .sourceIn) image.unlockFocus() diff --git a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift index 587664d41..4509b858a 100644 --- a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift +++ b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift @@ -27,7 +27,7 @@ extension NSUserNotificationCenter { self?.postNotification(title: title, info: info, identifier: identifier) } case .notDetermined: - notificationCenter.requestAuthorization(options: .alert) { granted, err in + notificationCenter.requestAuthorization(options: .alert) { granted, _ in if granted { DispatchQueue.main.async { self?.postNotification(title: title, info: info, identifier: identifier) @@ -49,9 +49,9 @@ extension NSUserNotificationCenter { postNotification(title: title, info: info, identifier: identifier) } } - + private func postNotification(title: String, info: String, identifier: String? = nil) { - var userInfo:[String : Any] = [:] + var userInfo: [String: Any] = [:] if let identifier = identifier { userInfo = ["identifier": identifier] } @@ -60,7 +60,7 @@ extension NSUserNotificationCenter { notificationCenter.delegate = UserNotificationCenterDelegate.shared notificationCenter.removeAllDeliveredNotifications() notificationCenter.removeAllPendingNotificationRequests() - let content = UNMutableNotificationContent(); + let content = UNMutableNotificationContent() content.title = title content.body = info content.userInfo = userInfo @@ -83,7 +83,7 @@ extension NSUserNotificationCenter { deliver(notification) } } - + func postNotificationAlert(title: String, info: String, identifier: String? = nil) { if Settings.disableNoti { return @@ -97,41 +97,41 @@ extension NSUserNotificationCenter { UserNotificationCenterDelegate.shared.handleNotificationActive(with: identifier) } } - + func postConfigFileChangeDetectionNotice() { post(title: NSLocalizedString("Config file have been changed", comment: ""), info: NSLocalizedString("Tap to reload config", comment: ""), identifier: "postConfigFileChangeDetectionNotice") } - + func postStreamApiConnectFail(api: String) { post(title: "\(api) api connect error!", info: NSLocalizedString("Use reload config to try reconnect.", comment: "")) } - + func postConfigErrorNotice(msg: String) { let configName = ConfigManager.selectConfigName.count > 0 ? Paths.configFileName(for: ConfigManager.selectConfigName) : "" - + let message = "\(configName): \(msg)" postNotificationAlert(title: NSLocalizedString("Config loading Fail!", comment: ""), info: message) } - + func postSpeedTestBeginNotice() { post(title: NSLocalizedString("Benchmark", comment: ""), info: NSLocalizedString("Benchmark has begun, please wait.", comment: "")) } - + func postSpeedTestingNotice() { post(title: NSLocalizedString("Benchmark", comment: ""), info: NSLocalizedString("Benchmark is processing, please wait.", comment: "")) } - + func postSpeedTestFinishNotice() { post(title: NSLocalizedString("Benchmark", comment: ""), - info: NSLocalizedString("Benchmark Finished!", comment: ""),notiOnly: false) + info: NSLocalizedString("Benchmark Finished!", comment: ""), notiOnly: false) } - + func postProxyChangeByOtherAppNotice() { post(title: NSLocalizedString("System Proxy Changed", comment: ""), info: NSLocalizedString("Proxy settings are changed by another process. ClashX is no longer the default system proxy.", comment: ""), notiOnly: true) @@ -140,33 +140,33 @@ extension NSUserNotificationCenter { class UserNotificationCenterDelegate: NSObject, NSUserNotificationCenterDelegate, UNUserNotificationCenterDelegate { static let shared = UserNotificationCenterDelegate() - + func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) { if let identifier = notification.userInfo?["identifier"] as? String { handleNotificationActive(with: identifier) } center.removeAllDeliveredNotifications() } - + func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { return true } - + @available(macOS 10.14, *) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { - + if let identifier = response.notification.request.content.userInfo["identifier"] as? String { handleNotificationActive(with: identifier) } center.removeAllDeliveredNotifications() completionHandler() } - + @available(macOS 10.14, *) func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler(.alert) } - + func handleNotificationActive(with identifier: String) { switch identifier { case "postConfigFileChangeDetectionNotice": @@ -176,4 +176,3 @@ class UserNotificationCenterDelegate: NSObject, NSUserNotificationCenterDelegate } } } - diff --git a/ClashX/General/Managers/AutoUpgardeManager.swift b/ClashX/General/Managers/AutoUpgardeManager.swift index 9a85da188..0fe8d2799 100644 --- a/ClashX/General/Managers/AutoUpgardeManager.swift +++ b/ClashX/General/Managers/AutoUpgardeManager.swift @@ -32,8 +32,8 @@ class AutoUpgardeManager: NSObject { } return items }() - - private var allowSelectChannel:Bool { + + private var allowSelectChannel: Bool { return Bundle.main.object(forInfoDictionaryKey: "SUDisallowSelectChannel") as? Bool != true } diff --git a/ClashX/General/Managers/ClashResourceManager.swift b/ClashX/General/Managers/ClashResourceManager.swift index 6cb9c298d..a81876b8a 100644 --- a/ClashX/General/Managers/ClashResourceManager.swift +++ b/ClashX/General/Managers/ClashResourceManager.swift @@ -1,4 +1,3 @@ - import Alamofire import AppKit import Foundation @@ -69,7 +68,7 @@ extension ClashResourceManager { @objc private static func updateGeoIP() { guard let url = showCustomAlert() else { return } - AF.download(url, to: { (_, _) in + AF.download(url, to: { (_, _) in let path = kConfigFolderPath.appending("/Country.mmdb") return (URL(fileURLWithPath: path), .removePreviousFile) }).response { res in @@ -92,7 +91,7 @@ extension ClashResourceManager { alert.runModal() } } - + private static func showCustomAlert() -> String? { let alert = NSAlert() alert.messageText = NSLocalizedString("Custom your GEOIP MMDB download address.", comment: "") diff --git a/ClashX/General/Managers/ConfigManager.swift b/ClashX/General/Managers/ConfigManager.swift index 2b30ec958..a31bb9bbf 100644 --- a/ClashX/General/Managers/ConfigManager.swift +++ b/ClashX/General/Managers/ConfigManager.swift @@ -55,8 +55,8 @@ class ConfigManager { } static func watchCurrentConfigFile() { - if iCloudManager.shared.isICloudEnable() { - iCloudManager.shared.getUrl { url in + if ICloudManager.shared.isICloudEnable() { + ICloudManager.shared.getUrl { url in guard let url = url else { return } let configUrl = url.appendingPathComponent(Paths.configFileName(for: selectConfigName)) ConfigFileManager.shared.watchFile(path: configUrl.path) diff --git a/ClashX/General/Managers/iCloudManager.swift b/ClashX/General/Managers/ICloudManager.swift similarity index 97% rename from ClashX/General/Managers/iCloudManager.swift rename to ClashX/General/Managers/ICloudManager.swift index e36a2d769..ff3e8e277 100644 --- a/ClashX/General/Managers/iCloudManager.swift +++ b/ClashX/General/Managers/ICloudManager.swift @@ -8,8 +8,8 @@ import Cocoa -class iCloudManager { - static let shared = iCloudManager() +class ICloudManager { + static let shared = ICloudManager() private let queue = DispatchQueue(label: "com.clashx.icloud") private var metaQuery: NSMetadataQuery? private var enableMenuItem: NSMenuItem? @@ -104,7 +104,7 @@ class iCloudManager { } } -extension iCloudManager { +extension ICloudManager { func addEnableMenuItem(_ menu: inout NSMenu) { let item = NSMenuItem(title: NSLocalizedString("Use iCloud", comment: ""), action: #selector(enableMenuItemTap(sender:)), keyEquivalent: "") menu.addItem(item) diff --git a/ClashX/General/Managers/MenuItemFactory.swift b/ClashX/General/Managers/MenuItemFactory.swift index feb188a7e..6d39d7901 100644 --- a/ClashX/General/Managers/MenuItemFactory.swift +++ b/ClashX/General/Managers/MenuItemFactory.swift @@ -77,14 +77,14 @@ class MenuItemFactory { item.state = ConfigManager.selectConfigName == config ? .on : .off return item } - + if RemoteControlManager.selectConfig != nil { complete([]) return } - if iCloudManager.shared.isICloudEnable() { - iCloudManager.shared.getConfigFilesList { + if ICloudManager.shared.isICloudEnable() { + ICloudManager.shared.getConfigFilesList { complete($0.map { generateMenuItem($0) }) } } else { @@ -113,8 +113,7 @@ class MenuItemFactory { private static func generateSelectorMenuItem(proxyGroup: ClashProxy, proxyInfo: ClashProxyResp, - leftPadding: Bool) -> NSMenuItem? - { + leftPadding: Bool) -> NSMenuItem? { let proxyMap = proxyInfo.proxiesMap let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global @@ -150,8 +149,7 @@ class MenuItemFactory { private static func generateUrlTestFallBackMenuItem(proxyGroup: ClashProxy, proxyInfo: ClashProxyResp, - leftPadding: Bool) -> NSMenuItem? - { + leftPadding: Bool) -> NSMenuItem? { let proxyMap = proxyInfo.proxiesMap let selectedName = proxyGroup.now ?? "" let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "") diff --git a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift index 0b44861a7..aa4b700c4 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift @@ -14,7 +14,7 @@ extension PrivilegedHelperManager { let bash = """ #!/bin/bash set -e - + plistPath=/Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist rm -rf /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName) if [ -e ${plistPath} ]; then @@ -22,10 +22,10 @@ extension PrivilegedHelperManager { rm ${plistPath} fi launchctl remove \(PrivilegedHelperManager.machServiceName) || true - + mkdir -p /Library/PrivilegedHelperTools/ rm -f /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName) - + cp "\(appPath)/Contents/Library/LaunchServices/\(PrivilegedHelperManager.machServiceName)" "/Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)" echo ' @@ -49,7 +49,7 @@ extension PrivilegedHelperManager { ' > ${plistPath} - + launchctl load -w ${plistPath} """ return bash diff --git a/ClashX/General/Managers/PrivilegedHelperManager.swift b/ClashX/General/Managers/PrivilegedHelperManager.swift index 8eaa1be91..64d096294 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager.swift @@ -30,7 +30,7 @@ class PrivilegedHelperManager { func checkInstall() { Logger.log("checkInstall", level: .debug) - + getHelperStatus { [weak self] installed in guard let self = self else {return} if !installed { @@ -112,8 +112,6 @@ class PrivilegedHelperManager { return .success } - - func helper(failture: (() -> Void)? = nil) -> ProxyConfigRemoteProcessProtocol? { connection = NSXPCConnection(machServiceName: PrivilegedHelperManager.machServiceName, options: NSXPCConnection.Options.privileged) connection?.remoteObjectInterface = NSXPCInterface(with: ProxyConfigRemoteProcessProtocol.self) @@ -127,17 +125,17 @@ class PrivilegedHelperManager { }) as? ProxyConfigRemoteProcessProtocol else { return nil } return helper } - + var timer: Timer? - private func getHelperStatus(callback:@escaping ((Bool)->Void)) { + private func getHelperStatus(callback:@escaping ((Bool) -> Void)) { var called = false - let reply:((Bool)->Void) = { + let reply: ((Bool) -> Void) = { installed in if called {return} called = true callback(installed) } - + let helperURL = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LaunchServices/" + PrivilegedHelperManager.machServiceName) guard let helperBundleInfo = CFBundleCopyInfoDictionaryForURL(helperURL as CFURL) as? [String: Any], @@ -153,12 +151,12 @@ class PrivilegedHelperManager { } let timeout: TimeInterval = helperFileExists ? 15 : 5 let time = Date() - + timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { _ in Logger.log("check helper timeout time: \(timeout)") reply(false) } - + helper()?.getVersion { [weak timer] installedHelperVersion in timer?.invalidate() timer = nil @@ -225,7 +223,7 @@ extension PrivilegedHelperManager { } } -fileprivate struct AppAuthorizationRights { +private struct AppAuthorizationRights { static let rightName: NSString = "\(PrivilegedHelperManager.machServiceName).config" as NSString static let rightDefaultRule: Dictionary = adminRightsRule static let rightDescription: CFString = "ProxyConfigHelper wants to configure your proxy setting'" as CFString @@ -235,7 +233,7 @@ fileprivate struct AppAuthorizationRights { "version": 1] } -fileprivate enum DaemonInstallResult { +private enum DaemonInstallResult { case success case authorizationFail case getAdminFail diff --git a/ClashX/General/Managers/RemoteConfigManager.swift b/ClashX/General/Managers/RemoteConfigManager.swift index a1554da65..275fd6b86 100755 --- a/ClashX/General/Managers/RemoteConfigManager.swift +++ b/ClashX/General/Managers/RemoteConfigManager.swift @@ -173,7 +173,7 @@ class RemoteConfigManager { } config.isPlaceHolderName = false - if iCloudManager.shared.isICloudEnable() { + if ICloudManager.shared.isICloudEnable() { ConfigFileManager.shared.stopWatchConfigFile() } else if config.name == ConfigManager.selectConfigName { ConfigFileManager.shared.pauseForNextChange() @@ -192,8 +192,8 @@ class RemoteConfigManager { } } - if iCloudManager.shared.isICloudEnable() { - iCloudManager.shared.getUrl { url in + if ICloudManager.shared.isICloudEnable() { + ICloudManager.shared.getUrl { url in guard let url = url else { return } let saveUrl = url.appendingPathComponent(Paths.configFileName(for: config.name)) saveAction(saveUrl.path) @@ -214,7 +214,7 @@ class RemoteConfigManager { return res } } - + static func showAdd() { let alertView = NSAlert() alertView.addButton(withTitle: NSLocalizedString("OK", comment: "")) diff --git a/ClashX/General/Managers/RemoteControlManager.swift b/ClashX/General/Managers/RemoteControlManager.swift index af416fc52..9ab145fed 100644 --- a/ClashX/General/Managers/RemoteControlManager.swift +++ b/ClashX/General/Managers/RemoteControlManager.swift @@ -28,7 +28,6 @@ class RemoteControlManager { static var selected: String } - static let shared = RemoteControlManager() static var configs: [RemoteControl] = loadConfig() { didSet { @@ -44,7 +43,7 @@ class RemoteControlManager { Recorder.selected = selectConfig?.uuid ?? "" } } - + private static var menuSeparator: NSMenuItem? static func loadConfig() -> [RemoteControl] { @@ -67,7 +66,7 @@ class RemoteControlManager { RemoteControlManager.recoverSelection() } } - + static private func recoverSelection() { if Recorder.selected != "" { if let config = configs.first(where: { $0.uuid == Recorder.selected }) { diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index a2b9fc3c6..7fb433d4e 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -10,16 +10,16 @@ import Foundation enum Settings { @UserDefault("mmdbDownloadUrl", defaultValue: "") static var mmdbDownloadUrl:String - + @UserDefault("filterInterface", defaultValue: true) static var filterInterface:Bool - + @UserDefault("disableNoti", defaultValue: false) static var disableNoti:Bool @UserDefault("usePacMode", defaultValue: false) static var usePacMode:Bool - + @UserDefault("configAutoUpdateInterval", defaultValue: 48*60*60) static var configAutoUpdateInterval:TimeInterval } diff --git a/ClashX/General/Utils/ClashStatusTool.swift b/ClashX/General/Utils/ClashStatusTool.swift index 88057f09f..1e100e9f8 100644 --- a/ClashX/General/Utils/ClashStatusTool.swift +++ b/ClashX/General/Utils/ClashStatusTool.swift @@ -26,7 +26,7 @@ class ClashStatusTool { } NSApp.terminate(nil) } - + } } } diff --git a/ClashX/General/Utils/JSBridgeHandler.swift b/ClashX/General/Utils/JSBridgeHandler.swift index ac0d998ab..835d821a5 100644 --- a/ClashX/General/Utils/JSBridgeHandler.swift +++ b/ClashX/General/Utils/JSBridgeHandler.swift @@ -16,7 +16,7 @@ class JsBridgeUtil { bridge.setWebViewDelegate(delegate) - bridge.registerHandler("isSystemProxySet") { anydata, responseCallback in + bridge.registerHandler("isSystemProxySet") { _, responseCallback in responseCallback?(ConfigManager.shared.proxyPortAutoSet) } @@ -89,13 +89,13 @@ class JsBridgeUtil { let data = [ "host": host, "port": port, - "secret": ConfigManager.shared.overrideSecret ?? ConfigManager.shared.apiSecret, + "secret": ConfigManager.shared.overrideSecret ?? ConfigManager.shared.apiSecret ] callback?(data) } // ping-pong - bridge.registerHandler("ping") { [weak bridge] anydata, responseCallback in + bridge.registerHandler("ping") { [weak bridge] _, responseCallback in bridge?.callHandler("pong") responseCallback?(true) } diff --git a/ClashX/General/Utils/NetworkChangeNotifier.swift b/ClashX/General/Utils/NetworkChangeNotifier.swift index 76d3dfb69..492e3a56c 100644 --- a/ClashX/General/Utils/NetworkChangeNotifier.swift +++ b/ClashX/General/Utils/NetworkChangeNotifier.swift @@ -27,7 +27,7 @@ class NetworkChangeNotifier { name: NSWorkspace.didWakeNotification, object: nil ) - let changed: SCDynamicStoreCallBack = { dynamicStore, _, _ in + let changed: SCDynamicStoreCallBack = { _, _, _ in NotificationCenter.default.post(name: .systemNetworkStatusDidChange, object: nil) } var dynamicContext = SCDynamicStoreContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) @@ -43,7 +43,7 @@ class NetworkChangeNotifier { } private static func startIPChangeWatch() { - let changed: SCDynamicStoreCallBack = { dynamicStore, _, _ in + let changed: SCDynamicStoreCallBack = { _, _, _ in NotificationCenter.default.post(name: .systemNetworkStatusIPUpdate, object: nil) } var dynamicContext = SCDynamicStoreContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) @@ -91,12 +91,12 @@ class NetworkChangeNotifier { return http == currentPort && https == currentPort && socks == currentSocks } } - + static func hasInterfaceProxySetToClash() -> Bool { let currentPort = ConfigManager.shared.currentConfig?.usedHttpPort let currentSocks = ConfigManager.shared.currentConfig?.usedSocksPort if let prefRef = SCPreferencesCreate(nil, "ClashX" as CFString, nil), - let sets = SCPreferencesGetValue(prefRef, kSCPrefNetworkServices){ + let sets = SCPreferencesGetValue(prefRef, kSCPrefNetworkServices) { for key in sets.allKeys { let dict = sets[key] as? NSDictionary let proxySettings = dict?["Proxies"] as? [String:Any] diff --git a/ClashX/Macro/Notification.swift b/ClashX/Macro/Notification.swift index f0d9a6282..d0beae30c 100644 --- a/ClashX/Macro/Notification.swift +++ b/ClashX/Macro/Notification.swift @@ -18,5 +18,5 @@ extension Notification.Name { static func proxyUpdate(for name: ClashProxyName) -> Notification.Name { return Notification.Name("kProxyUpdate_\(name)") } - + } diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index f821f7165..362126f75 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -58,8 +58,8 @@ class ClashProxySpeedHistory: Codable { let time: Date let delay: Int - class hisDateFormaterInstance { - static let shared = hisDateFormaterInstance() + class HisDateFormaterInstance { + static let shared = HisDateFormaterInstance() lazy var formater: DateFormatter = { var f = DateFormatter() f.dateFormat = "HH:mm" @@ -75,7 +75,7 @@ class ClashProxySpeedHistory: Codable { }() lazy var dateDisplay: String = { - return hisDateFormaterInstance.shared.formater.string(from: time) + return HisDateFormaterInstance.shared.formater.string(from: time) }() lazy var displayString: String = "\(dateDisplay) \(delayDisplay)" @@ -87,8 +87,8 @@ class ClashProxy: Codable { let all: [ClashProxyName]? let history: [ClashProxySpeedHistory] let now: ClashProxyName? - weak var enclosingResp: ClashProxyResp? = nil - weak var enclosingProvider: ClashProvider? = nil + weak var enclosingResp: ClashProxyResp? + weak var enclosingProvider: ClashProvider? enum SpeedtestAbleItem { case proxy(name: ClashProxyName) diff --git a/ClashX/Models/ClashRule.swift b/ClashX/Models/ClashRule.swift index ba3c39e5e..8be225b06 100644 --- a/ClashX/Models/ClashRule.swift +++ b/ClashX/Models/ClashRule.swift @@ -15,7 +15,7 @@ class ClashRule: Codable { } class ClashRuleResponse: Codable { - var rules: [ClashRule]? = nil + var rules: [ClashRule]? static func empty() -> ClashRuleResponse { return ClashRuleResponse() diff --git a/ClashX/Vendor/UserDefaultWrapper.swift b/ClashX/Vendor/UserDefaultWrapper.swift index 33c9a5191..0b5aa38d3 100644 --- a/ClashX/Vendor/UserDefaultWrapper.swift +++ b/ClashX/Vendor/UserDefaultWrapper.swift @@ -15,13 +15,13 @@ public struct UserDefault { let key: String let defaultValue: Value var userDefaults: UserDefaults - + public init(_ key: String, defaultValue: Value, userDefaults: UserDefaults = .standard) { self.key = key self.defaultValue = defaultValue self.userDefaults = userDefaults } - + public var wrappedValue: Value { get { return userDefaults.object(forKey: key) as? Value ?? defaultValue @@ -64,7 +64,6 @@ extension UInt64: PropertyListValue {} extension Double: PropertyListValue {} extension Float: PropertyListValue {} - extension Array: PropertyListValue where Element: PropertyListValue {} extension Dictionary: PropertyListValue where Key == String, Value: PropertyListValue {} diff --git a/ClashX/ViewControllers/AboutViewController.swift b/ClashX/ViewControllers/AboutViewController.swift index c6a06705c..8b9389be7 100644 --- a/ClashX/ViewControllers/AboutViewController.swift +++ b/ClashX/ViewControllers/AboutViewController.swift @@ -64,7 +64,7 @@ class HyperlinkTextField: NSTextField { super.awakeFromNib() let attributes: [NSAttributedString.Key: Any] = [ NSAttributedString.Key.foregroundColor: NSColor.linkColor, - NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue as AnyObject, + NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue as AnyObject ] attributedStringValue = NSAttributedString(string: stringValue, attributes: attributes) } diff --git a/ClashX/ViewControllers/ExternalControlViewController.swift b/ClashX/ViewControllers/ExternalControlViewController.swift index 48b1b72d2..73573f901 100644 --- a/ClashX/ViewControllers/ExternalControlViewController.swift +++ b/ClashX/ViewControllers/ExternalControlViewController.swift @@ -137,7 +137,7 @@ class ExternalControlAddView: NSView { nameField.leadingAnchor.constraint(equalTo: urlTextField.leadingAnchor), nameLabel.centerYAnchor.constraint(equalTo: nameField.centerYAnchor), nameLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), - nameField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 5), + nameField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 5) ]) } diff --git a/ClashX/Views/ProxyGroupMenu.swift b/ClashX/Views/ProxyGroupMenu.swift index 597b7fde7..8a75ec5e9 100644 --- a/ClashX/Views/ProxyGroupMenu.swift +++ b/ClashX/Views/ProxyGroupMenu.swift @@ -7,7 +7,7 @@ // import AppKit -@objc protocol ProxyGroupMenuHighlightDelegate: class { +@objc protocol ProxyGroupMenuHighlightDelegate: AnyObject { func highlight(item: NSMenuItem?) } diff --git a/ClashX/Views/ProxyGroupMenuItemView.swift b/ClashX/Views/ProxyGroupMenuItemView.swift index 6ef707488..a2dcbff4e 100644 --- a/ClashX/Views/ProxyGroupMenuItemView.swift +++ b/ClashX/Views/ProxyGroupMenuItemView.swift @@ -31,7 +31,7 @@ class ProxyGroupMenuItemView: MenuItemBaseView { return [groupNameLabel.cell, selectProxyLabel.cell, arrowLabel.cell] } - init(group: ClashProxyName, targetProxy: ClashProxyName, hasLeftPadding: Bool, observeUpdate:Bool = true) { + init(group: ClashProxyName, targetProxy: ClashProxyName, hasLeftPadding: Bool, observeUpdate: Bool = true) { groupNameLabel = VibrancyTextField(labelWithString: group) selectProxyLabel = VibrancyTextField(labelWithString: targetProxy) super.init(autolayout: true) diff --git a/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift b/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift index b66e14eae..dc7ed368b 100644 --- a/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift +++ b/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift @@ -47,12 +47,12 @@ class ProxyGroupSpeedTestMenuItem: NSMenuItem { ApiRequest.getMergedProxyData { [weak self] proxyResp in guard let self = self else { return } var providers = Set() - self.proxyGroup.all?.compactMap{ + self.proxyGroup.all?.compactMap { proxyResp?.proxiesMap[$0]?.enclosingProvider?.name - }.forEach{ + }.forEach { providers.insert($0) } - providers.forEach{ + providers.forEach { ApiRequest.healthCheck(proxy: $0) } } @@ -66,7 +66,7 @@ extension ProxyGroupSpeedTestMenuItem: ProxyGroupMenuHighlightDelegate { } } -fileprivate class ProxyGroupSpeedTestMenuItemView: MenuItemBaseView { +private class ProxyGroupSpeedTestMenuItemView: MenuItemBaseView { private let label: NSTextField init(_ title: String) { diff --git a/ClashX/Views/ProxyMenuItem.swift b/ClashX/Views/ProxyMenuItem.swift index 18bf61a00..6e04e894e 100644 --- a/ClashX/Views/ProxyMenuItem.swift +++ b/ClashX/Views/ProxyMenuItem.swift @@ -108,7 +108,7 @@ extension ProxyMenuItem { func getAttributedTitle(name: String, delay: String?) -> NSAttributedString { let paragraph = NSMutableParagraphStyle() paragraph.tabStops = [ - NSTextTab(textAlignment: .right, location: 65 + maxProxyNameLength, options: [:]), + NSTextTab(textAlignment: .right, location: 65 + maxProxyNameLength, options: [:]) ] let proxyName = name.replacingOccurrences(of: "\t", with: " ") let str: String @@ -122,7 +122,7 @@ extension ProxyMenuItem { string: str, attributes: [ NSAttributedString.Key.paragraphStyle: paragraph, - NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 14), + NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 14) ] ) diff --git a/ClashX/Views/RemoteConfigUpdateIntervalSettingView.swift b/ClashX/Views/RemoteConfigUpdateIntervalSettingView.swift index 74b3cc5de..4b8e272f8 100644 --- a/ClashX/Views/RemoteConfigUpdateIntervalSettingView.swift +++ b/ClashX/Views/RemoteConfigUpdateIntervalSettingView.swift @@ -14,11 +14,11 @@ class RemoteConfigUpdateIntervalSettingView: NSView { super.init(frame: .zero) setup() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + let stackView = NSStackView() let textfield = NSTextField() func setup() { diff --git a/ClashX/Views/StatusItemView.swift b/ClashX/Views/StatusItemView.swift index 7b9e3bdc9..d710766d4 100644 --- a/ClashX/Views/StatusItemView.swift +++ b/ClashX/Views/StatusItemView.swift @@ -71,7 +71,7 @@ class StatusItemView: NSView { imageView.image = menuImage.tint(color: enableProxy ? selectedColor : unselectedColor) updateStatusItemView() } - + func getSpeedString(for byte: Int) -> String { let kb = byte / 1024 if kb < 1024 { @@ -88,7 +88,6 @@ class StatusItemView: NSView { } } } - func updateSpeedLabel(up: Int, down: Int) { guard !speedContainerView.isHidden else { return } From fd648908fa57f8dea394a4b46aef5383af03c6b8 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 19 Jun 2022 14:50:43 +0800 Subject: [PATCH 004/122] misc: update ci --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f77dcc653..80081ecc4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,11 +4,11 @@ on: [push] env: FASTLANE_SKIP_UPDATE_CHECK: true - DEVELOPER_DIR: /Applications/Xcode_13.1.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_13.4.1.app/Contents/Developer jobs: build: - runs-on: macos-11 + runs-on: macos-12 steps: - uses: actions/checkout@v2 - name: import certs From f80e02b0d9de7ae0bdafc553fd1b766699b2db65 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 19 Jun 2022 14:57:37 +0800 Subject: [PATCH 005/122] misc: add update mmdb log --- ClashX/General/Managers/ClashResourceManager.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ClashX/General/Managers/ClashResourceManager.swift b/ClashX/General/Managers/ClashResourceManager.swift index a81876b8a..6b1406c25 100644 --- a/ClashX/General/Managers/ClashResourceManager.swift +++ b/ClashX/General/Managers/ClashResourceManager.swift @@ -33,11 +33,13 @@ class ClashResourceManager { let versionChange = AppVersionUtil.hasVersionChanged || AppVersionUtil.isFirstLaunch let customMMDBSet = !Settings.mmdbDownloadUrl.isEmpty if !vaild || (versionChange && customMMDBSet) { + Logger.log("removing new mmdb file") try? fileManage.removeItem(atPath: destMMDBPath) } } if !fileManage.fileExists(atPath: destMMDBPath) { + Logger.log("installing new mmdb file") if let mmdbUrl = Bundle.main.url(forResource: "Country.mmdb", withExtension: "gz") { do { let data = try Data(contentsOf: mmdbUrl).gunzipped() From ca2fa203b2d925a26ff021e0f5b914abde767a30 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Jun 2022 21:05:37 +0800 Subject: [PATCH 006/122] misc: update deps and version --- ClashX.xcodeproj/project.pbxproj | 8 +-- .../xcshareddata/xcschemes/ClashX.xcscheme | 4 +- ClashX/goClash/go.mod | 23 ++++---- ClashX/goClash/go.sum | 52 ++++++++----------- Podfile.lock | 12 ++--- 5 files changed, 46 insertions(+), 53 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 03a417df1..53c8806c3 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -938,7 +938,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; - CURRENT_PROJECT_VERSION = 1.91.1; + CURRENT_PROJECT_VERSION = 1.94.0; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -960,7 +960,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.12; - MARKETING_VERSION = 1.91.1; + MARKETING_VERSION = 1.94.0; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -985,7 +985,7 @@ COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1.91.1; + CURRENT_PROJECT_VERSION = 1.94.0; DEPLOYMENT_POSTPROCESSING = YES; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; @@ -1008,7 +1008,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.12; - MARKETING_VERSION = 1.91.1; + MARKETING_VERSION = 1.94.0; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme index dcd83e90e..83ba271a9 100644 --- a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme +++ b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme @@ -15,8 +15,8 @@ diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 099c0fe0e..11b714f7c 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -1,34 +1,33 @@ module github.com/yichengchen/clashX/ClashX require ( - github.com/Dreamacro/clash v1.10.1-0.20220320033218-8c9e0b388437 - github.com/oschwald/geoip2-golang v1.6.1 + github.com/Dreamacro/clash v1.11.0 + github.com/oschwald/geoip2-golang v1.7.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) require ( - github.com/Dreamacro/go-shadowsocks2 v0.1.7 // indirect github.com/go-chi/chi/v5 v5.0.7 // indirect - github.com/go-chi/cors v1.2.0 // indirect + github.com/go-chi/cors v1.2.1 // indirect github.com/go-chi/render v1.0.1 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd // indirect - github.com/miekg/dns v1.1.47 // indirect - github.com/oschwald/maxminddb-golang v1.8.0 // indirect + github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect + github.com/miekg/dns v1.1.49 // indirect + github.com/oschwald/maxminddb-golang v1.9.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect golang.org/x/mod v0.4.2 // indirect - golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect + golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 // indirect + golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect + golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) go 1.18 diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index a4971c581..29f299732 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,15 +1,13 @@ -github.com/Dreamacro/clash v1.10.1-0.20220320033218-8c9e0b388437 h1:vdg/f0ZmHliM88+UAkdqS7brXOAUQXBv1+c620XRxk4= -github.com/Dreamacro/clash v1.10.1-0.20220320033218-8c9e0b388437/go.mod h1:Qre43BqAGIKpvRMvLB0cflDohlH2FmN/RrQZOlhi1jY= -github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q= -github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc= +github.com/Dreamacro/clash v1.11.0 h1:M/jl+7aDYU8L8f9YUeODBUyKuHazV+jyVuqtJuRFaxY= +github.com/Dreamacro/clash v1.11.0/go.mod h1:f1DHEEkPOQnST2D1H6sWmDxakxSwZ0pmvueNOOpwagM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE= -github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= +github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= @@ -23,8 +21,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd h1:efcJu2Vzz6DoSq245deWNzTz6l/gsqdphm3FjmI88/g= -github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= +github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= @@ -37,12 +35,12 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8= -github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/oschwald/geoip2-golang v1.6.1 h1:GKxT3yaWWNXSb7vj6D7eoJBns+lGYgx08QO0UcNm0YY= -github.com/oschwald/geoip2-golang v1.6.1/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s= -github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= -github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= +github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= +github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= +github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= +github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -55,8 +53,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -67,9 +64,8 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -82,14 +78,14 @@ golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -99,7 +95,6 @@ golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -111,8 +106,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -131,7 +126,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/Podfile.lock b/Podfile.lock index 15ec51fc2..69e8bb127 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,8 +1,8 @@ PODS: - - Alamofire (5.5.0) - - AppCenter/Analytics (4.4.1): + - Alamofire (5.6.1) + - AppCenter/Analytics (4.4.2): - AppCenter/Core - - AppCenter/Core (4.4.1) + - AppCenter/Core (4.4.2) - CocoaLumberjack/Core (3.7.4) - CocoaLumberjack/Swift (3.7.4): - CocoaLumberjack/Core @@ -51,8 +51,8 @@ SPEC REPOS: - WebViewJavascriptBridge SPEC CHECKSUMS: - Alamofire: 1c4fb5369c3fe93d2857c780d8bbe09f06f97e7c - AppCenter: b0b6f1190215b5f983c42934db718f3b46fff3c0 + Alamofire: 87bd8c952f9a4454320fce00d9cc3de57bcadaf5 + AppCenter: b0eca112a27b71e97488ffa1949ee38c7abd4b79 CocoaLumberjack: 543c79c114dadc3b1aba95641d8738b06b05b646 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa @@ -67,4 +67,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 3d889ed6c786fdaa0a2b35a70d4c5abcb240da2b -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 From cf8f489d21cb35d477b59c4b83563fa041c8afad Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Jun 2022 21:20:21 +0800 Subject: [PATCH 007/122] misc: fix build issue --- ClashX.xcodeproj/project.pbxproj | 2 +- ClashX/General/ApiRequest.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 53c8806c3..e2c5f3a00 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -674,7 +674,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nLOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${FRAMEWORKS_FOLDER_PATH}\"\n\n# By default, use the configured code signing identity for the project/target\nIDENTITY=\"${CODE_SIGN_IDENTITY}\"\nif [ \"$IDENTITY\" == \"\" ]\nthen\n # If a code signing identity is not specified, use ad hoc signing\n IDENTITY=\"-\"\nfi\ncodesign --verbose --force --deep -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"\ncodesign --verbose --force -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A\"\n"; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nLOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${FRAMEWORKS_FOLDER_PATH}\"\n\n# By default, use the configured code signing identity for the project/target\nIDENTITY=\"${EXPANDED_CODE_SIGN_IDENTITY_NAME}\"\nif [ \"$IDENTITY\" == \"\" ]\nthen\n # If a code signing identity is not specified, use ad hoc signing\n IDENTITY=\"-\"\nfi\ncodesign --verbose --force --deep -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"\ncodesign --verbose --force -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A\"\n"; }; A741C26F5755233F0D7CEC6F /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index ec910c6e8..8bdb6bc8e 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -105,8 +105,8 @@ class ApiRequest { } static func requestConfigUpdate(configName: String, callback: @escaping ((ErrorString?) -> Void)) { - if iCloudManager.shared.isICloudEnable() { - iCloudManager.shared.getUrl { url in + if ICloudManager.shared.isICloudEnable() { + ICloudManager.shared.getUrl { url in guard let url = url else { callback("icloud error") return From b35ded2920fefd4ad024b04fbb1d69387557df91 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 4 Sep 2022 11:39:08 +0800 Subject: [PATCH 008/122] misc: update deps --- .github/workflows/main.yml | 2 +- ClashX.xcodeproj/project.pbxproj | 8 ++-- ClashX/goClash/go.mod | 31 ++++++++------- ClashX/goClash/go.sum | 65 ++++++++++++++++++-------------- ClashX/goClash/main.go | 1 - Podfile.lock | 10 ++--- 6 files changed, 63 insertions(+), 54 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 80081ecc4..d4bd20275 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: - name: setup Go uses: actions/setup-go@v2 with: - go-version: 1.18.x + go-version: 1.19.x - name: install deps run: | diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index e2c5f3a00..46fc62cf4 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -938,7 +938,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; - CURRENT_PROJECT_VERSION = 1.94.0; + CURRENT_PROJECT_VERSION = 1.95.1; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -960,7 +960,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.12; - MARKETING_VERSION = 1.94.0; + MARKETING_VERSION = 1.95.1; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -985,7 +985,7 @@ COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1.94.0; + CURRENT_PROJECT_VERSION = 1.95.1; DEPLOYMENT_POSTPROCESSING = YES; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; @@ -1008,7 +1008,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.12; - MARKETING_VERSION = 1.94.0; + MARKETING_VERSION = 1.95.1; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 11b714f7c..9960188d6 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -1,33 +1,36 @@ module github.com/yichengchen/clashX/ClashX +go 1.19 + require ( - github.com/Dreamacro/clash v1.11.0 - github.com/oschwald/geoip2-golang v1.7.0 + github.com/Dreamacro/clash v1.11.9-0.20220902085900-4f291fa51350 + github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) require ( + github.com/ajg/form v1.5.1 // indirect github.com/go-chi/chi/v5 v5.0.7 // indirect github.com/go-chi/cors v1.2.1 // indirect - github.com/go-chi/render v1.0.1 // indirect + github.com/go-chi/render v1.0.2 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect - github.com/miekg/dns v1.1.49 // indirect - github.com/oschwald/maxminddb-golang v1.9.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/miekg/dns v1.1.50 // indirect + github.com/oschwald/maxminddb-golang v1.10.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect - go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + go.uber.org/atomic v1.10.0 // indirect + golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 // indirect golang.org/x/mod v0.4.2 // indirect - golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 // indirect - golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect - golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect + golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c // indirect + golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -go 1.18 diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 29f299732..8c9bc6976 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,7 @@ -github.com/Dreamacro/clash v1.11.0 h1:M/jl+7aDYU8L8f9YUeODBUyKuHazV+jyVuqtJuRFaxY= -github.com/Dreamacro/clash v1.11.0/go.mod h1:f1DHEEkPOQnST2D1H6sWmDxakxSwZ0pmvueNOOpwagM= +github.com/Dreamacro/clash v1.11.9-0.20220902085900-4f291fa51350 h1:YB8nz7XydRfGuSdQhG0tXF6mEfFhb9yZz6MR0JP3zMA= +github.com/Dreamacro/clash v1.11.9-0.20220902085900-4f291fa51350/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -8,8 +10,8 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= -github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= +github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= +github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -21,13 +23,18 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= -github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= +github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= @@ -35,37 +42,36 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= -github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= -github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= -github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= -github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= +github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= +github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= +github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to= +golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -80,12 +86,12 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -94,7 +100,6 @@ golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -106,8 +111,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -124,8 +130,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 8e62c8b9b..24bdd97c1 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -76,7 +76,6 @@ func readConfig(path string) ([]byte, error) { return data, err } - func parseDefaultConfigThenStart(checkPort, allowLan bool) (*config.Config, error) { buf, err := readConfig(constant.Path.Config()) if err != nil { diff --git a/Podfile.lock b/Podfile.lock index 69e8bb127..e4ccc10a4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,8 +1,8 @@ PODS: - - Alamofire (5.6.1) - - AppCenter/Analytics (4.4.2): + - Alamofire (5.6.2) + - AppCenter/Analytics (4.4.3): - AppCenter/Core - - AppCenter/Core (4.4.2) + - AppCenter/Core (4.4.3) - CocoaLumberjack/Core (3.7.4) - CocoaLumberjack/Swift (3.7.4): - CocoaLumberjack/Core @@ -51,8 +51,8 @@ SPEC REPOS: - WebViewJavascriptBridge SPEC CHECKSUMS: - Alamofire: 87bd8c952f9a4454320fce00d9cc3de57bcadaf5 - AppCenter: b0eca112a27b71e97488ffa1949ee38c7abd4b79 + Alamofire: d368e1ff8a298e6dde360e35a3e68e6c610e7204 + AppCenter: 3fd04aa1b166e16fdb03ec81dabe488aece83fbd CocoaLumberjack: 543c79c114dadc3b1aba95641d8738b06b05b646 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa From 0b1dcb85011f72f1dca99935269d4127be498b74 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 6 Nov 2022 21:38:00 +0800 Subject: [PATCH 009/122] fix: add AssociatedBundleIdentifiers --- ProxyConfigHelper/Helper-Info.plist | 4 ++-- ProxyConfigHelper/Helper-Launchd.plist | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ProxyConfigHelper/Helper-Info.plist b/ProxyConfigHelper/Helper-Info.plist index 7c3ec8662..ac9ef290a 100755 --- a/ProxyConfigHelper/Helper-Info.plist +++ b/ProxyConfigHelper/Helper-Info.plist @@ -9,9 +9,9 @@ CFBundleName com.west2online.ClashX.ProxyConfigHelper CFBundleShortVersionString - 1.8 + 2.0 CFBundleVersion - 3 + 5 SMAuthorizedClients anchor apple generic and identifier "com.west2online.ClashX" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = MEWHFZ92DY) diff --git a/ProxyConfigHelper/Helper-Launchd.plist b/ProxyConfigHelper/Helper-Launchd.plist index d02328484..d63b8e4f1 100755 --- a/ProxyConfigHelper/Helper-Launchd.plist +++ b/ProxyConfigHelper/Helper-Launchd.plist @@ -2,6 +2,8 @@ + AssociatedBundleIdentifiers + com.west2online.ClashX Label com.west2online.ClashX.ProxyConfigHelper MachServices From a2cc791361a81911eddffe3506475f64ced79474 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 7 Nov 2022 14:23:40 +0800 Subject: [PATCH 010/122] misc: update LoginServiceKit for macos 13 --- .../LoginServiceKit/LoginServiceKit.swift | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift b/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift index c2f72277e..6e78bd0fb 100644 --- a/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift +++ b/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift @@ -47,8 +47,9 @@ // import Cocoa +import ServiceManagement -public final class LoginServiceKit: NSObject { +public final class LoginServiceKit { private static var snapshot: (list: LSSharedFileList, items: [LSSharedFileListItem])? { guard let list = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil)?.takeRetainedValue() else { return nil @@ -57,6 +58,9 @@ public final class LoginServiceKit: NSObject { } public static func isExistLoginItems(at path: String = Bundle.main.bundlePath) -> Bool { + if #available(macOS 13.0, *) { + return SMAppService.mainApp.status == .enabled + } return loginItem(at: path) != nil } @@ -65,6 +69,17 @@ public final class LoginServiceKit: NSObject { guard isExistLoginItems(at: path) == false else { return false } + + if #available(macOS 13.0, *) { + do { + try SMAppService.mainApp.register() + return true + } catch let err { + Logger.log("add loginItem error: \(err.localizedDescription)", level: .error) + return false + } + } + guard let (list, _) = snapshot else { return false } @@ -78,6 +93,16 @@ public final class LoginServiceKit: NSObject { guard isExistLoginItems(at: path) == true else { return false } + + if #available(macOS 13.0, *) { + do { + try SMAppService.mainApp.unregister() + return true + } catch let err { + Logger.log("remove loginItem error: \(err.localizedDescription)", level: .error) + return false + } + } guard let (list, items) = snapshot else { return false } From 8179ec739ac447318c6cea9029a45246c93354df Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 20 Nov 2022 10:34:31 +0800 Subject: [PATCH 011/122] misc: update MACOSX_DEPLOYMENT_TARGET to 10.14 --- ClashX.xcodeproj/project.pbxproj | 8 ++++---- ClashX/Views/StatusItemView.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 46fc62cf4..d0201dd45 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -959,7 +959,7 @@ "$(PROJECT_DIR)/ClashX", "$(PROJECT_DIR)/ClashX/goClash", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.14; MARKETING_VERSION = 1.95.1; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1007,7 +1007,7 @@ "$(PROJECT_DIR)/ClashX", "$(PROJECT_DIR)/ClashX/goClash", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.14; MARKETING_VERSION = 1.95.1; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; @@ -1030,7 +1030,7 @@ DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.14; MARKETING_VERSION = 1.6; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -1062,7 +1062,7 @@ DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.14; MARKETING_VERSION = 1.6; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( diff --git a/ClashX/Views/StatusItemView.swift b/ClashX/Views/StatusItemView.swift index d710766d4..3fc0e9052 100644 --- a/ClashX/Views/StatusItemView.swift +++ b/ClashX/Views/StatusItemView.swift @@ -119,7 +119,7 @@ extension NSStatusItem { let img = NSImage(size: view.bounds.size) img.addRepresentation(rep) img.isTemplate = true - image = img + button?.image = img } } } From eb69af72a96f1b5a241deecbe5f4d44673f29da8 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 20 Nov 2022 10:34:43 +0800 Subject: [PATCH 012/122] misc: update deps --- Podfile | 2 +- Podfile.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Podfile b/Podfile index 17ad581b6..e5e55e772 100644 --- a/Podfile +++ b/Podfile @@ -9,7 +9,7 @@ post_install do |installer| end end if config.build_settings['MACOSX_DEPLOYMENT_TARGET'] == '' - config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.10' + config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.13' end end end diff --git a/Podfile.lock b/Podfile.lock index e4ccc10a4..b28e27a38 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,10 +1,10 @@ PODS: - Alamofire (5.6.2) - - AppCenter/Analytics (4.4.3): + - AppCenter/Analytics (5.0.0): - AppCenter/Core - - AppCenter/Core (4.4.3) - - CocoaLumberjack/Core (3.7.4) - - CocoaLumberjack/Swift (3.7.4): + - AppCenter/Core (5.0.0) + - CocoaLumberjack/Core (3.8.0) + - CocoaLumberjack/Swift (3.8.0): - CocoaLumberjack/Core - FlexibleDiff (0.0.9) - GzipSwift (5.1.1) @@ -52,8 +52,8 @@ SPEC REPOS: SPEC CHECKSUMS: Alamofire: d368e1ff8a298e6dde360e35a3e68e6c610e7204 - AppCenter: 3fd04aa1b166e16fdb03ec81dabe488aece83fbd - CocoaLumberjack: 543c79c114dadc3b1aba95641d8738b06b05b646 + AppCenter: 30ac5adefc8fc41d9530f1cc91829810a6af4c93 + CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa LetsMove: 7b9fe44737707d984fbd3f47af46609a9a07b461 @@ -65,6 +65,6 @@ SPEC CHECKSUMS: SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 -PODFILE CHECKSUM: 3d889ed6c786fdaa0a2b35a70d4c5abcb240da2b +PODFILE CHECKSUM: 8723148a909e836604a13ea038cf5206d1d450a0 COCOAPODS: 1.11.3 From a00c24c3c54c06ea717a9998d1c44bf9c15aba3c Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 20 Nov 2022 10:35:13 +0800 Subject: [PATCH 013/122] misc: fix alamofire warning --- ClashX/General/ApiRequest.swift | 20 +++++++++++-------- ClashX/Models/ClashProxy.swift | 13 ++++++------ .../ClashWebViewContoller.swift | 2 -- ClashX/goClash/main.go | 9 +++++++++ 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index 8bdb6bc8e..a35401906 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -11,7 +11,7 @@ import Cocoa import Starscream import SwiftyJSON -protocol ApiRequestStreamDelegate: class { +protocol ApiRequestStreamDelegate: AnyObject { func didUpdateTraffic(up: Int, down: Int) func didGetLog(log: String, level: String) } @@ -125,7 +125,7 @@ class ApiRequest { // DEV MODE: Use API if !useDirectApi() { - req("/configs", method: .put, parameters: ["Path": configPath], encoding: JSONEncoding.default).responseJSON { res in + req("/configs", method: .put, parameters: ["Path": configPath], encoding: JSONEncoding.default).responseData { res in if res.response?.statusCode == 204 { ConfigManager.shared.isRunning = true callback(nil) @@ -155,7 +155,7 @@ class ApiRequest { static func updateOutBoundMode(mode: ClashProxyMode, callback: ((Bool) -> Void)? = nil) { req("/configs", method: .patch, parameters: ["mode": mode.rawValue], encoding: JSONEncoding.default) - .responseJSON { response in + .responseData { response in switch response.result { case .success: callback?(true) @@ -166,7 +166,7 @@ class ApiRequest { } static func updateLogLevel(level: ClashLogLevel, callback: ((Bool) -> Void)? = nil) { - req("/configs", method: .patch, parameters: ["log-level": level.rawValue], encoding: JSONEncoding.default).responseJSON(completionHandler: { response in + req("/configs", method: .patch, parameters: ["log-level": level.rawValue], encoding: JSONEncoding.default).responseData(completionHandler: { response in switch response.result { case .success: callback?(true) @@ -177,7 +177,7 @@ class ApiRequest { } static func requestProxyGroupList(completeHandler: ((ClashProxyResp) -> Void)? = nil) { - req("/proxies").responseJSON { + req("/proxies").responseData { res in let proxies = ClashProxyResp(try? res.result.get()) ApiRequest.shared.proxyRespCache = proxies @@ -215,7 +215,7 @@ class ApiRequest { method: .put, parameters: ["name": selectProxy], encoding: JSONEncoding.default) - .responseJSON { response in + .responseData { response in callback(response.response?.statusCode == 204) } } @@ -263,7 +263,7 @@ class ApiRequest { req("/proxies/\(proxyName.encoded)/delay", method: .get, parameters: ["timeout": 5000, "url": ConfigManager.shared.benchMarkUrl]) - .responseJSON { res in + .responseData { res in switch res.result { case let .success(value): let json = JSON(value) @@ -315,7 +315,11 @@ extension ApiRequest { } static func closeAllConnection() { - req("/connections", method: .delete).response { _ in } + if useDirectApi() { + clash_closeAllConnections() + } else { + req("/connections", method: .delete).response { _ in } + } } } diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index 362126f75..d365eec7c 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -7,6 +7,7 @@ // import Cocoa +import SwiftyJSON enum ClashProxyType: String, Codable { case urltest = "URLTest" @@ -151,24 +152,22 @@ class ClashProxyResp { var enclosingProviderResp: ClashProviderResp? - init(_ data: Any?) { - guard - let data = data as? [String: [String: Any]], - let proxies = data["proxies"] + init(_ data: Data?) { + guard let data else { self.proxiesMap = [:] self.proxies = [] return } - + let proxies = JSON(data)["proxies"] var proxiesModel = [ClashProxy]() var proxiesMap = [ClashProxyName: ClashProxy]() let decoder = JSONDecoder() decoder.dateDecodingStrategy = .formatted(DateFormatter.js) - for value in proxies.values { - guard let data = try? JSONSerialization.data(withJSONObject: value, options: .prettyPrinted) else { + for value in proxies.dictionaryValue.values { + guard let data = try? value.rawData() else { continue } guard let proxy = try? decoder.decode(ClashProxy.self, from: data) else { diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index 6b373a282..dd8f7a08b 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -198,8 +198,6 @@ extension ClashWebViewContoller: WKUIDelegate, WKNavigationDelegate { } } -extension ClashWebViewContoller: WebResourceLoadDelegate {} - class CustomWKWebView: WKWebView { var dragableAreaHeight: CGFloat = 30 let alwaysDragableLeftAreaWidth: CGFloat = 150 diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 24bdd97c1..69f33a4a3 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -16,6 +16,7 @@ import ( "github.com/Dreamacro/clash/hub/executor" "github.com/Dreamacro/clash/hub/route" "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/tunnel/statistic" "github.com/oschwald/geoip2-golang" "github.com/phayes/freeport" ) @@ -213,5 +214,13 @@ func verifyGEOIPDataBase() bool { return true } +//export clash_closeAllConnections +func clash_closeAllConnections() { + snapshot := statistic.DefaultManager.Snapshot() + for _, c := range snapshot.Connections { + c.Close() + } +} + func main() { } From 2b37be74ecaafe592463a22efed414fd80590ffa Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 20 Nov 2022 10:36:04 +0800 Subject: [PATCH 014/122] feat: proxyhelper support ignore list --- ClashX/General/Managers/Settings.swift | 11 +++- .../General/Managers/SystemProxyManager.swift | 1 + ProxyConfigHelper/ProxyConfigHelper.m | 3 +- .../ProxyConfigRemoteProcessProtocol.h | 7 +-- ProxyConfigHelper/ProxySettingTool.h | 4 +- ProxyConfigHelper/ProxySettingTool.m | 52 ++++--------------- 6 files changed, 29 insertions(+), 49 deletions(-) diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index 7fb433d4e..1a6cdcdee 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -21,5 +21,14 @@ enum Settings { static var usePacMode:Bool @UserDefault("configAutoUpdateInterval", defaultValue: 48*60*60) - static var configAutoUpdateInterval:TimeInterval + static var configAutoUpdateInterval: TimeInterval + + @UserDefault("proxyIgnoreList", defaultValue: ["192.168.0.0/16", + "10.0.0.0/8", + "172.16.0.0/12", + "127.0.0.1", + "localhost", + "*.local", + "timestamp.apple.com"]) + static var proxyIgnoreList: [String] } diff --git a/ClashX/General/Managers/SystemProxyManager.swift b/ClashX/General/Managers/SystemProxyManager.swift index f0d4fc674..40a3ee9c2 100644 --- a/ClashX/General/Managers/SystemProxyManager.swift +++ b/ClashX/General/Managers/SystemProxyManager.swift @@ -61,6 +61,7 @@ class SystemProxyManager: NSObject { socksPort: Int32(socksPort), pac: nil, filterInterface: Settings.filterInterface, + ignoreList: Settings.proxyIgnoreList, error: { error in if let error = error { Logger.log("enableProxy \(error)", level: .error) diff --git a/ProxyConfigHelper/ProxyConfigHelper.m b/ProxyConfigHelper/ProxyConfigHelper.m index 3ad0e2513..1d8b6dd92 100644 --- a/ProxyConfigHelper/ProxyConfigHelper.m +++ b/ProxyConfigHelper/ProxyConfigHelper.m @@ -92,10 +92,11 @@ - (void)enableProxyWithPort:(int)port socksPort:(int)socksPort pac:(NSString *)pac filterInterface:(BOOL)filterInterface + ignoreList:(NSArray*)ignoreList error:(stringReplyBlock)reply { dispatch_async(dispatch_get_main_queue(), ^{ ProxySettingTool *tool = [ProxySettingTool new]; - [tool enableProxyWithport:port socksPort:socksPort pacUrl:pac filterInterface:filterInterface]; + [tool enableProxyWithport:port socksPort:socksPort pacUrl:pac filterInterface:filterInterface ignoreList:ignoreList]; reply(nil); }); } diff --git a/ProxyConfigHelper/ProxyConfigRemoteProcessProtocol.h b/ProxyConfigHelper/ProxyConfigRemoteProcessProtocol.h index 0f16a0b30..51eb3f2b6 100644 --- a/ProxyConfigHelper/ProxyConfigRemoteProcessProtocol.h +++ b/ProxyConfigHelper/ProxyConfigRemoteProcessProtocol.h @@ -18,10 +18,11 @@ typedef void(^dictReplyBlock)(NSDictionary *); - (void)getVersion:(stringReplyBlock)reply; - (void)enableProxyWithPort:(int)port - socksPort:(int)socksPort - pac:(NSString *)pac + socksPort:(int)socksPort + pac:(NSString *)pac filterInterface:(BOOL)filterInterface - error:(stringReplyBlock)reply; + ignoreList:(NSArray*)ignoreList + error:(stringReplyBlock)reply; - (void)disableProxyWithFilterInterface:(BOOL)filterInterface reply:(stringReplyBlock)reply; diff --git a/ProxyConfigHelper/ProxySettingTool.h b/ProxyConfigHelper/ProxySettingTool.h index a11bb0441..737b1ae4a 100644 --- a/ProxyConfigHelper/ProxySettingTool.h +++ b/ProxyConfigHelper/ProxySettingTool.h @@ -14,7 +14,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)enableProxyWithport:(int)port socksPort:(int)socksPort pacUrl:(NSString *)pacUrl - filterInterface:(BOOL)filterInterFace; + filterInterface:(BOOL)filterInterface + ignoreList:(NSArray*)ignoreList; + - (void)disableProxyWithfilterInterface:(BOOL)filterInterFace; - (void)restoreProxySettint:(NSDictionary *)savedInfo diff --git a/ProxyConfigHelper/ProxySettingTool.m b/ProxyConfigHelper/ProxySettingTool.m index 123bbea0f..028b260e6 100644 --- a/ProxyConfigHelper/ProxySettingTool.m +++ b/ProxyConfigHelper/ProxySettingTool.m @@ -29,11 +29,12 @@ - (instancetype)init { - (void)enableProxyWithport:(int)port socksPort:(int)socksPort pacUrl:(NSString *)pacUrl - filterInterface:(BOOL)filterInterface { + filterInterface:(BOOL)filterInterface + ignoreList:(NSArray*)ignoreList { [self applySCNetworkSettingWithRef:^(SCPreferencesRef ref) { [ProxySettingTool getDiviceListWithPrefRef:ref filterInterface:filterInterface devices:^(NSString *key, NSDictionary *dict) { - [self enableProxySettings:ref interface:key port:port socksPort:socksPort pac:pacUrl]; + [self enableProxySettings:ref interface:key port:port socksPort:socksPort ignoreList:ignoreList pac:pacUrl]; }]; }]; } @@ -110,45 +111,9 @@ - (void)dealloc { } -+ (NSString *)getUserHomePath { - NSString *userName = [CommonUtils runCommand:@"/usr/bin/stat" args:@[@"-f",@"%Su",@"/dev/console"]]; - userName = [userName stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - if (!userName) { - return nil; - } - NSString *path = [NSString stringWithFormat:@"/Users/%@", userName]; - if([NSFileManager.defaultManager fileExistsAtPath:path]) { - return path; - } - return nil; -} - - -- (NSArray *)getIgnoreList { - NSString *homePath = [ProxySettingTool getUserHomePath]; - if (homePath.length > 0) { - NSString *configPath = [homePath stringByAppendingString:@"/.config/clash/proxyIgnoreList.plist"]; - if ([NSFileManager.defaultManager fileExistsAtPath:configPath]) { - NSArray *arr = [[NSArray alloc] initWithContentsOfFile:configPath]; - if (arr != nil && arr.count > 0) { - return arr; - } - } - } - NSArray *ignoreList = @[ - @"192.168.0.0/16", - @"10.0.0.0/8", - @"172.16.0.0/12", - @"127.0.0.1", - @"localhost", - @"*.local", - @"timestamp.apple.com" - ]; - return ignoreList; -} - - (NSDictionary *)getProxySetting:(BOOL)enable port:(int) port - socksPort: (int)socksPort pac:(NSString *)pac { + socksPort: (int)socksPort pac:(NSString *)pac + ignoreList:(NSArray*)ignoreList { NSMutableDictionary *proxySettings = [NSMutableDictionary dictionary]; @@ -183,7 +148,7 @@ - (NSDictionary *)getProxySetting:(BOOL)enable port:(int) port } if (enable) { - proxySettings[(__bridge NSString *)kCFNetworkProxiesExceptionsList] = [self getIgnoreList]; + proxySettings[(__bridge NSString *)kCFNetworkProxiesExceptionsList] = ignoreList; } else { proxySettings[(__bridge NSString *)kCFNetworkProxiesExceptionsList] = @[]; } @@ -202,16 +167,17 @@ - (void)enableProxySettings:(SCPreferencesRef)prefs interface:(NSString *)interfaceKey port:(int) port socksPort:(int) socksPort + ignoreList:(NSArray*)ignoreList pac:(NSString *)pac { - NSDictionary *proxySettings = [self getProxySetting:YES port:port socksPort:socksPort pac:pac]; + NSDictionary *proxySettings = [self getProxySetting:YES port:port socksPort:socksPort pac:pac ignoreList:ignoreList]; [self setProxyConfig:prefs interface:interfaceKey proxySetting:proxySettings]; } - (void)disableProxySetting:(SCPreferencesRef)prefs interface:(NSString *)interfaceKey { - NSDictionary *proxySettings = [self getProxySetting:NO port:0 socksPort:0 pac:nil]; + NSDictionary *proxySettings = [self getProxySetting:NO port:0 socksPort:0 pac:nil ignoreList:@[]]; [self setProxyConfig:prefs interface:interfaceKey proxySetting:proxySettings]; } From 3c86163008ad8629c94683517bab620a4c5e3b2d Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 20 Nov 2022 12:08:46 +0800 Subject: [PATCH 015/122] misc: add setting interface --- ClashX.xcodeproj/project.pbxproj | 16 ++ ClashX/AppDelegate.swift | 5 +- ClashX/Base.lproj/Main.storyboard | 211 +++++++++++++++++- ClashX/General/ApiRequest.swift | 2 +- ClashX/General/Managers/ConfigManager.swift | 2 +- ClashX/General/Managers/ICloudManager.swift | 70 +++--- ClashX/General/Managers/MenuItemFactory.swift | 2 +- .../Managers/RemoteConfigManager.swift | 4 +- ClashX/General/Managers/Settings.swift | 4 +- .../en.lproj/Localizable.strings | 6 + .../zh-Hans.lproj/Localizable.strings | 6 + .../GeneralSettingViewController.swift | 53 +++++ .../Settings/SettingTabViewController.swift | 19 ++ ClashX/zh-Hans.lproj/Main.strings | 32 ++- README.md | 14 +- proxyIgnoreList.plist | 14 -- 16 files changed, 373 insertions(+), 87 deletions(-) create mode 100644 ClashX/ViewControllers/Settings/GeneralSettingViewController.swift create mode 100644 ClashX/ViewControllers/Settings/SettingTabViewController.swift delete mode 100644 proxyIgnoreList.plist diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index d0201dd45..ba4057d9d 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -39,6 +39,8 @@ 4982F51F2344A216008804B0 /* Cgo+Convert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4982F51E2344A216008804B0 /* Cgo+Convert.swift */; }; 49862FA0218418C600A1D5EC /* ClashRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49862F9F218418C600A1D5EC /* ClashRule.swift */; }; 4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98D20D0AE990001E564 /* sampleConfig.yaml */; }; + 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */; }; + 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */; }; 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */; }; 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */; }; 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485722ED715200F6C675 /* RemoteConfigModel.swift */; }; @@ -162,6 +164,8 @@ 49862F9F218418C600A1D5EC /* ClashRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashRule.swift; sourceTree = ""; }; 498960722340F21C00AFB7EC /* com.west2online.ClashX.ProxyConfigHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = com.west2online.ClashX.ProxyConfigHelper.entitlements; sourceTree = ""; }; 4989F98D20D0AE990001E564 /* sampleConfig.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = sampleConfig.yaml; sourceTree = ""; }; + 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTabViewController.swift; sourceTree = ""; }; + 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingViewController.swift; sourceTree = ""; }; 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWebViewContoller.swift; sourceTree = ""; }; 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigViewController.swift; sourceTree = ""; }; 499A485722ED715200F6C675 /* RemoteConfigModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigModel.swift; sourceTree = ""; }; @@ -385,6 +389,7 @@ 4989F98520D0AA300001E564 /* ViewControllers */ = { isa = PBXGroup; children = ( + 498BC2512929CC0A00CA8084 /* Settings */, 49BC061B212931F4005A0FE7 /* AboutViewController.swift */, 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */, 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */, @@ -393,6 +398,15 @@ path = ViewControllers; sourceTree = ""; }; + 498BC2512929CC0A00CA8084 /* Settings */ = { + isa = PBXGroup; + children = ( + 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */, + 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */, + ); + path = Settings; + sourceTree = ""; + }; 4997732220D251A60009B136 /* Basic */ = { isa = PBXGroup; children = ( @@ -721,6 +735,7 @@ 49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */, 4913C82321157D0200F6B87C /* Notification.swift in Sources */, 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */, + 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */, 49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */, F9203A26236342820020D57D /* AppDelegate+..swift in Sources */, 499A485C22ED793C00F6C675 /* NSView+Nib.swift in Sources */, @@ -760,6 +775,7 @@ 493AEAE5221AE7230016FE98 /* ProxyMenuItem.swift in Sources */, 499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */, F939724C23A4B33500FE5A3F /* ClashProvider.swift in Sources */, + 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */, 49862FA0218418C600A1D5EC /* ClashRule.swift in Sources */, 49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */, 4929F684258CE07500A435F6 /* UserDefaultWrapper.swift in Sources */, diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 52de0592f..fc2e7e671 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -490,7 +490,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { if WebPortalManager.hasWebProtal { WebPortalManager.shared.addWebProtalMenuItem(&statusMenu) } - ICloudManager.shared.addEnableMenuItem(&experimentalMenu) AutoUpgardeManager.shared.setup() AutoUpgardeManager.shared.addChanelMenuItem(&experimentalMenu) updateExperimentalFeatureStatus() @@ -695,7 +694,7 @@ extension AppDelegate { extension AppDelegate { @IBAction func openConfigFolder(_ sender: Any) { - if ICloudManager.shared.isICloudEnable() { + if ICloudManager.shared.useiCloud.value { ICloudManager.shared.getUrl { url in if let url = url { @@ -841,7 +840,7 @@ extension AppDelegate { } } - if ICloudManager.shared.isICloudEnable() { + if ICloudManager.shared.useiCloud.value { ICloudManager.shared.getConfigFilesList { list in action(list) } diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 4cbbb0a16..4b128ab3c 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1,11 +1,198 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -290,6 +477,13 @@ + + + + + + + @@ -677,7 +871,7 @@ - + @@ -716,7 +910,7 @@ - + @@ -755,7 +949,7 @@ - + @@ -912,7 +1106,7 @@ - + @@ -957,7 +1151,7 @@ - + @@ -999,7 +1193,7 @@ - + @@ -1122,5 +1316,6 @@ + diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index a35401906..99de19da2 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -105,7 +105,7 @@ class ApiRequest { } static func requestConfigUpdate(configName: String, callback: @escaping ((ErrorString?) -> Void)) { - if ICloudManager.shared.isICloudEnable() { + if ICloudManager.shared.useiCloud.value { ICloudManager.shared.getUrl { url in guard let url = url else { callback("icloud error") diff --git a/ClashX/General/Managers/ConfigManager.swift b/ClashX/General/Managers/ConfigManager.swift index a31bb9bbf..b33c85613 100644 --- a/ClashX/General/Managers/ConfigManager.swift +++ b/ClashX/General/Managers/ConfigManager.swift @@ -55,7 +55,7 @@ class ConfigManager { } static func watchCurrentConfigFile() { - if ICloudManager.shared.isICloudEnable() { + if ICloudManager.shared.useiCloud.value { ICloudManager.shared.getUrl { url in guard let url = url else { return } let configUrl = url.appendingPathComponent(Paths.configFileName(for: selectConfigName)) diff --git a/ClashX/General/Managers/ICloudManager.swift b/ClashX/General/Managers/ICloudManager.swift index ff3e8e277..97c64313c 100644 --- a/ClashX/General/Managers/ICloudManager.swift +++ b/ClashX/General/Managers/ICloudManager.swift @@ -7,6 +7,8 @@ // import Cocoa +import RxSwift +import RxCocoa class ICloudManager { static let shared = ICloudManager() @@ -14,23 +16,28 @@ class ICloudManager { private var metaQuery: NSMetadataQuery? private var enableMenuItem: NSMenuItem? private var icloudAvailable = false { - didSet { updateMenuItemStatus() } + didSet { useiCloud.accept(userEnableiCloud && icloudAvailable) } } + private var disposeBag = DisposeBag() - private var userEnableiCloud: Bool = UserDefaults.standard.bool(forKey: "kUserEnableiCloud") { - didSet { UserDefaults.standard.set(userEnableiCloud, forKey: "kUserEnableiCloud") } + let useiCloud = BehaviorRelay(value: false) + + var userEnableiCloud: Bool = UserDefaults.standard.bool(forKey: "kUserEnableiCloud") { + didSet { + UserDefaults.standard.set(userEnableiCloud, forKey: "kUserEnableiCloud") + useiCloud.accept(userEnableiCloud && icloudAvailable) + } } func setup() { addNotification() - icloudAvailable = isICloudAvailable() - if isICloudEnable() { - checkiCloud() - } - } + useiCloud.distinctUntilChanged().filter({$0}).subscribe { + [weak self] _ in + self?.checkiCloud() + }.disposed(by: disposeBag) - func isICloudEnable() -> Bool { - return icloudAvailable && userEnableiCloud + icloudAvailable = isICloudAvailable() + useiCloud.accept(userEnableiCloud && icloudAvailable) } func getConfigFilesList(configs: @escaping (([String]) -> Void)) { @@ -48,19 +55,16 @@ class ICloudManager { } private func checkiCloud() { - if isICloudAvailable() { - icloudAvailable = true - getUrl { url in - guard let url = url else { - self.icloudAvailable = false - return - } - let files = try? FileManager.default.contentsOfDirectory(atPath: url.path) - if let count = files?.count, count == 0 { - let path = Bundle.main.path(forResource: "sampleConfig", ofType: "yaml")! - try? FileManager.default.copyItem(atPath: path, toPath: kDefaultConfigFilePath) - try? FileManager.default.copyItem(atPath: Bundle.main.path(forResource: "sampleConfig", ofType: "yaml")!, toPath: url.appendingPathComponent("config.yaml").path) - } + getUrl { url in + guard let url = url else { + self.icloudAvailable = false + return + } + let files = try? FileManager.default.contentsOfDirectory(atPath: url.path) + if let count = files?.count, count == 0 { + let path = Bundle.main.path(forResource: "sampleConfig", ofType: "yaml")! + try? FileManager.default.copyItem(atPath: path, toPath: kDefaultConfigFilePath) + try? FileManager.default.copyItem(atPath: Bundle.main.path(forResource: "sampleConfig", ofType: "yaml")!, toPath: url.appendingPathComponent("config.yaml").path) } } } @@ -103,23 +107,3 @@ class ICloudManager { icloudAvailable = isICloudAvailable() } } - -extension ICloudManager { - func addEnableMenuItem(_ menu: inout NSMenu) { - let item = NSMenuItem(title: NSLocalizedString("Use iCloud", comment: ""), action: #selector(enableMenuItemTap(sender:)), keyEquivalent: "") - menu.addItem(item) - enableMenuItem = item - updateMenuItemStatus() - } - - @objc func enableMenuItemTap(sender: NSMenuItem) { - userEnableiCloud = !userEnableiCloud - updateMenuItemStatus() - checkiCloud() - } - - func updateMenuItemStatus() { - enableMenuItem?.state = isICloudEnable() ? .on : .off - enableMenuItem?.target = icloudAvailable ? self : nil - } -} diff --git a/ClashX/General/Managers/MenuItemFactory.swift b/ClashX/General/Managers/MenuItemFactory.swift index 6d39d7901..f6974de21 100644 --- a/ClashX/General/Managers/MenuItemFactory.swift +++ b/ClashX/General/Managers/MenuItemFactory.swift @@ -83,7 +83,7 @@ class MenuItemFactory { return } - if ICloudManager.shared.isICloudEnable() { + if ICloudManager.shared.useiCloud.value { ICloudManager.shared.getConfigFilesList { complete($0.map { generateMenuItem($0) }) } diff --git a/ClashX/General/Managers/RemoteConfigManager.swift b/ClashX/General/Managers/RemoteConfigManager.swift index 275fd6b86..a4f536475 100755 --- a/ClashX/General/Managers/RemoteConfigManager.swift +++ b/ClashX/General/Managers/RemoteConfigManager.swift @@ -173,7 +173,7 @@ class RemoteConfigManager { } config.isPlaceHolderName = false - if ICloudManager.shared.isICloudEnable() { + if ICloudManager.shared.useiCloud.value { ConfigFileManager.shared.stopWatchConfigFile() } else if config.name == ConfigManager.selectConfigName { ConfigFileManager.shared.pauseForNextChange() @@ -192,7 +192,7 @@ class RemoteConfigManager { } } - if ICloudManager.shared.isICloudEnable() { + if ICloudManager.shared.useiCloud.value { ICloudManager.shared.getUrl { url in guard let url = url else { return } let saveUrl = url.appendingPathComponent(Paths.configFileName(for: config.name)) diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index 1a6cdcdee..998fae580 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -29,6 +29,8 @@ enum Settings { "127.0.0.1", "localhost", "*.local", - "timestamp.apple.com"]) + "timestamp.apple.com", + "sequoia.apple.com", + "seed-sequoia.siri.apple.com"]) static var proxyIgnoreList: [String] } diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 759d1d78b..5b148d62c 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -64,6 +64,9 @@ /* No comment provided by engineer. */ "Fail:" = "Fail:"; +/* No comment provided by engineer. */ +"fails: %@" = "fails: %@"; + /* No comment provided by engineer. */ "Global" = "Global"; @@ -108,6 +111,9 @@ /* No comment provided by engineer. */ "Quit" = "Quit"; +/* No comment provided by engineer. */ +"Reduce alerts if notification permission is disabled" = "Reduce alerts if notification permission is disabled"; + /* No comment provided by engineer. */ "Reload Config Fail" = "Reload Config Fail"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 292273ac7..9a97b8d59 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -64,6 +64,9 @@ /* No comment provided by engineer. */ "Fail:" = "失败:"; +/* No comment provided by engineer. */ +"fails: %@" = "失败: %@"; + /* No comment provided by engineer. */ "Global" = "全局"; @@ -108,6 +111,9 @@ /* No comment provided by engineer. */ "Quit" = "退出"; +/* No comment provided by engineer. */ +"Reduce alerts if notification permission is disabled" = "在通知权限关闭时减少提示弹窗"; + /* No comment provided by engineer. */ "Reload Config Fail" = "重载配置文件失败"; diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift new file mode 100644 index 000000000..8c6bd573e --- /dev/null +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -0,0 +1,53 @@ +// +// GeneralSettingViewController.swift +// ClashX Pro +// +// Created by yicheng on 2022/11/20. +// Copyright © 2022 west2online. All rights reserved. +// + +import Cocoa +import RxSwift + +class GeneralSettingViewController: NSViewController { + @IBOutlet var ignoreListTextView: NSTextView! + @IBOutlet weak var launchAtLoginButton: NSButton! + + @IBOutlet weak var reduceNotificationsButton: NSButton! + @IBOutlet weak var useiCloudButton: NSButton! + + var disposeBag = DisposeBag() + override func viewDidLoad() { + super.viewDidLoad() + ignoreListTextView.string = Settings.proxyIgnoreList.joined(separator: ",") + ignoreListTextView.rx + .string.debounce(.milliseconds(500), scheduler: MainScheduler.instance) + .map { $0.components(separatedBy: ",").filter {!$0.isEmpty} } + .subscribe { arr in + print(arr) + Settings.proxyIgnoreList = arr + }.disposed(by: disposeBag) + + LaunchAtLogin.shared.isEnableVirable + .map { $0 ? .on : .off } + .bind(to: launchAtLoginButton.rx.state) + .disposed(by: disposeBag) + launchAtLoginButton.rx.state.map({$0 == .on}).subscribe { + LaunchAtLogin.shared.isEnabled = $0 + }.disposed(by: disposeBag) + + ICloudManager.shared.useiCloud + .map { $0 ? .on : .off } + .bind(to: useiCloudButton.rx.state) + .disposed(by: disposeBag) + useiCloudButton.rx.state.map({$0 == .on}).subscribe { + ICloudManager.shared.userEnableiCloud = $0 + }.disposed(by: disposeBag) + reduceNotificationsButton.toolTip = NSLocalizedString("Reduce alerts if notification permission is disabled", comment: "") + reduceNotificationsButton.state = Settings.disableNoti ? .on : .off + reduceNotificationsButton.rx.state.map {$0 == .on }.subscribe { + Settings.disableNoti = $0 + }.disposed(by: disposeBag) + } + +} diff --git a/ClashX/ViewControllers/Settings/SettingTabViewController.swift b/ClashX/ViewControllers/Settings/SettingTabViewController.swift new file mode 100644 index 000000000..1128bdce3 --- /dev/null +++ b/ClashX/ViewControllers/Settings/SettingTabViewController.swift @@ -0,0 +1,19 @@ +// +// SettingTabViewController.swift +// ClashX Pro +// +// Created by yicheng on 2022/11/20. +// Copyright © 2022 west2online. All rights reserved. +// + +import Cocoa + +class SettingTabViewController: NSTabViewController { + + override func viewDidLoad() { + super.viewDidLoad() + tabStyle = .toolbar + NSApp.activate(ignoringOtherApps: true) + } + +} diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index a733fd28a..a789c2366 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -190,5 +190,35 @@ /* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ "WkL-aX-66E.title" = "远程控制器允许你控制其他设备上的 Clash 状态。"; -/* Class = "NSMenuItem"; title = "Set update Interval"; ObjectID = "h1H-7k-9HS"; */ +/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ "h1H-7k-9HS.title" = "设置更新间隔"; + +/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ +"Gnh-m8-PAz.title" = ""; + +/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ +"LAj-p8-9gd.title" = "ClashX 设置"; + +/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ +"Ltt-Vq-Hh1.label" = "通用"; + +/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for there Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ +"NPu-V9-f3r.title" = "忽略这些主机与域的代理设置"; + +/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ +"dV6-4Z-2SO.title" = "开机启动"; + +/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ +"kma-mp-ncL.title" = ""; + +/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ +"krh-QF-pqZ.title" = "更多设置"; + +/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ +"p7q-KN-kIv.title" = "将配置文件存储在iCloud中"; + +/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ +"jsL-HC-6ne.title" = "减少通知"; + +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ +"sfe-wu-UXp.title" = "使用英文逗号(,)分隔"; diff --git a/README.md b/README.md index 1390e89a5..aae265b25 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,7 @@ Checkout [Clash](https://github.com/Dreamacro/clash) or [SS-Rule-Snippet for Cla ### Change default system ignore list. -- Download sample plist in the [Here](proxyIgnoreList.plist) and place in the - - ``` - ~/.config/clash/proxyIgnoreList.plist - ``` - -- Edit the `proxyIgnoreList.plist` to set up your own proxy ignore list +- Change by menu -> Config -> Setting -> Bypass proxy settings for there Hosts & Domains ### URL Schemes. @@ -106,11 +100,7 @@ script: ### 关闭ClashX的通知 1. 在系统设置中关闭 clashx 的推送权限 -2. 执行 -``` -defaults write com.west2online.ClashX disableNoti -bool true -defaults write com.west2online.ClashXPro disableNoti -bool true -``` +2. 在菜单栏->配置->更多设置中选中减少通知 Note:强烈不推荐这么做,这可能导致clashx的很多重要错误提醒无法显示。 diff --git a/proxyIgnoreList.plist b/proxyIgnoreList.plist deleted file mode 100644 index 9577c079f..000000000 --- a/proxyIgnoreList.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - 192.168.0.0/16 - 10.0.0.0/8 - 172.16.0.0/12 - 127.0.0.1 - localhost - *.local - *.crashlytics.com - my-custom-site.com - - From 43b88c62867f0ce8bcdcd42dbba82649b2bbefc9 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 20 Nov 2022 16:31:32 +0800 Subject: [PATCH 016/122] feat: show alert if user disable daemon permission on macos13 --- .../General/Managers/PrivilegedHelperManager.swift | 12 ++++++++++++ ClashX/Support Files/en.lproj/Localizable.strings | 6 ++++++ .../Support Files/zh-Hans.lproj/Localizable.strings | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/ClashX/General/Managers/PrivilegedHelperManager.swift b/ClashX/General/Managers/PrivilegedHelperManager.swift index 64d096294..c454f2fa5 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager.swift @@ -31,6 +31,18 @@ class PrivilegedHelperManager { func checkInstall() { Logger.log("checkInstall", level: .debug) + if #available(macOS 13, *) { + let url = URL(string: "/Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist")! + let status = SMAppService.statusForLegacyPlist(at: url) + if status == .requiresApproval { + let alert = NSAlert() + alert.messageText = NSLocalizedString("ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app", comment: "") + alert.addButton(withTitle: NSLocalizedString("Open System Login Item Setting", comment: "")) + alert.runModal() + SMAppService.openSystemSettingsLoginItems() + return + } + } getHelperStatus { [weak self] installed in guard let self = self else {return} if !installed { diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 5b148d62c..2145be845 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -34,6 +34,9 @@ /* No comment provided by engineer. */ "ClashX Start Error!" = "ClashX Start Error!"; +/* No comment provided by engineer. */ +"ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app" = "ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app"; + /* No comment provided by engineer. */ "Config file have been changed" = "Config file have been changed"; @@ -96,6 +99,9 @@ /* No comment provided by engineer. */ "OK" = "OK"; +/* No comment provided by engineer. */ +"Open System Login Item Setting" = "Open System Login Item Setting"; + /* No comment provided by engineer. */ "Ports Open Fail, Please try to restart ClashX" = "Ports Open Fail, Please try to restart ClashX"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 9a97b8d59..884d0754f 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -34,6 +34,9 @@ /* No comment provided by engineer. */ "ClashX Start Error!" = "ClashX 启动出错"; +/* No comment provided by engineer. */ +"ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app" = "ClashX需要通过后台Daemon进程来设置系统代理,请在\"系统偏好设置->登录项->允许在后台 中\"允许ClashX"; + /* No comment provided by engineer. */ "Config file have been changed" = "配置文件已被修改"; @@ -96,6 +99,9 @@ /* No comment provided by engineer. */ "OK" = "确定"; +/* No comment provided by engineer. */ +"Open System Login Item Setting" = "打开系统登录项设置"; + /* No comment provided by engineer. */ "Ports Open Fail, Please try to restart ClashX" = "端口打开失败,请尝试重启ClashX"; From 2895bfb07a6976724204ebd9c73e8459fde87951 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 20 Nov 2022 17:18:37 +0800 Subject: [PATCH 017/122] misc: bump version --- .github/workflows/main.yml | 5 ++- ClashX.xcodeproj/project.pbxproj | 8 ++--- ClashX/goClash/go.mod | 25 +++++++------- ClashX/goClash/go.sum | 56 +++++++++++++++++++++----------- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d4bd20275..85e85a390 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,7 +4,6 @@ on: [push] env: FASTLANE_SKIP_UPDATE_CHECK: true - DEVELOPER_DIR: /Applications/Xcode_13.4.1.app/Contents/Developer jobs: build: @@ -20,6 +19,10 @@ jobs: with: go-version: 1.19.x + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: install deps run: | bash install_dependency.sh diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index ba4057d9d..f3cf92470 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -954,7 +954,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; - CURRENT_PROJECT_VERSION = 1.95.1; + CURRENT_PROJECT_VERSION = 1.96.0; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -976,7 +976,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.95.1; + MARKETING_VERSION = 1.96.0; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1001,7 +1001,7 @@ COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1.95.1; + CURRENT_PROJECT_VERSION = 1.96.0; DEPLOYMENT_POSTPROCESSING = YES; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; @@ -1024,7 +1024,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.95.1; + MARKETING_VERSION = 1.96.0; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 9960188d6..3c3cea0ca 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.11.9-0.20220902085900-4f291fa51350 + github.com/Dreamacro/clash v1.11.13-0.20221104053120-de264c42a811 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) @@ -13,24 +13,27 @@ require ( github.com/go-chi/chi/v5 v5.0.7 // indirect github.com/go-chi/cors v1.2.1 // indirect github.com/go-chi/render v1.0.2 // indirect - github.com/gofrs/uuid v4.2.0+incompatible // indirect + github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 // indirect + github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect + github.com/josharian/native v1.0.0 // indirect github.com/kr/pretty v0.1.0 // indirect + github.com/mdlayher/netlink v1.6.2 // indirect + github.com/mdlayher/socket v0.2.3 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c // indirect - golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/tools v0.1.12 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 8c9bc6976..18e1fffb1 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.11.9-0.20220902085900-4f291fa51350 h1:YB8nz7XydRfGuSdQhG0tXF6mEfFhb9yZz6MR0JP3zMA= -github.com/Dreamacro/clash v1.11.9-0.20220902085900-4f291fa51350/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ= +github.com/Dreamacro/clash v1.11.13-0.20221104053120-de264c42a811 h1:UTEgzYBvC629R2rutfqOuny2SInX0zf9iytARXNHUoU= +github.com/Dreamacro/clash v1.11.13-0.20221104053120-de264c42a811/go.mod h1:WiRGFHBrOUYP89GXJ9k4KCyZq5i485LWzc4FPsEPlMI= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -12,19 +12,24 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= -github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= +github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= +github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= @@ -40,8 +45,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ= +github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= +github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= @@ -59,7 +68,7 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -70,13 +79,15 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to= -golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= +golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -86,12 +97,14 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= +golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -110,25 +123,30 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= +golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= From 075b59cc10f683d9826de04325cd1716ad465c57 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 18 Sep 2022 19:53:37 +0800 Subject: [PATCH 018/122] Bump GH actions https://github.com/actions/checkout/releases/tag/v3.0.0 https://github.com/actions/setup-go/releases/tag/v3.0.0 https://github.com/actions/setup-node/releases/tag/v3.0.0 --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 85e85a390..f53446d4c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,13 +9,13 @@ jobs: build: runs-on: macos-12 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: import certs run: | echo `/usr/bin/xcodebuild -version` openssl aes-256-cbc -k "${{ secrets.ENCRYPTION_SECRET }}" -in ".github/certs/dist.p12.enc" -d -a -out ".github/certs/dist.p12" -md md5 - name: setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: 1.19.x @@ -47,7 +47,7 @@ jobs: - name: setup node if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[appcenter]') - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: '10.x' From 499c81da8b408bd39563534d8cd79a55a9332891 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 18 Sep 2022 19:53:48 +0800 Subject: [PATCH 019/122] Enable CI running on PRs --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f53446d4c..16db3fed1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,6 @@ name: ClashX -on: [push] +on: [ push, pull_request ] env: FASTLANE_SKIP_UPDATE_CHECK: true From 6e736995c7f1e5ad494e154805d7faca5a9ad7ac Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 21 Nov 2022 19:30:11 +0800 Subject: [PATCH 020/122] misc: support wireguard in api --- ClashX/Models/ClashProxy.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index d365eec7c..1b477232a 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -25,6 +25,7 @@ enum ClashProxyType: String, Codable { case trojan = "Trojan" case relay = "Relay" case unknown = "Unknown" + case wireguard = "Wireguard" static let proxyGroups: [ClashProxyType] = [.select, .urltest, .fallback, .loadBalance] From f8fb1fdba7ea335072fa96eac7cd28decfd1c0a2 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 21 Nov 2022 20:15:41 +0800 Subject: [PATCH 021/122] misc: bump core version, support tunnel --- ClashX.xcodeproj/project.pbxproj | 12 ++++++------ ClashX/goClash/go.mod | 6 +++--- ClashX/goClash/go.sum | 17 +++++++++-------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index f3cf92470..e2f8de893 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -879,7 +879,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -934,7 +934,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -954,7 +954,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; - CURRENT_PROJECT_VERSION = 1.96.0; + CURRENT_PROJECT_VERSION = 1.96.1; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -976,7 +976,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.96.0; + MARKETING_VERSION = 1.96.1; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1001,7 +1001,7 @@ COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1.96.0; + CURRENT_PROJECT_VERSION = 1.96.1; DEPLOYMENT_POSTPROCESSING = YES; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; @@ -1024,7 +1024,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.96.0; + MARKETING_VERSION = 1.96.1; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 3c3cea0ca..b59c0fcd8 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.11.13-0.20221104053120-de264c42a811 + github.com/Dreamacro/clash v1.11.13-0.20221120133055-5b07d7b776d1 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) @@ -18,22 +18,22 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect github.com/josharian/native v1.0.0 // indirect - github.com/kr/pretty v0.1.0 // indirect github.com/mdlayher/netlink v1.6.2 // indirect github.com/mdlayher/socket v0.2.3 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect + github.com/samber/lo v1.35.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 // indirect golang.org/x/text v0.4.0 // indirect golang.org/x/tools v0.1.12 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 18e1fffb1..f376974dc 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.11.13-0.20221104053120-de264c42a811 h1:UTEgzYBvC629R2rutfqOuny2SInX0zf9iytARXNHUoU= -github.com/Dreamacro/clash v1.11.13-0.20221104053120-de264c42a811/go.mod h1:WiRGFHBrOUYP89GXJ9k4KCyZq5i485LWzc4FPsEPlMI= +github.com/Dreamacro/clash v1.11.13-0.20221120133055-5b07d7b776d1 h1:GtzUKc1AgyiFoi578Q6h03Ae/bz4Sjl8ygIb29PKJJ0= +github.com/Dreamacro/clash v1.11.13-0.20221120133055-5b07d7b776d1/go.mod h1:V0EqpSiaXUeuHVyc2356972oWDB2ItxYqDjRHy75cgM= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -35,10 +35,6 @@ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGu github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= @@ -53,6 +49,7 @@ github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= @@ -61,6 +58,8 @@ github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoU github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0= +github.com/samber/lo v1.35.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -69,6 +68,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -81,6 +81,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -149,8 +151,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From b3974c5b6a85c3974adfd9ce0db6a0e65bf69c41 Mon Sep 17 00:00:00 2001 From: yichengchen <11733500+yichengchen@users.noreply.github.com> Date: Tue, 22 Nov 2022 13:11:48 +0800 Subject: [PATCH 022/122] fix: proxy setting might be uneditable --- ClashX/Base.lproj/Main.storyboard | 89 +++++++++++++++---------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 4b128ab3c..dbd4241a6 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -34,17 +34,17 @@ - + - + - + - + @@ -92,78 +92,75 @@ - + - + - - - - + + + + + + + + + + + + + + + + + + + + - - + + - - + + - + - - - - - - - - - - - - - - - - - + - - - - + + + - - - - + @@ -183,7 +180,7 @@ - + From cca41bbaeda753b75524c89ed2c977ecbde2b83c Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 22 Nov 2022 20:28:00 +0800 Subject: [PATCH 023/122] misc: disable old daemon when update --- .gitignore | 1 + .../Managers/PrivilegedHelperManager.swift | 45 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 96a0611cc..79cd50823 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ ClashX/goClash/goClash.a fastlane/report.xml ClashX/Resources/Country.mmdb.gz .bundle/config +*.app/** diff --git a/ClashX/General/Managers/PrivilegedHelperManager.swift b/ClashX/General/Managers/PrivilegedHelperManager.swift index c454f2fa5..823a8f025 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager.swift @@ -43,15 +43,16 @@ class PrivilegedHelperManager { return } } - getHelperStatus { [weak self] installed in + getHelperStatus { [weak self] status in guard let self = self else {return} - if !installed { + if status != .installed { + let isUpdate = status == .needUpdate Logger.log("need to install helper", level: .debug) if Thread.isMainThread { - self.notifyInstall() + self.notifyInstall(isUpdate: isUpdate) } else { DispatchQueue.main.async { - self.notifyInstall() + self.notifyInstall(isUpdate: isUpdate) } } } else { @@ -76,7 +77,7 @@ class PrivilegedHelperManager { } /// Install new helper daemon - private func installHelperDaemon() -> DaemonInstallResult { + private func installHelperDaemon(isUpdate:Bool) -> DaemonInstallResult { Logger.log("installHelperDaemon", level: .info) defer { @@ -113,7 +114,10 @@ class PrivilegedHelperManager { // Launch the privileged helper using SMJobBless tool var error: Unmanaged? - + if isUpdate { + Logger.log("disable old daemon") + SMJobRemove(kSMDomainSystemLaunchd, PrivilegedHelperManager.machServiceName as CFString, authRef, true, &error) + } if SMJobBless(kSMDomainSystemLaunchd, PrivilegedHelperManager.machServiceName as CFString, authRef, &error) == false { let blessError = error!.takeRetainedValue() as Error Logger.log("Bless Error: \(blessError)", level: .error) @@ -139,13 +143,20 @@ class PrivilegedHelperManager { } var timer: Timer? - private func getHelperStatus(callback:@escaping ((Bool) -> Void)) { + + enum HelperStatus { + case installed + case noFound + case needUpdate + } + + private func getHelperStatus(callback:@escaping ((HelperStatus) -> Void)) { var called = false - let reply: ((Bool) -> Void) = { - installed in + let reply: ((HelperStatus) -> Void) = { + status in if called {return} called = true - callback(installed) + callback(status) } let helperURL = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LaunchServices/" + PrivilegedHelperManager.machServiceName) @@ -153,12 +164,12 @@ class PrivilegedHelperManager { let helperBundleInfo = CFBundleCopyInfoDictionaryForURL(helperURL as CFURL) as? [String: Any], let helperVersion = helperBundleInfo["CFBundleShortVersionString"] as? String else { Logger.log("check helper status fail") - reply(false) + reply(.noFound) return } let helperFileExists = FileManager.default.fileExists(atPath: "/Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)") if !helperFileExists { - reply(false) + reply(.noFound) return } let timeout: TimeInterval = helperFileExists ? 15 : 5 @@ -166,23 +177,23 @@ class PrivilegedHelperManager { timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { _ in Logger.log("check helper timeout time: \(timeout)") - reply(false) + reply(.noFound) } helper()?.getVersion { [weak timer] installedHelperVersion in timer?.invalidate() timer = nil Logger.log("helper version \(installedHelperVersion ?? "") require version \(helperVersion)", level: .debug) - let installed = installedHelperVersion == helperVersion + let versionMatch = installedHelperVersion == helperVersion let interval = Date().timeIntervalSince(time) Logger.log("check helper using time: \(interval)") - reply(installed) + reply(versionMatch ? .installed : .needUpdate) } } } extension PrivilegedHelperManager { - private func notifyInstall() { + private func notifyInstall(isUpdate: Bool) { guard showInstallHelperAlert() else { exit(0) } if cancelInstallCheck { @@ -198,7 +209,7 @@ extension PrivilegedHelperManager { return } - let result = installHelperDaemon() + let result = installHelperDaemon(isUpdate: isUpdate) if case .success = result { return } From 9f779ca3bd092ad2e21a1816d230f430c686da2f Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 23 Nov 2022 21:44:00 +0800 Subject: [PATCH 024/122] misc: bump version --- ClashX.xcodeproj/project.pbxproj | 8 ++++---- ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index e2f8de893..bbbbee9ec 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -954,7 +954,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; - CURRENT_PROJECT_VERSION = 1.96.1; + CURRENT_PROJECT_VERSION = 1.96.2; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -976,7 +976,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.96.1; + MARKETING_VERSION = 1.96.2; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1001,7 +1001,7 @@ COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1.96.1; + CURRENT_PROJECT_VERSION = 1.96.2; DEPLOYMENT_POSTPROCESSING = YES; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; @@ -1024,7 +1024,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.96.1; + MARKETING_VERSION = 1.96.2; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index b59c0fcd8..79e2251f7 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.11.13-0.20221120133055-5b07d7b776d1 + github.com/Dreamacro/clash v1.11.13-0.20221122130151-efa4b9e0b8ac github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index f376974dc..fef95ff28 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.11.13-0.20221120133055-5b07d7b776d1 h1:GtzUKc1AgyiFoi578Q6h03Ae/bz4Sjl8ygIb29PKJJ0= -github.com/Dreamacro/clash v1.11.13-0.20221120133055-5b07d7b776d1/go.mod h1:V0EqpSiaXUeuHVyc2356972oWDB2ItxYqDjRHy75cgM= +github.com/Dreamacro/clash v1.11.13-0.20221122130151-efa4b9e0b8ac h1:4Cu4WdT9ZEknzZj52M+PSXnIV/jQqjsYdaTEoMlM1ew= +github.com/Dreamacro/clash v1.11.13-0.20221122130151-efa4b9e0b8ac/go.mod h1:V0EqpSiaXUeuHVyc2356972oWDB2ItxYqDjRHy75cgM= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From d4a5c58a1bd3db0a17a8874aa77ce49108312350 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Fri, 25 Nov 2022 18:27:31 +0800 Subject: [PATCH 025/122] misc: allow continue if SMAppService.statusForLegacyPlist return .requiresApproval --- ClashX/General/Managers/PrivilegedHelperManager.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ClashX/General/Managers/PrivilegedHelperManager.swift b/ClashX/General/Managers/PrivilegedHelperManager.swift index 823a8f025..26f61b7b9 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager.swift @@ -40,7 +40,6 @@ class PrivilegedHelperManager { alert.addButton(withTitle: NSLocalizedString("Open System Login Item Setting", comment: "")) alert.runModal() SMAppService.openSystemSettingsLoginItems() - return } } getHelperStatus { [weak self] status in From 08f2b1120c0a0a8fbc81a20349be977edcf38abb Mon Sep 17 00:00:00 2001 From: yichengchen <11733500+yichengchen@users.noreply.github.com> Date: Mon, 28 Nov 2022 17:22:50 +0800 Subject: [PATCH 026/122] feat: support remove helper --- .../PrivilegedHelperManager+Legacy.swift | 32 +++++++++++++++---- .../Managers/PrivilegedHelperManager.swift | 12 +++++-- .../en.lproj/Localizable.strings | 6 ++++ .../zh-Hans.lproj/Localizable.strings | 6 ++++ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift index aa4b700c4..e8dba8f32 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift @@ -54,13 +54,9 @@ extension PrivilegedHelperManager { """ return bash } - - func legacyInstallHelper() { - defer { - resetConnection() - Thread.sleep(forTimeInterval: 1) - } - let script = getInstallScript() + + + func runScriptWithRootPermission(script: String) { let tmpPath = FileManager.default.temporaryDirectory.appendingPathComponent(NSUUID().uuidString).appendingPathExtension("sh") do { try script.write(to: tmpPath, atomically: true, encoding: .utf8) @@ -77,4 +73,26 @@ extension PrivilegedHelperManager { } try? FileManager.default.removeItem(at: tmpPath) } + + func legacyInstallHelper() { + defer { + resetConnection() + Thread.sleep(forTimeInterval: 1) + } + let script = getInstallScript() + runScriptWithRootPermission(script: script) + } + + + func removeInstallHelper() { + defer { + resetConnection() + Thread.sleep(forTimeInterval: 1) + } + let script = """ + rm -rf /Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist + rm -rf /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName) + """ + runScriptWithRootPermission(script: script) + } } diff --git a/ClashX/General/Managers/PrivilegedHelperManager.swift b/ClashX/General/Managers/PrivilegedHelperManager.swift index 26f61b7b9..64afa8b75 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager.swift @@ -36,10 +36,16 @@ class PrivilegedHelperManager { let status = SMAppService.statusForLegacyPlist(at: url) if status == .requiresApproval { let alert = NSAlert() - alert.messageText = NSLocalizedString("ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app", comment: "") + let notice = NSLocalizedString("ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app", comment: "") + let addition = NSLocalizedString("If you can not find ClashX in the settings, you can try reset daemon", comment: "") + alert.messageText = notice + "\n" + addition alert.addButton(withTitle: NSLocalizedString("Open System Login Item Setting", comment: "")) - alert.runModal() - SMAppService.openSystemSettingsLoginItems() + alert.addButton(withTitle: NSLocalizedString("Reset Daemon", comment: "")) + if alert.runModal() == .alertFirstButtonReturn { + SMAppService.openSystemSettingsLoginItems() + } else { + removeInstallHelper() + } } } getHelperStatus { [weak self] status in diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 2145be845..61e9cf6b1 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -76,6 +76,9 @@ /* No comment provided by engineer. */ "hours" = "hours"; +/* No comment provided by engineer. */ +"If you can not find ClashX in the settings, you can try reset daemon" = "If you can not find ClashX in the settings, you can try reset daemon"; + /* No comment provided by engineer. */ "Install" = "Install"; @@ -135,6 +138,9 @@ /* No comment provided by engineer. */ "Remote Config Update Fail" = "Remote Config Update Fail"; +/* No comment provided by engineer. */ +"Reset Daemon" = "Reset Daemon"; + /* No comment provided by engineer. */ "ReTest" = "ReTest"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 884d0754f..3757733ed 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -76,6 +76,9 @@ /* No comment provided by engineer. */ "hours" = "小时"; +/* No comment provided by engineer. */ +"If you can not find ClashX in the settings, you can try reset daemon" = "如果在设置里没找到ClashX,可以尝试重置守护程序"; + /* No comment provided by engineer. */ "Install" = "安装"; @@ -135,6 +138,9 @@ /* No comment provided by engineer. */ "Remote Config Update Fail" = "托管配置文件更新失败"; +/* No comment provided by engineer. */ +"Reset Daemon" = "重置守护程序"; + /* No comment provided by engineer. */ "ReTest" = "重新测速"; From d1fb0d63158fbf531bec1f9f7304079df3c30bd1 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:33:36 +0800 Subject: [PATCH 027/122] feat: check require approve only when no found helper --- .../PrivilegedHelperManager+Legacy.swift | 9 ++- .../Managers/PrivilegedHelperManager.swift | 58 +++++++++---------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift index e8dba8f32..e4576dc0a 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift @@ -54,8 +54,7 @@ extension PrivilegedHelperManager { """ return bash } - - + func runScriptWithRootPermission(script: String) { let tmpPath = FileManager.default.temporaryDirectory.appendingPathComponent(NSUUID().uuidString).appendingPathExtension("sh") do { @@ -82,14 +81,14 @@ extension PrivilegedHelperManager { let script = getInstallScript() runScriptWithRootPermission(script: script) } - - + func removeInstallHelper() { defer { resetConnection() - Thread.sleep(forTimeInterval: 1) + Thread.sleep(forTimeInterval: 5) } let script = """ + launchctl remove \(PrivilegedHelperManager.machServiceName) || true rm -rf /Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist rm -rf /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName) """ diff --git a/ClashX/General/Managers/PrivilegedHelperManager.swift b/ClashX/General/Managers/PrivilegedHelperManager.swift index 64afa8b75..170d2dc3f 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager.swift @@ -30,37 +30,39 @@ class PrivilegedHelperManager { func checkInstall() { Logger.log("checkInstall", level: .debug) - - if #available(macOS 13, *) { - let url = URL(string: "/Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist")! - let status = SMAppService.statusForLegacyPlist(at: url) - if status == .requiresApproval { - let alert = NSAlert() - let notice = NSLocalizedString("ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app", comment: "") - let addition = NSLocalizedString("If you can not find ClashX in the settings, you can try reset daemon", comment: "") - alert.messageText = notice + "\n" + addition - alert.addButton(withTitle: NSLocalizedString("Open System Login Item Setting", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Reset Daemon", comment: "")) - if alert.runModal() == .alertFirstButtonReturn { - SMAppService.openSystemSettingsLoginItems() - } else { - removeInstallHelper() - } - } - } getHelperStatus { [weak self] status in + Logger.log("check result: \(status)", level: .debug) guard let self = self else {return} - if status != .installed { - let isUpdate = status == .needUpdate + switch status { + case .noFound: + if #available(macOS 13, *) { + let url = URL(string: "/Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist")! + let status = SMAppService.statusForLegacyPlist(at: url) + if status == .requiresApproval { + let alert = NSAlert() + let notice = NSLocalizedString("ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app", comment: "") + let addition = NSLocalizedString("If you can not find ClashX in the settings, you can try reset daemon", comment: "") + alert.messageText = notice + "\n" + addition + alert.addButton(withTitle: NSLocalizedString("Open System Login Item Setting", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Reset Daemon", comment: "")) + if alert.runModal() == .alertFirstButtonReturn { + SMAppService.openSystemSettingsLoginItems() + } else { + self.removeInstallHelper() + } + } + } + fallthrough + case .needUpdate: Logger.log("need to install helper", level: .debug) if Thread.isMainThread { - self.notifyInstall(isUpdate: isUpdate) + self.notifyInstall() } else { DispatchQueue.main.async { - self.notifyInstall(isUpdate: isUpdate) + self.notifyInstall() } } - } else { + case .installed: self.isHelperCheckFinished.accept(true) } } @@ -82,7 +84,7 @@ class PrivilegedHelperManager { } /// Install new helper daemon - private func installHelperDaemon(isUpdate:Bool) -> DaemonInstallResult { + private func installHelperDaemon() -> DaemonInstallResult { Logger.log("installHelperDaemon", level: .info) defer { @@ -119,10 +121,6 @@ class PrivilegedHelperManager { // Launch the privileged helper using SMJobBless tool var error: Unmanaged? - if isUpdate { - Logger.log("disable old daemon") - SMJobRemove(kSMDomainSystemLaunchd, PrivilegedHelperManager.machServiceName as CFString, authRef, true, &error) - } if SMJobBless(kSMDomainSystemLaunchd, PrivilegedHelperManager.machServiceName as CFString, authRef, &error) == false { let blessError = error!.takeRetainedValue() as Error Logger.log("Bless Error: \(blessError)", level: .error) @@ -198,7 +196,7 @@ class PrivilegedHelperManager { } extension PrivilegedHelperManager { - private func notifyInstall(isUpdate: Bool) { + private func notifyInstall() { guard showInstallHelperAlert() else { exit(0) } if cancelInstallCheck { @@ -214,7 +212,7 @@ extension PrivilegedHelperManager { return } - let result = installHelperDaemon(isUpdate: isUpdate) + let result = installHelperDaemon() if case .success = result { return } From 267f75b63124b7acd95f92c8d5a48fdafaec088d Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 28 Nov 2022 19:04:34 +0800 Subject: [PATCH 028/122] misc: update core --- ClashX/goClash/go.mod | 12 ++++++------ ClashX/goClash/go.sum | 33 ++++++++++++--------------------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 79e2251f7..1b29134ab 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.11.13-0.20221122130151-efa4b9e0b8ac + github.com/Dreamacro/clash v1.12.1-0.20221126032724-90b40a8e5a81 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) @@ -18,8 +18,8 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect github.com/josharian/native v1.0.0 // indirect - github.com/mdlayher/netlink v1.6.2 // indirect - github.com/mdlayher/socket v0.2.3 // indirect + github.com/mdlayher/netlink v1.7.0 // indirect + github.com/mdlayher/socket v0.4.0 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/samber/lo v1.35.0 // indirect @@ -27,12 +27,12 @@ require ( github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 // indirect + golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 // indirect + golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 // indirect + golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 // indirect golang.org/x/text v0.4.0 // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index fef95ff28..641049ab3 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.11.13-0.20221122130151-efa4b9e0b8ac h1:4Cu4WdT9ZEknzZj52M+PSXnIV/jQqjsYdaTEoMlM1ew= -github.com/Dreamacro/clash v1.11.13-0.20221122130151-efa4b9e0b8ac/go.mod h1:V0EqpSiaXUeuHVyc2356972oWDB2ItxYqDjRHy75cgM= +github.com/Dreamacro/clash v1.12.1-0.20221126032724-90b40a8e5a81 h1:gDZg3b3HMSP57qUxqu4izIWcL6um6rHE/9MxOE/EPZ4= +github.com/Dreamacro/clash v1.12.1-0.20221126032724-90b40a8e5a81/go.mod h1:KXZNe2ZS9Z7zZYCFENEW8J9OgyrYrwlr/Gj9ZpzcDVU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -19,7 +19,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -41,12 +40,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ= -github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= +github.com/mdlayher/netlink v1.7.0 h1:ZNGI4V7i1fJ94DPYtWhI/R85i/Q7ZxnuhUJQcJMoodI= +github.com/mdlayher/netlink v1.7.0/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= -github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= +github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= +github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -79,8 +78,8 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= -golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a h1:diz9pEYuTIuLMJLs3rGDkeaTsNyRs6duYdFyPAxzE/U= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -89,7 +88,6 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -99,12 +97,10 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e h1:IVOjWZQH/57UDcpX19vSmMz8w3ohroOMWohn8qWpRkg= +golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -125,19 +121,14 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk= +golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 447c1c63a2e8c4622f296ea34c6cd1f7fb059a92 Mon Sep 17 00:00:00 2001 From: yichengchen <11733500+yichengchen@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:54:35 +0800 Subject: [PATCH 029/122] feat: add notice if status icon is covered by notch --- ClashX/AppDelegate.swift | 59 +++++++++++++++++++ ClashX/General/Managers/Settings.swift | 3 + .../en.lproj/Localizable.strings | 6 ++ .../zh-Hans.lproj/Localizable.strings | 6 ++ ClashX/Views/StatusItemView.swift | 2 + 5 files changed, 76 insertions(+) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index fc2e7e671..ca6b055d9 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -86,6 +86,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { func postFinishLaunching() { defer { statusItem.menu = statusMenu + DispatchQueue.main.asyncAfter(deadline: .now()+1) { + self.checkMenuIconVisable() + } + } setupStatusMenuItemData() AppVersionUtil.showUpgradeAlert() @@ -183,6 +187,61 @@ class AppDelegate: NSObject, NSApplicationDelegate { SystemProxyManager.shared.disableProxy() } } + + + func buttonRectOnScreen() -> CGRect { + guard let button = statusItem.button else { return .zero } + guard let window = button.window else { return .zero } + let buttonRect = button.convert(button.bounds, to: nil) + let onScreenRect = window.convertToScreen(buttonRect) + return onScreenRect + } + + func leftScreenX() -> CGFloat { + let screens = NSScreen.screens + + var left: CGFloat = 0 + + for screen in screens { + if screen.frame.origin.x < left { + left = screen.frame.origin.x + } + } + return left + } + + func checkMenuIconVisable() { + guard let button = statusItem.button else {assertionFailure(); return } + guard let window = button.window else {assertionFailure(); return } + let buttonRect = button.convert(button.bounds, to: nil) + let onScreenRect = window.convertToScreen(buttonRect) + var leftScreenX: CGFloat = 0 + for screen in NSScreen.screens { + if screen.frame.origin.x < leftScreenX { + leftScreenX = screen.frame.origin.x + } + } + let isMenuIconHidden = onScreenRect.midX < leftScreenX + + var isCoverdByNotch = false + if #available(macOS 12, *), NSScreen.screens.count == 1, let screen = NSScreen.screens.first, let leftArea = screen.auxiliaryTopLeftArea, let rightArea = screen.auxiliaryTopRightArea { + if onScreenRect.minX > leftArea.maxX, onScreenRect.maxX Date: Wed, 1 Feb 2023 05:29:27 +0000 Subject: [PATCH 030/122] Fix typo --- ClashX/Base.lproj/Main.storyboard | 2 +- ClashX/zh-Hans.lproj/Main.strings | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index dbd4241a6..96cbf8e9a 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -99,7 +99,7 @@ - + diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index a789c2366..adfef1a0a 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -202,7 +202,7 @@ /* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ "Ltt-Vq-Hh1.label" = "通用"; -/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for there Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ +/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ "NPu-V9-f3r.title" = "忽略这些主机与域的代理设置"; /* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ diff --git a/README.md b/README.md index aae265b25..704ad62c0 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Checkout [Clash](https://github.com/Dreamacro/clash) or [SS-Rule-Snippet for Cla ### Change default system ignore list. -- Change by menu -> Config -> Setting -> Bypass proxy settings for there Hosts & Domains +- Change by menu -> Config -> Setting -> Bypass proxy settings for these Hosts & Domains ### URL Schemes. From b9265b6a5874d37ce1c7a07ea3f2e0802a56f379 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Feb 2023 09:09:22 +0800 Subject: [PATCH 031/122] misc: swift lint --- ClashX/AppDelegate.swift | 13 ++++++------- ClashX/General/Managers/Settings.swift | 4 ++-- ClashX/goClash/build_clash_universal.py | 3 ++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index ca6b055d9..ccacfd752 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -187,8 +187,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { SystemProxyManager.shared.disableProxy() } } - - + func buttonRectOnScreen() -> CGRect { guard let button = statusItem.button else { return .zero } guard let window = button.window else { return .zero } @@ -199,9 +198,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { func leftScreenX() -> CGFloat { let screens = NSScreen.screens - + var left: CGFloat = 0 - + for screen in screens { if screen.frame.origin.x < left { left = screen.frame.origin.x @@ -209,7 +208,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } return left } - + func checkMenuIconVisable() { guard let button = statusItem.button else {assertionFailure(); return } guard let window = button.window else {assertionFailure(); return } @@ -222,14 +221,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } let isMenuIconHidden = onScreenRect.midX < leftScreenX - + var isCoverdByNotch = false if #available(macOS 12, *), NSScreen.screens.count == 1, let screen = NSScreen.screens.first, let leftArea = screen.auxiliaryTopLeftArea, let rightArea = screen.auxiliaryTopRightArea { if onScreenRect.minX > leftArea.maxX, onScreenRect.maxX Date: Mon, 20 Feb 2023 10:41:35 +0800 Subject: [PATCH 032/122] misc: update github action --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 16db3fed1..6f61afc9e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,8 +17,8 @@ jobs: - name: setup Go uses: actions/setup-go@v3 with: - go-version: 1.19.x - + go-version: 1.20.x + - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: latest-stable From d3e0f349fff5a6211809a69a1786fdb75a310ecd Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:00:13 +0800 Subject: [PATCH 033/122] feat: add port setting in setting --- ClashX/AppDelegate.swift | 24 ++- ClashX/Base.lproj/Main.storyboard | 191 +++++++++++++++--- ClashX/General/Managers/ConfigManager.swift | 1 + ClashX/General/Managers/Settings.swift | 29 ++- .../GeneralSettingViewController.swift | 38 +++- ClashX/goClash/build.sh | 2 +- ClashX/goClash/build_clash.py | 55 ----- ClashX/goClash/main.go | 46 +++-- ClashX/zh-Hans.lproj/Main.strings | 23 ++- 9 files changed, 285 insertions(+), 124 deletions(-) delete mode 100644 ClashX/goClash/build_clash.py diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index ccacfd752..4a41f07aa 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -467,11 +467,25 @@ class AppDelegate: NSObject, NSApplicationDelegate { setUIPath(uiPath.goStringBuffer()) } - Logger.log("Trying start proxy") - let string = run(ConfigManager.builtInApiMode.goObject(), ConfigManager.allowConnectFromLan.goObject())?.toString() ?? "" - let jsonData = string.data(using: .utf8) ?? Data() + Logger.log("Trying start proxy, build-in mode: \(ConfigManager.builtInApiMode), allow lan: \(ConfigManager.allowConnectFromLan) custom port: \(Settings.proxyPort)") + + var apiAddr = "" + if Settings.apiPort > 0 { + if Settings.apiPortAllowLan { + apiAddr = "0.0.0.0:\(Settings.apiPort)" + } else { + apiAddr = "127.0.0.1:\(Settings.apiPort)" + } + } + let startRes = run(ConfigManager.builtInApiMode.goObject(), + ConfigManager.allowConnectFromLan.goObject(), + GoUint32(Settings.proxyPort), + apiAddr.goStringBuffer())? + .toString() ?? "" + let jsonData = startRes.data(using: .utf8) ?? Data() if let res = try? JSONDecoder().decode(StartProxyResp.self, from: jsonData) { let port = res.externalController.components(separatedBy: ":").last ?? "9090" + ConfigManager.shared.allowExternalControl = !res.externalController.contains("127.0.0.1") && !res.externalController.contains("localhost") ConfigManager.shared.apiPort = port ConfigManager.shared.apiSecret = res.secret ConfigManager.shared.isRunning = true @@ -480,8 +494,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { } else { ConfigManager.shared.isRunning = false proxyModeMenuItem.isEnabled = false - Logger.log(string, level: .error) - NSUserNotificationCenter.default.postConfigErrorNotice(msg: string) + Logger.log(startRes, level: .error) + NSUserNotificationCenter.default.postConfigErrorNotice(msg: startRes) } Logger.log("Start proxy done") } diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 96cbf8e9a..32e43db7f 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1,15 +1,15 @@ - + - + - + @@ -33,38 +33,38 @@ - - - + + + - + - + - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -106,7 +219,7 @@ - + @@ -114,18 +227,18 @@ - + - + - + - - + + @@ -133,15 +246,25 @@ - + @@ -150,25 +273,31 @@ + + - + + + + + @@ -176,19 +305,21 @@ - + + + - + @@ -1071,7 +1202,7 @@ - + @@ -1241,7 +1372,7 @@ - + diff --git a/ClashX/General/Managers/ConfigManager.swift b/ClashX/General/Managers/ConfigManager.swift index b33c85613..c07da53f2 100644 --- a/ClashX/General/Managers/ConfigManager.swift +++ b/ClashX/General/Managers/ConfigManager.swift @@ -15,6 +15,7 @@ class ConfigManager { static let shared = ConfigManager() private let disposeBag = DisposeBag() var apiPort = "8080" + var allowExternalControl = false var apiSecret: String = "" var overrideApiURL: URL? var overrideSecret: String? diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index faaf60872..2f8489051 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -23,17 +23,28 @@ enum Settings { @UserDefault("configAutoUpdateInterval", defaultValue: 48*60*60) static var configAutoUpdateInterval: TimeInterval - @UserDefault("proxyIgnoreList", defaultValue: ["192.168.0.0/16", - "10.0.0.0/8", - "172.16.0.0/12", - "127.0.0.1", - "localhost", - "*.local", - "timestamp.apple.com", - "sequoia.apple.com", - "seed-sequoia.siri.apple.com"]) + static let proxyIgnoreListDefaultValue = ["192.168.0.0/16", + "10.0.0.0/8", + "172.16.0.0/12", + "127.0.0.1", + "localhost", + "*.local", + "timestamp.apple.com", + "sequoia.apple.com", + "seed-sequoia.siri.apple.com"] + @UserDefault("proxyIgnoreList", defaultValue: proxyIgnoreListDefaultValue) static var proxyIgnoreList: [String] @UserDefault("disableMenubarNotice", defaultValue: false) static var disableMenubarNotice: Bool + + @UserDefault("proxyPort", defaultValue: 0) + static var proxyPort: Int + + @UserDefault("apiPort", defaultValue: 0) + static var apiPort: Int + + @UserDefault("apiPortAllowLan", defaultValue: false) + static var apiPortAllowLan: Bool + } diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 8c6bd573e..9c3efdebd 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -16,6 +16,9 @@ class GeneralSettingViewController: NSViewController { @IBOutlet weak var reduceNotificationsButton: NSButton! @IBOutlet weak var useiCloudButton: NSButton! + @IBOutlet weak var proxyPortTextField: NSTextField! + @IBOutlet weak var apiPortTextField: NSTextField! + var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() @@ -24,7 +27,6 @@ class GeneralSettingViewController: NSViewController { .string.debounce(.milliseconds(500), scheduler: MainScheduler.instance) .map { $0.components(separatedBy: ",").filter {!$0.isEmpty} } .subscribe { arr in - print(arr) Settings.proxyIgnoreList = arr }.disposed(by: disposeBag) @@ -48,6 +50,40 @@ class GeneralSettingViewController: NSViewController { reduceNotificationsButton.rx.state.map {$0 == .on }.subscribe { Settings.disableNoti = $0 }.disposed(by: disposeBag) + + if Settings.proxyPort > 0 { + proxyPortTextField.stringValue = "\(Settings.proxyPort)" + } else { + proxyPortTextField.stringValue = "\(ConfigManager.shared.currentConfig?.mixedPort ?? 0)" + } + if Settings.apiPort > 0 { + apiPortTextField.stringValue = "\(Settings.apiPort)" + } else { + apiPortTextField.stringValue = ConfigManager.shared.apiPort + } + + proxyPortTextField.rx.text + .compactMap {$0} + .compactMap {Int($0)} + .bind { + Settings.proxyPort = $0 + }.disposed(by: disposeBag) + + apiPortTextField.rx.text + .compactMap {$0} + .compactMap {Int($0)} + .bind { + Settings.apiPort = $0 + }.disposed(by: disposeBag) + } + + override func viewDidAppear() { + super.viewDidAppear() + view.window?.makeFirstResponder(nil) } + @IBAction func actionResetIgnoreList(_ sender: Any) { + ignoreListTextView.string = Settings.proxyIgnoreListDefaultValue.joined(separator: ",") + Settings.proxyIgnoreList = Settings.proxyIgnoreListDefaultValue + } } diff --git a/ClashX/goClash/build.sh b/ClashX/goClash/build.sh index 469ac2b5b..160049fa8 100755 --- a/ClashX/goClash/build.sh +++ b/ClashX/goClash/build.sh @@ -1,2 +1,2 @@ rm -f *.h *.a -python3 build_clash.py +python3 build_clash_universal.py diff --git a/ClashX/goClash/build_clash.py b/ClashX/goClash/build_clash.py deleted file mode 100644 index df5a7e344..000000000 --- a/ClashX/goClash/build_clash.py +++ /dev/null @@ -1,55 +0,0 @@ -import subprocess -import datetime -import plistlib -import os - -def get_version(): - with open('./go.mod') as file: - for line in file.readlines(): - if "clash" in line and "ClashX" not in line: - return line.split("-")[-1].strip()[:6] - return "unknown" - -def get_full_version(): - with open('./go.mod') as file: - for line in file.readlines(): - if "clash" in line and "ClashX" not in line: - return line.split(" ")[-1].strip() - -def build_clash(version): - build_time = datetime.datetime.now().strftime("%Y-%m-%d-%H%M") - command = f"""CGO_CFLAGS=-mmacosx-version-min=10.12 \ -CGO_LDFLAGS=-mmacosx-version-min=10.12 \ -go build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version={version}" \ --X "github.com/Dreamacro/clash/constant.BuildTime={build_time}"' \ --buildmode=c-archive -o goClash.a """ - subprocess.check_output(command, shell=True) - - -def write_to_info(version): - path = "../info.plist" - - with open(path, 'rb') as f: - contents = plistlib.load(f) - - if not contents: - exit(-1) - - contents["coreVersion"] = version - with open(path, 'wb') as f: - plistlib.dump(contents, f, sort_keys=False) - - -def run(): - version = get_version() - print("current clash version:", version) - build_clash(version) - print("build static library complete!") - if os.environ.get("CI", False) or os.environ.get("GITHUB_ACTIONS", False): - print("writing info.plist") - write_to_info(version) - print("done") - - -if __name__ == "__main__": - run() diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 69f33a4a3..0c14b5ade 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -77,7 +77,7 @@ func readConfig(path string) ([]byte, error) { return data, err } -func parseDefaultConfigThenStart(checkPort, allowLan bool) (*config.Config, error) { +func parseDefaultConfigThenStart(checkPort, allowLan bool, proxyPort uint32, externalController string) (*config.Config, error) { buf, err := readConfig(constant.Path.Config()) if err != nil { return nil, err @@ -88,29 +88,37 @@ func parseDefaultConfigThenStart(checkPort, allowLan bool) (*config.Config, erro return nil, err } - if rawCfg.MixedPort == 0 { - if rawCfg.Port > 0 { - rawCfg.MixedPort = rawCfg.Port - rawCfg.Port = 0 - } else if rawCfg.SocksPort > 0 { - rawCfg.MixedPort = rawCfg.SocksPort - rawCfg.SocksPort = 0 - } else { - rawCfg.MixedPort = 7890 - } + if proxyPort > 0 { + rawCfg.MixedPort = int(proxyPort) + rawCfg.SocksPort = 0 + rawCfg.Port = 0 + } else { + if rawCfg.MixedPort == 0 { + if rawCfg.Port > 0 { + rawCfg.MixedPort = rawCfg.Port + rawCfg.Port = 0 + } else if rawCfg.SocksPort > 0 { + rawCfg.MixedPort = rawCfg.SocksPort + rawCfg.SocksPort = 0 + } else { + rawCfg.MixedPort = 7890 + } - if rawCfg.SocksPort == rawCfg.MixedPort { - rawCfg.SocksPort = 0 - } + if rawCfg.SocksPort == rawCfg.MixedPort { + rawCfg.SocksPort = 0 + } - if rawCfg.Port == rawCfg.MixedPort { - rawCfg.Port = 0 + if rawCfg.Port == rawCfg.MixedPort { + rawCfg.Port = 0 + } } - } rawCfg.ExternalUI = "" rawCfg.Profile.StoreSelected = false + if len(externalController) > 0 { + rawCfg.ExternalController = externalController + } if checkPort { if !isAddrValid(rawCfg.ExternalController) { port, err := freeport.GetFreePort() @@ -154,8 +162,8 @@ func verifyClashConfig(content *C.char) *C.char { } //export run -func run(checkConfig, allowLan bool) *C.char { - cfg, err := parseDefaultConfigThenStart(checkConfig, allowLan) +func run(checkConfig, allowLan bool, portOverride uint32, externalController *C.char) *C.char { + cfg, err := parseDefaultConfigThenStart(checkConfig, allowLan, portOverride, C.GoString(externalController)) if err != nil { return C.CString(err.Error()) } diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index adfef1a0a..3f74a245e 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -193,9 +193,6 @@ /* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ "h1H-7k-9HS.title" = "设置更新间隔"; -/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ -"Gnh-m8-PAz.title" = ""; - /* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ "LAj-p8-9gd.title" = "ClashX 设置"; @@ -209,7 +206,7 @@ "dV6-4Z-2SO.title" = "开机启动"; /* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ -"kma-mp-ncL.title" = ""; +"kma-mp-ncL.title" = "通用"; /* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ "krh-QF-pqZ.title" = "更多设置"; @@ -222,3 +219,21 @@ /* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ "sfe-wu-UXp.title" = "使用英文逗号(,)分隔"; + +/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ +"E8B-e5-K0A.title" = "允许局域网控制(不推荐)"; + +/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "端口设置(重启应用生效)"; + +/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ +"mbn-tK-UQa.title" = "Api 端口"; + +/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ +"uUA-LS-Hu8.title" = "代理端口"; + +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "yXh-2Y-aTS"; */ +"yXh-2Y-aTS.title" = "重置"; + +/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ +"Gnh-m8-PAz.title" = "Box"; From 86adebc30ca6cc2fcc9127fb194113b22042d34e Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:08:44 +0800 Subject: [PATCH 034/122] misc: fix typo --- ProxyConfigHelper/ProxyConfigHelper.m | 2 +- ProxyConfigHelper/ProxySettingTool.h | 2 +- ProxyConfigHelper/ProxySettingTool.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProxyConfigHelper/ProxyConfigHelper.m b/ProxyConfigHelper/ProxyConfigHelper.m index 1d8b6dd92..773bd3d91 100644 --- a/ProxyConfigHelper/ProxyConfigHelper.m +++ b/ProxyConfigHelper/ProxyConfigHelper.m @@ -117,7 +117,7 @@ - (void)restoreProxyWithCurrentPort:(int)port error:(stringReplyBlock)reply { dispatch_async(dispatch_get_main_queue(), ^{ ProxySettingTool *tool = [ProxySettingTool new]; - [tool restoreProxySettint:dict currentPort:port currentSocksPort:socksPort filterInterface:filterInterface]; + [tool restoreProxySetting:dict currentPort:port currentSocksPort:socksPort filterInterface:filterInterface]; reply(nil); }); } diff --git a/ProxyConfigHelper/ProxySettingTool.h b/ProxyConfigHelper/ProxySettingTool.h index 737b1ae4a..2df8215d7 100644 --- a/ProxyConfigHelper/ProxySettingTool.h +++ b/ProxyConfigHelper/ProxySettingTool.h @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)disableProxyWithfilterInterface:(BOOL)filterInterFace; -- (void)restoreProxySettint:(NSDictionary *)savedInfo +- (void)restoreProxySetting:(NSDictionary *)savedInfo currentPort:(int)port currentSocksPort:(int)socksPort filterInterface:(BOOL)filterInterface; diff --git a/ProxyConfigHelper/ProxySettingTool.m b/ProxyConfigHelper/ProxySettingTool.m index 028b260e6..7a5614442 100644 --- a/ProxyConfigHelper/ProxySettingTool.m +++ b/ProxyConfigHelper/ProxySettingTool.m @@ -47,7 +47,7 @@ - (void)disableProxyWithfilterInterface:(BOOL)filterInterface { }]; } -- (void)restoreProxySettint:(NSDictionary *)savedInfo +- (void)restoreProxySetting:(NSDictionary *)savedInfo currentPort:(int)port currentSocksPort:(int)socksPort filterInterface:(BOOL)filterInterface{ From 327f2b4638eedbe23ab98bcf762e81c521853ecb Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:13:17 +0800 Subject: [PATCH 035/122] misc: update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 704ad62c0..63b4515ae 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ Checkout [Clash](https://github.com/Dreamacro/clash) or [SS-Rule-Snippet for Cla ## Advance Config -### Change the ports of ClashX +### 修改代理端口号 +1. 在菜单栏->配置->更多设置中修改对应端口号 - Please modify the `config.yaml` file generated by ClashX, not the other config file you created or downloaded. The `General` section settings in your custom config file would be ignored. Then relaunch ClashX to apply changes. ### Change your status menu icon From 9fa2bedb77ef4fef257013b7ba804263cda214a0 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:26:21 +0800 Subject: [PATCH 036/122] misc: bump version --- ClashX.xcodeproj/project.pbxproj | 8 ++--- ClashX/goClash/go.mod | 26 +++++++------- ClashX/goClash/go.sum | 61 +++++++++++++++----------------- ClashX/goClash/upgrade_core.py | 13 ++++--- Podfile.lock | 12 +++---- 5 files changed, 60 insertions(+), 60 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index bbbbee9ec..135cb4406 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -954,7 +954,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; - CURRENT_PROJECT_VERSION = 1.96.2; + CURRENT_PROJECT_VERSION = 1.97.0; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -976,7 +976,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.96.2; + MARKETING_VERSION = 1.97.0; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1001,7 +1001,7 @@ COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1.96.2; + CURRENT_PROJECT_VERSION = 1.97.0; DEPLOYMENT_POSTPROCESSING = YES; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; @@ -1024,7 +1024,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.96.2; + MARKETING_VERSION = 1.97.0; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 1b29134ab..5feae1efa 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,37 +3,37 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.12.1-0.20221126032724-90b40a8e5a81 + github.com/Dreamacro/clash v1.13.1-0.20230216134340-8173d6681b07 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) require ( github.com/ajg/form v1.5.1 // indirect - github.com/go-chi/chi/v5 v5.0.7 // indirect + github.com/go-chi/chi/v5 v5.0.8 // indirect github.com/go-chi/cors v1.2.1 // indirect github.com/go-chi/render v1.0.2 // indirect - github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect - github.com/josharian/native v1.0.0 // indirect - github.com/mdlayher/netlink v1.7.0 // indirect + github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 // indirect + github.com/josharian/native v1.1.0 // indirect + github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 // indirect github.com/mdlayher/socket v0.4.0 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect - github.com/samber/lo v1.35.0 // indirect + github.com/samber/lo v1.37.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect - github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect - go.etcd.io/bbolt v1.3.6 // indirect + github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect + go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a // indirect + golang.org/x/crypto v0.6.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 641049ab3..08d402a91 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,19 +1,19 @@ -github.com/Dreamacro/clash v1.12.1-0.20221126032724-90b40a8e5a81 h1:gDZg3b3HMSP57qUxqu4izIWcL6um6rHE/9MxOE/EPZ4= -github.com/Dreamacro/clash v1.12.1-0.20221126032724-90b40a8e5a81/go.mod h1:KXZNe2ZS9Z7zZYCFENEW8J9OgyrYrwlr/Gj9ZpzcDVU= +github.com/Dreamacro/clash v1.13.1-0.20230216134340-8173d6681b07 h1:GVbK1trdQHCTyIVu6ikFbLtIr8o07U9h/dAv7YEbA88= +github.com/Dreamacro/clash v1.13.1-0.20230216134340-8173d6681b07/go.mod h1:0hkl4QlO1dxG82u72o5VWihNL1MZOq8MOndr8Hpv17A= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= -github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -25,30 +25,29 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= -github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= -github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg= +github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE= +github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.7.0 h1:ZNGI4V7i1fJ94DPYtWhI/R85i/Q7ZxnuhUJQcJMoodI= -github.com/mdlayher/netlink v1.7.0/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ= +github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4= +github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= @@ -57,8 +56,8 @@ github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoU github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0= -github.com/samber/lo v1.35.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= +github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= +github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -67,19 +66,18 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= -github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= -github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= +github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww= +github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a h1:diz9pEYuTIuLMJLs3rGDkeaTsNyRs6duYdFyPAxzE/U= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -97,8 +95,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e h1:IVOjWZQH/57UDcpX19vSmMz8w3ohroOMWohn8qWpRkg= -golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -112,7 +110,6 @@ golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -120,17 +117,17 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk= -golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -141,8 +138,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ClashX/goClash/upgrade_core.py b/ClashX/goClash/upgrade_core.py index e126b8ef0..b20fef28e 100644 --- a/ClashX/goClash/upgrade_core.py +++ b/ClashX/goClash/upgrade_core.py @@ -1,8 +1,5 @@ import subprocess -import os -import re -from build_clash import get_full_version -from build_clash import build_clash +from build_clash_universal import run def upgrade_version(current_version): @@ -12,6 +9,12 @@ def upgrade_version(current_version): file.write(string) +def get_full_version(): + with open('./go.mod') as file: + for line in file.readlines(): + if "clash" in line and "ClashX" not in line: + return line.split(" ")[-1].strip() + def install(): subprocess.check_output("go mod download", shell=True) subprocess.check_output("go mod tidy", shell=True) @@ -25,4 +28,4 @@ def install(): install() new_version = get_full_version() print("new version:", new_version, ",start building") - build_clash(new_version) + run() diff --git a/Podfile.lock b/Podfile.lock index b28e27a38..5b79655d4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,8 +1,8 @@ PODS: - - Alamofire (5.6.2) - - AppCenter/Analytics (5.0.0): + - Alamofire (5.6.4) + - AppCenter/Analytics (5.0.1): - AppCenter/Core - - AppCenter/Core (5.0.0) + - AppCenter/Core (5.0.1) - CocoaLumberjack/Core (3.8.0) - CocoaLumberjack/Swift (3.8.0): - CocoaLumberjack/Core @@ -51,8 +51,8 @@ SPEC REPOS: - WebViewJavascriptBridge SPEC CHECKSUMS: - Alamofire: d368e1ff8a298e6dde360e35a3e68e6c610e7204 - AppCenter: 30ac5adefc8fc41d9530f1cc91829810a6af4c93 + Alamofire: 4e95d97098eacb88856099c4fc79b526a299e48c + AppCenter: 18153bb6bc4241d14c8cce57466ac1c88136b476 CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa @@ -67,4 +67,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 8723148a909e836604a13ea038cf5206d1d450a0 -COCOAPODS: 1.11.3 +COCOAPODS: 1.11.0 From a5c1b33fbb31819822cbedd783e1e23054f5bb49 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:24:23 +0800 Subject: [PATCH 037/122] ci: update nodejs version --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6f61afc9e..898b1b807 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,7 +49,7 @@ jobs: if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[appcenter]') uses: actions/setup-node@v3 with: - node-version: '10.x' + node-version: '18.x' - name: create dmg if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[appcenter]') From 7deb34f9ed3c12d3814b16e38e17fe13d38c45f5 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 1 Mar 2023 11:43:48 +0800 Subject: [PATCH 038/122] misc: use swiftui for menubar item --- ClashX.xcodeproj/project.pbxproj | 26 +++++- ClashX/AppDelegate.swift | 14 ++-- .../Views/StatusItem/NewStatusItemView.swift | 79 +++++++++++++++++++ ClashX/Views/StatusItem/StatusItemTool.swift | 58 ++++++++++++++ .../{ => StatusItem}/StatusItemView.swift | 61 +++----------- .../Views/{ => StatusItem}/StatusItemView.xib | 0 .../StatusItem/StatusItemViewProtocol.swift | 16 ++++ 7 files changed, 196 insertions(+), 58 deletions(-) create mode 100644 ClashX/Views/StatusItem/NewStatusItemView.swift create mode 100644 ClashX/Views/StatusItem/StatusItemTool.swift rename ClashX/Views/{ => StatusItem}/StatusItemView.swift (55%) rename ClashX/Views/{ => StatusItem}/StatusItemView.xib (100%) create mode 100644 ClashX/Views/StatusItem/StatusItemViewProtocol.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 135cb4406..3310bbce4 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -65,6 +65,9 @@ 49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */; }; 49D176A9235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176A8235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift */; }; 49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */; }; + 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45129AEEC15006487EF /* StatusItemTool.swift */; }; + 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */; }; + 49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */; }; 8A2BBEA727A03ACB0081EBEF /* ProxySetting.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */; }; 8ACD21BB27A04C7800BC4632 /* ProxySettingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */; }; 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BC27A04ED500BC4632 /* ProxyModeChangeCommand.swift */; }; @@ -144,7 +147,7 @@ 4949D153213242F600EF85E6 /* Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; 4952C3BE2115C7CA004A4FA8 /* MenuItemFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemFactory.swift; sourceTree = ""; }; 4952C3CF2117027C004A4FA8 /* ConfigFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigFileManager.swift; sourceTree = ""; }; - 495340AF20DE5F7200B0D3FF /* StatusItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatusItemView.xib; sourceTree = ""; }; + 495340AF20DE5F7200B0D3FF /* StatusItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = StatusItemView.xib; path = ClashX/Views/StatusItem/StatusItemView.xib; sourceTree = SOURCE_ROOT; }; 495340B220DE68C300B0D3FF /* StatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemView.swift; sourceTree = ""; }; 495A44D220D267D000888A0A /* LaunchAtLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLogin.swift; sourceTree = ""; }; 495BFB8721919B9800C8779D /* RemoteConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigManager.swift; sourceTree = ""; }; @@ -194,6 +197,9 @@ 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkChangeNotifier.swift; sourceTree = ""; }; 49D176A8235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupSpeedTestMenuItem.swift; sourceTree = ""; }; 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupMenuItemView.swift; sourceTree = ""; }; + 49D6A45129AEEC15006487EF /* StatusItemTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemTool.swift; sourceTree = ""; }; + 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusItemView.swift; sourceTree = ""; }; + 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemViewProtocol.swift; sourceTree = ""; }; 49D8276627E9B01700159D93 /* LoginKitWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginKitWrapper.h; sourceTree = ""; }; 49D8276727E9B01700159D93 /* LoginKitWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginKitWrapper.m; sourceTree = ""; }; 5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.debug.xcconfig"; sourceTree = ""; }; @@ -337,14 +343,13 @@ 4931969C21631F2E00A8E6E7 /* Views */ = { isa = PBXGroup; children = ( + 49BC1B4029AEEBBB002007B1 /* StatusItem */, F92D0B2B236C7C3600575E15 /* MenuItemBaseView.swift */, 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */, 49D176A8235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift */, F92D0B2D236D35C000575E15 /* ProxyItemView.swift */, 493AEAE4221AE7230016FE98 /* ProxyMenuItem.swift */, 499A485922ED781100F6C675 /* RemoteConfigAddView.xib */, - 495340B220DE68C300B0D3FF /* StatusItemView.swift */, - 495340AF20DE5F7200B0D3FF /* StatusItemView.xib */, F910AA23240134AF00116E95 /* ProxyGroupMenu.swift */, 493A9F272453E60400D35296 /* ProxyDelayHistoryMenu.swift */, 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */, @@ -419,6 +424,18 @@ path = Basic; sourceTree = ""; }; + 49BC1B4029AEEBBB002007B1 /* StatusItem */ = { + isa = PBXGroup; + children = ( + 495340AF20DE5F7200B0D3FF /* StatusItemView.xib */, + 495340B220DE68C300B0D3FF /* StatusItemView.swift */, + 49D6A45129AEEC15006487EF /* StatusItemTool.swift */, + 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */, + 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */, + ); + path = StatusItem; + sourceTree = ""; + }; 49CF3B1420CD7463001EBF94 = { isa = PBXGroup; children = ( @@ -716,6 +733,7 @@ files = ( 49ABB749236B0F9E00535CD7 /* UnsafePointer+bridge.swift in Sources */, 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */, + 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */, 49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */, 4952C3D02117027C004A4FA8 /* ConfigFileManager.swift in Sources */, 49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */, @@ -728,6 +746,7 @@ F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */, 495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */, 4929F67F258CE04700A435F6 /* Settings.swift in Sources */, + 49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */, 493AEAE3221AE3420016FE98 /* AppVersionUtil.swift in Sources */, 49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */, 496BDEE021196F1E00C5207F /* Logger.swift in Sources */, @@ -754,6 +773,7 @@ F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */, F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */, 4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */, + 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */, F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */, 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */, 49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */, diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 4a41f07aa..32ef89384 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -15,7 +15,7 @@ import RxSwift import AppCenter import AppCenterAnalytics -private let statusItemLengthWithSpeed: CGFloat = 72 +let statusItemLengthWithSpeed: CGFloat = 72 @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @@ -50,7 +50,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet var externalControlSeparator: NSMenuItem! var disposeBag = DisposeBag() - var statusItemView: StatusItemView! + var statusItemView: StatusItemViewProtocol! var isSpeedTesting = false var runAfterConfigReload: (() -> Void)? @@ -74,8 +74,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { ProcessInfo.processInfo.disableSuddenTermination() // setup menu item first statusItem = NSStatusBar.system.statusItem(withLength: statusItemLengthWithSpeed) - statusItemView = StatusItemView.create(statusItem: statusItem) - statusItemView.frame = CGRect(x: 0, y: 0, width: statusItemLengthWithSpeed, height: 22) + if #available(macOS 11, *), let button = statusItem.button { + statusItemView = NewStatusMenuView.create(on: button) + } else { + statusItemView = StatusItemView.create(statusItem: statusItem) + } + statusItemView.updateSize(width: statusItemLengthWithSpeed) statusMenu.delegate = self registCrashLogger() DispatchQueue.main.async { @@ -250,7 +254,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.showNetSpeedIndicatorMenuItem.state = (show ?? true) ? .on : .off let statusItemLength: CGFloat = (show ?? true) ? statusItemLengthWithSpeed : 25 self.statusItem.length = statusItemLength - self.statusItemView.frame.size.width = statusItemLength + self.statusItemView.updateSize(width: statusItemLength) self.statusItemView.showSpeedContainer(show: show ?? true) }.disposed(by: disposeBag) diff --git a/ClashX/Views/StatusItem/NewStatusItemView.swift b/ClashX/Views/StatusItem/NewStatusItemView.swift new file mode 100644 index 000000000..dc1b500a6 --- /dev/null +++ b/ClashX/Views/StatusItem/NewStatusItemView.swift @@ -0,0 +1,79 @@ +// +// NewStatusMenuView.swift +// ClashX Pro +// +// Created by yicheng on 2023/3/1. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import SwiftUI +@available(macOS 10.15, *) +class NewStatusMenuView:NSHostingView, StatusItemViewProtocol { + private var viewModel: StatusMenuViewModel! + + static func create(on button: NSView) -> NewStatusMenuView { + let model = StatusMenuViewModel() + let view = NewStatusMenuView(rootView: SwiftUIView(viewModel: model)) + view.viewModel = model + view.translatesAutoresizingMaskIntoConstraints = false + button.addSubview(view) + NSLayoutConstraint.activate([ + view.topAnchor.constraint(equalTo: button.topAnchor), + view.leadingAnchor.constraint(equalTo: button.leadingAnchor), + view.trailingAnchor.constraint(equalTo: button.trailingAnchor), + view.bottomAnchor.constraint(equalTo: button.bottomAnchor) + ]) + return view + } + func updateViewStatus(enableProxy: Bool) { + viewModel.image = StatusItemTool.getMenuImage(enableProxy: enableProxy) + } + + func updateSpeedLabel(up: Int, down: Int) { + let upSpeed = StatusItemTool.getSpeedString(for: up) + let downSpeed = StatusItemTool.getSpeedString(for: down) + if upSpeed != viewModel.upSpeed {viewModel.upSpeed = upSpeed} + if downSpeed != viewModel.downSpeed {viewModel.downSpeed = downSpeed} + } + + func showSpeedContainer(show: Bool) { + viewModel.showSpeed = show + } + + func updateSize(width: CGFloat) {} +} + +@available(macOS 10.15, *) +class StatusMenuViewModel:ObservableObject { + @Published var image = StatusItemTool.getMenuImage(enableProxy: false) + @Published var upSpeed = "0KB/s" + @Published var downSpeed = "0KB/s" + @Published var showSpeed = true +} + +@available(macOS 10.15, *) +struct SwiftUIView: View { + @ObservedObject var viewModel: StatusMenuViewModel + var body: some View { + HStack(alignment:.center) { + Image(nsImage: $viewModel.image.wrappedValue).renderingMode(.template) + if $viewModel.showSpeed.wrappedValue { + Spacer(minLength: 0) + VStack(alignment: .trailing) { + Text($viewModel.upSpeed.wrappedValue) + Text($viewModel.downSpeed.wrappedValue) + }.font(Font(StatusItemTool.font)) + } + } + .frame(width: $viewModel.showSpeed.wrappedValue ? statusItemLengthWithSpeed - 6 : 25) + .frame(minHeight: 22) + } +} + +@available(macOS 10.15, *) +struct SwiftUIView_Previews: PreviewProvider { + static var previews: some View { + SwiftUIView(viewModel: StatusMenuViewModel()) + } +} diff --git a/ClashX/Views/StatusItem/StatusItemTool.swift b/ClashX/Views/StatusItem/StatusItemTool.swift new file mode 100644 index 000000000..bb992589a --- /dev/null +++ b/ClashX/Views/StatusItem/StatusItemTool.swift @@ -0,0 +1,58 @@ +// +// StatusItemTool.swift +// ClashX Pro +// +// Created by yicheng on 2023/3/1. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +enum StatusItemTool { + static let menuImage: NSImage = { + let customImagePath = (NSHomeDirectory() as NSString).appendingPathComponent("/.config/clash/menuImage.png") + if let image = NSImage(contentsOfFile: customImagePath) { + return image + } + if let imagePath = Bundle.main.path(forResource: "menu_icon@2x", ofType: "png"), + let image = NSImage(contentsOfFile: imagePath) { + return image + } + return NSImage() + }() + + static let font: NSFont = { + let fontSize: CGFloat = 9 + let font: NSFont + if let fontName = UserDefaults.standard.string(forKey: "kStatusMenuFontName"), + let f = NSFont(name: fontName, size: fontSize) { + font = f + } else { + font = NSFont.menuBarFont(ofSize: fontSize) + } + return font + }() + + static func getSpeedString(for byte: Int) -> String { + let kb = byte / 1024 + if kb < 1024 { + return "\(kb)KB/s" + } else { + let mb = Double(kb) / 1024.0 + if mb >= 100 { + if mb >= 1000 { + return String(format: "%.1fGB/s", mb/1024) + } + return String(format: "%.1fMB/s", mb) + } else { + return String(format: "%.2fMB/s", mb) + } + } + } + + static func getMenuImage(enableProxy: Bool) -> NSImage { + let selectedColor = NSColor.red + let unselectedColor = selectedColor.withSystemEffect(.disabled) + return StatusItemTool.menuImage.tint(color: enableProxy ? selectedColor : unselectedColor) + } +} diff --git a/ClashX/Views/StatusItemView.swift b/ClashX/Views/StatusItem/StatusItemView.swift similarity index 55% rename from ClashX/Views/StatusItemView.swift rename to ClashX/Views/StatusItem/StatusItemView.swift index f0c3c18e4..c2ef27681 100644 --- a/ClashX/Views/StatusItemView.swift +++ b/ClashX/Views/StatusItem/StatusItemView.swift @@ -11,7 +11,7 @@ import Foundation import RxCocoa import RxSwift -class StatusItemView: NSView { +class StatusItemView: NSView, StatusItemViewProtocol { @IBOutlet var imageView: NSImageView! @IBOutlet var uploadSpeedLabel: NSTextField! @@ -20,18 +20,6 @@ class StatusItemView: NSView { weak var statusItem: NSStatusItem? - lazy var menuImage: NSImage = { - let customImagePath = (NSHomeDirectory() as NSString).appendingPathComponent("/.config/clash/menuImage.png") - if let image = NSImage(contentsOfFile: customImagePath) { - return image - } - if let imagePath = Bundle.main.path(forResource: "menu_icon@2x", ofType: "png"), - let image = NSImage(contentsOfFile: imagePath) { - return image - } - return NSImage() - }() - static func create(statusItem: NSStatusItem?) -> StatusItemView { var topLevelObjects: NSArray? if Bundle.main.loadNibNamed("StatusItemView", owner: self, topLevelObjects: &topLevelObjects) { @@ -44,55 +32,28 @@ class StatusItemView: NSView { } func setupView() { - let fontSize: CGFloat = 9 - let font: NSFont - if let fontName = UserDefaults.standard.string(forKey: "kStatusMenuFontName"), - let f = NSFont(name: fontName, size: fontSize) { - font = f - } else { - font = NSFont.menuBarFont(ofSize: fontSize) - } - uploadSpeedLabel.font = font - downloadSpeedLabel.font = font + uploadSpeedLabel.font = StatusItemTool.font + downloadSpeedLabel.font = StatusItemTool.font uploadSpeedLabel.textColor = NSColor.black downloadSpeedLabel.textColor = NSColor.black } + func updateSize(width: CGFloat) { + frame = CGRect(x: 0, y: 0, width: width, height: 22) + } + func updateViewStatus(enableProxy: Bool) { let selectedColor = NSColor.red - let unselectedColor: NSColor - if #available(OSX 10.14, *) { - unselectedColor = selectedColor.withSystemEffect(.disabled) - } else { - unselectedColor = selectedColor.withAlphaComponent(0.5) - } - - imageView.image = menuImage.tint(color: enableProxy ? selectedColor : unselectedColor) + let unselectedColor = selectedColor.withSystemEffect(.disabled) + imageView.image = StatusItemTool.menuImage.tint(color: enableProxy ? selectedColor : unselectedColor) updateStatusItemView() } - func getSpeedString(for byte: Int) -> String { - let kb = byte / 1024 - if kb < 1024 { - return "\(kb)KB/s" - } else { - let mb = Double(kb) / 1024.0 - if mb >= 100 { - if mb >= 1000 { - return String(format: "%.1fGB/s", mb/1024) - } - return String(format: "%.1fMB/s", mb) - } else { - return String(format: "%.2fMB/s", mb) - } - } - } - func updateSpeedLabel(up: Int, down: Int) { guard !speedContainerView.isHidden else { return } - let finalUpStr = getSpeedString(for: up) - let finalDownStr = getSpeedString(for: down) + let finalUpStr = StatusItemTool.getSpeedString(for: up) + let finalDownStr = StatusItemTool.getSpeedString(for: down) if downloadSpeedLabel.stringValue == finalDownStr && uploadSpeedLabel.stringValue == finalUpStr { return diff --git a/ClashX/Views/StatusItemView.xib b/ClashX/Views/StatusItem/StatusItemView.xib similarity index 100% rename from ClashX/Views/StatusItemView.xib rename to ClashX/Views/StatusItem/StatusItemView.xib diff --git a/ClashX/Views/StatusItem/StatusItemViewProtocol.swift b/ClashX/Views/StatusItem/StatusItemViewProtocol.swift new file mode 100644 index 000000000..6695cbad8 --- /dev/null +++ b/ClashX/Views/StatusItem/StatusItemViewProtocol.swift @@ -0,0 +1,16 @@ +// +// StatusItemViewProtocol.swift +// ClashX Pro +// +// Created by yicheng on 2023/3/1. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +protocol StatusItemViewProtocol: AnyObject { + func updateViewStatus(enableProxy: Bool) + func updateSpeedLabel(up: Int, down: Int) + func showSpeedContainer(show: Bool) + func updateSize(width: CGFloat) +} From 7664a43b8d76807a3d94570ada90636692acfb8c Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:26:39 +0800 Subject: [PATCH 039/122] feat: use built in api to fetch log/traffic instead of websocket --- ClashX/AppDelegate.swift | 21 ++++++++++ ClashX/Basic/Logger.swift | 3 +- ClashX/General/ApiRequest.swift | 2 + ClashX/Models/ClashConfig.swift | 20 ++++++++- ClashX/goClash/main.go | 72 ++++++++++++++++++++++++++++++++- 5 files changed, 115 insertions(+), 3 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 32ef89384..a357f513d 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -11,6 +11,7 @@ import Cocoa import LetsMove import RxCocoa import RxSwift +import CocoaLumberjack import AppCenter import AppCenterAnalytics @@ -110,6 +111,25 @@ class AppDelegate: NSObject, NSApplicationDelegate { // claer not existed selected model removeUnExistProxyGroups() + // clash logger + if ApiRequest.useDirectApi() { + Logger.log("setup built in logger/traffic") + clash_setLogBlock { line, level in + let clashLevel = ClashLogLevel(rawValue: level ?? "info") + Logger.log(line ?? "", level: clashLevel ?? .info) + } + clashSetupLogger() + + clash_setTrafficBlock { [weak self] up, down in + DispatchQueue.main.async { + self?.didUpdateTraffic(up: Int(up), down: Int(down)) + } + } + clashSetupTraffic() + + } else { + Logger.log("do not setup built in logger/traffic, useDirectApi = false") + } // start proxy Logger.log("initClashCore") initClashCore() @@ -789,6 +809,7 @@ extension AppDelegate { @IBAction func actionSetLogLevel(_ sender: NSMenuItem) { let level = ClashLogLevel(rawValue: sender.title.lowercased()) ?? .unknow ConfigManager.selectLoggingApiLevel = level + dynamicLogLevel = level.toDDLogLevel() updateLoggingLevel() resetStreamApi() } diff --git a/ClashX/Basic/Logger.swift b/ClashX/Basic/Logger.swift index 233cd0349..e99720be8 100644 --- a/ClashX/Basic/Logger.swift +++ b/ClashX/Basic/Logger.swift @@ -23,6 +23,7 @@ class Logger { fileLogger.rollingFrequency = TimeInterval(60 * 60 * 24) // 24 hours fileLogger.logFileManager.maximumNumberOfLogFiles = 3 DDLog.add(fileLogger) + dynamicLogLevel = ConfigManager.selectLoggingApiLevel.toDDLogLevel() } private func logToFile(msg: String, level: ClashLogLevel) { @@ -36,7 +37,7 @@ class Logger { case .warning: DDLogWarn(msg) case .unknow: - DDLogVerbose(msg) + DDLogWarn(msg) } } diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index 99de19da2..ff3d8e05b 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -346,6 +346,7 @@ extension ApiRequest { } private func requestTrafficInfo() { + if ApiRequest.useDirectApi() { return } trafficWebSocketRetryTimer?.invalidate() trafficWebSocketRetryTimer = nil trafficWebSocket?.disconnect(forceTimeout: 0, closeCode: 0) @@ -361,6 +362,7 @@ extension ApiRequest { } private func requestLog() { + if ApiRequest.useDirectApi() { return } loggingWebSocketRetryTimer?.invalidate() loggingWebSocketRetryTimer = nil loggingWebSocket?.disconnect() diff --git a/ClashX/Models/ClashConfig.swift b/ClashX/Models/ClashConfig.swift index 110087212..af112d214 100644 --- a/ClashX/Models/ClashConfig.swift +++ b/ClashX/Models/ClashConfig.swift @@ -6,6 +6,7 @@ // Copyright © 2018年 yichengchen. All rights reserved. // import Foundation +import CocoaLumberjack enum ClashProxyMode: String, Codable { case rule @@ -25,11 +26,28 @@ extension ClashProxyMode { enum ClashLogLevel: String, Codable { case info - case warning + case warning = "warn" case error case debug case silent case unknow = "unknown" + + func toDDLogLevel() -> DDLogLevel { + switch self { + case .info: + return .info + case .warning: + return .warning + case .error: + return .error + case .debug: + return .debug + case .silent: + return .off + case .unknow: + return .error + } + } } class ClashConfig: Codable { diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 0c14b5ade..709a2bf4e 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -1,7 +1,40 @@ package main +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation +#import + +typedef void (^NStringCallback)(NSString *,NSString *); +typedef void (^IntCallback)(int64_t,int64_t); +NStringCallback logCallback; +IntCallback trafficCallback; +void clash_setLogBlock(NStringCallback block) { + logCallback = [block copy]; +} + +void clash_setTrafficBlock(IntCallback block) { + trafficCallback = [block copy]; +} + +static inline void sendLogToUI(char *s, char *level) { + @autoreleasepool { + if (logCallback) { + logCallback([NSString stringWithUTF8String:s], [NSString stringWithUTF8String:level]); + } + } +} + +static inline void sendTrafficToUI(int64_t up, int64_t down) { + if (trafficCallback) { + trafficCallback(up, down); + } +} +*/ +import "C" + import ( - "C" + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -10,6 +43,10 @@ import ( "path/filepath" "strconv" "strings" + "sync" + "syscall" + "time" + "unsafe" "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/constant" @@ -161,6 +198,39 @@ func verifyClashConfig(content *C.char) *C.char { return C.CString("success") } +//export clashSetupLogger +func clashSetupLogger() { + sub := log.Subscribe() + logBuf := bytes.Buffer{} + + go func() { + for l := range sub { + logBuf.Reset() + log.PrettyPrint(&logBuf, l, false, false) + cs := C.CString(logBuf.String()) + cl := C.CString(l.Level) + C.sendLogToUI(cs, cl) + C.free(unsafe.Pointer(cs)) + C.free(unsafe.Pointer(cl)) + } + }() +} + +//export clashSetupTraffic +func clashSetupTraffic() { + go func() { + tick := time.NewTicker(time.Second) + defer tick.Stop() + t := statistic.DefaultManager + buf := &bytes.Buffer{} + for range tick.C { + buf.Reset() + up, down := t.Now() + C.sendTrafficToUI(C.longlong(up), C.longlong(down)) + } + }() +} + //export run func run(checkConfig, allowLan bool, portOverride uint32, externalController *C.char) *C.char { cfg, err := parseDefaultConfigThenStart(checkConfig, allowLan, portOverride, C.GoString(externalController)) From 457f035157fdcc86cb1f5ea5ff86501dc3ceeb5b Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 2 Mar 2023 09:37:22 +0800 Subject: [PATCH 040/122] fix: ci build --- ClashX/goClash/main.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 709a2bf4e..36f535cf9 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -43,8 +43,6 @@ import ( "path/filepath" "strconv" "strings" - "sync" - "syscall" "time" "unsafe" @@ -201,14 +199,11 @@ func verifyClashConfig(content *C.char) *C.char { //export clashSetupLogger func clashSetupLogger() { sub := log.Subscribe() - logBuf := bytes.Buffer{} - go func() { - for l := range sub { - logBuf.Reset() - log.PrettyPrint(&logBuf, l, false, false) - cs := C.CString(logBuf.String()) - cl := C.CString(l.Level) + for elm := range sub { + log := elm.(log.Event) + cs := C.CString(log.Payload) + cl := C.CString(log.Type()) C.sendLogToUI(cs, cl) C.free(unsafe.Pointer(cs)) C.free(unsafe.Pointer(cl)) From 7fa11a386e4890dd21b15dabc9bd48fb57bf795e Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 2 Mar 2023 10:44:21 +0800 Subject: [PATCH 041/122] misc: clangWrap is no longer needed --- ClashX/goClash/build_clash_universal.py | 10 +++------- ClashX/goClash/clangWrap.sh | 12 ------------ 2 files changed, 3 insertions(+), 19 deletions(-) delete mode 100755 ClashX/goClash/clangWrap.sh diff --git a/ClashX/goClash/build_clash_universal.py b/ClashX/goClash/build_clash_universal.py index 74ad927b8..2a153f163 100644 --- a/ClashX/goClash/build_clash_universal.py +++ b/ClashX/goClash/build_clash_universal.py @@ -14,20 +14,17 @@ def get_version(): go_bin = "go" def build_clash(version,build_time,arch): - clang = f"{os.getcwd()}/clangWrap.sh" command = f""" {go_bin} build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version={version}" \ -X "github.com/Dreamacro/clash/constant.BuildTime={build_time}"' \ -buildmode=c-archive -o goClash_{arch}.a """ envs = os.environ.copy() envs.update({ - "CC":clang, - "CXX":clang, "GOOS":"darwin", "GOARCH":arch, "CGO_ENABLED":"1", - "CGO_LDFLAGS":"-mmacosx-version-min=10.12", - "CGO_CFLAGS":"-mmacosx-version-min=10.12", + "CGO_LDFLAGS":"-mmacosx-version-min=10.14", + "CGO_CFLAGS":"-mmacosx-version-min=10.14", }) subprocess.check_output(command, shell=True,env=envs) @@ -62,8 +59,7 @@ def run(): print("current clash version:", version) build_time = datetime.datetime.now().strftime("%Y-%m-%d-%H%M") print("clean existing") - subprocess.check_output("rm -f *.h *.a", shell=True) - + subprocess.check_output("rm -f *Clash*.h *.a", shell=True) print("create arm64") build_clash(version,build_time,"arm64") print("create amd64") diff --git a/ClashX/goClash/clangWrap.sh b/ClashX/goClash/clangWrap.sh deleted file mode 100755 index f4e21431d..000000000 --- a/ClashX/goClash/clangWrap.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -SDK=macosx -PLATFORM=macosx -if [ "$GOARCH" == "arm64" ]; then - CLANGARCH="arm64" -else - CLANGARCH="x86_64" -fi - -SDK_PATH=`xcrun --sdk $SDK --show-sdk-path` -CLANG=`xcrun --sdk $SDK --find clang` -exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -mmacosx-version-min=10.12 "$@" From e6522be5d984e9f2f05c5dd2471b66d497da53f0 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 2 Mar 2023 10:58:35 +0800 Subject: [PATCH 042/122] fix: spilt c code into header and imp file --- ClashX/goClash/UIHelper.h | 23 +++++++++++++++++++++++ ClashX/goClash/UIHelper.m | 10 ++++++++++ ClashX/goClash/build.sh | 1 - ClashX/goClash/main.go | 27 +-------------------------- 4 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 ClashX/goClash/UIHelper.h create mode 100644 ClashX/goClash/UIHelper.m diff --git a/ClashX/goClash/UIHelper.h b/ClashX/goClash/UIHelper.h new file mode 100644 index 000000000..bc6c34eb8 --- /dev/null +++ b/ClashX/goClash/UIHelper.h @@ -0,0 +1,23 @@ +#import + +typedef void (^NStringCallback)(NSString *,NSString *); +typedef void (^IntCallback)(int64_t,int64_t); +extern NStringCallback logCallback; +extern IntCallback trafficCallback; +void clash_setLogBlock(NStringCallback block); + +void clash_setTrafficBlock(IntCallback block); + +static inline void sendLogToUI(char *s, char *level) { + @autoreleasepool { + if (logCallback) { + logCallback([NSString stringWithUTF8String:s], [NSString stringWithUTF8String:level]); + } + } +} + +static inline void sendTrafficToUI(int64_t up, int64_t down) { + if (trafficCallback) { + trafficCallback(up, down); + } +} diff --git a/ClashX/goClash/UIHelper.m b/ClashX/goClash/UIHelper.m new file mode 100644 index 000000000..eaf4e3f8e --- /dev/null +++ b/ClashX/goClash/UIHelper.m @@ -0,0 +1,10 @@ +#import "UIHelper.h" +NStringCallback logCallback; +IntCallback trafficCallback; +void clash_setLogBlock(NStringCallback block) { + logCallback = [block copy]; +} + +void clash_setTrafficBlock(IntCallback block) { + trafficCallback = [block copy]; +} \ No newline at end of file diff --git a/ClashX/goClash/build.sh b/ClashX/goClash/build.sh index 160049fa8..62b8d0618 100755 --- a/ClashX/goClash/build.sh +++ b/ClashX/goClash/build.sh @@ -1,2 +1 @@ -rm -f *.h *.a python3 build_clash_universal.py diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 36f535cf9..66ee869b1 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -4,32 +4,7 @@ package main #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Foundation #import - -typedef void (^NStringCallback)(NSString *,NSString *); -typedef void (^IntCallback)(int64_t,int64_t); -NStringCallback logCallback; -IntCallback trafficCallback; -void clash_setLogBlock(NStringCallback block) { - logCallback = [block copy]; -} - -void clash_setTrafficBlock(IntCallback block) { - trafficCallback = [block copy]; -} - -static inline void sendLogToUI(char *s, char *level) { - @autoreleasepool { - if (logCallback) { - logCallback([NSString stringWithUTF8String:s], [NSString stringWithUTF8String:level]); - } - } -} - -static inline void sendTrafficToUI(int64_t up, int64_t down) { - if (trafficCallback) { - trafficCallback(up, down); - } -} +#import "UIHelper.h" */ import "C" From aaaa0e3d25d269fdf79dec1d9879419029483f06 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 2 Mar 2023 11:13:24 +0800 Subject: [PATCH 043/122] feat: use meanDelay to show proxy delay --- ClashX/Models/ClashProxy.swift | 5 +++-- ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index 1b477232a..3b8e897b3 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -59,6 +59,7 @@ typealias ClashProviderName = String class ClashProxySpeedHistory: Codable { let time: Date let delay: Int + let meanDelay: Int class HisDateFormaterInstance { static let shared = HisDateFormaterInstance() @@ -70,9 +71,9 @@ class ClashProxySpeedHistory: Codable { } lazy var delayDisplay: String = { - switch delay { + switch meanDelay { case 0: return NSLocalizedString("fail", comment: "") - default: return "\(delay) ms" + default: return "\(meanDelay) ms" } }() diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 5feae1efa..5e5a9e887 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.13.1-0.20230216134340-8173d6681b07 + github.com/Dreamacro/clash v1.13.1-0.20230228052842-f78a7cb2cbe3 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 08d402a91..2b9ef2682 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.13.1-0.20230216134340-8173d6681b07 h1:GVbK1trdQHCTyIVu6ikFbLtIr8o07U9h/dAv7YEbA88= -github.com/Dreamacro/clash v1.13.1-0.20230216134340-8173d6681b07/go.mod h1:0hkl4QlO1dxG82u72o5VWihNL1MZOq8MOndr8Hpv17A= +github.com/Dreamacro/clash v1.13.1-0.20230228052842-f78a7cb2cbe3 h1:ZIRDYdwaOHfpgcbZlHzov+c/4BIcftiYfdpuPqmA2O0= +github.com/Dreamacro/clash v1.13.1-0.20230228052842-f78a7cb2cbe3/go.mod h1:0hkl4QlO1dxG82u72o5VWihNL1MZOq8MOndr8Hpv17A= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 2d954457609fe0d46358acc73bda6d3ed6a201e0 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 2 Mar 2023 14:33:24 +0800 Subject: [PATCH 044/122] misc: read tag version on tag build --- .github/workflows/main.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 898b1b807..fe2178f33 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,6 +32,13 @@ jobs: run: | bundle exec fastlane beta bundle exec fastlane run set_info_plist_value path:ClashX/Info.plist key:BETA value:YES + + - name: update tag build version + if: startsWith(github.ref, 'refs/tags/') + run: | + tag=${GITHUB_REF##*/} + bundle exec fastlane run increment_build_number_in_plist build_number:"${tag}" scheme:"ClashX" + bundle exec fastlane run increment_version_number_in_plist version_number:"${tag}" scheme:"ClashX" - name: build env: From d73d70ce0818198b5a0b42b11df8514ba4be3324 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:15:02 +0800 Subject: [PATCH 045/122] fix: fix allow api lan usage button --- ClashX/Base.lproj/Main.storyboard | 1 + .../Settings/GeneralSettingViewController.swift | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 32e43db7f..e4284e6dc 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -309,6 +309,7 @@ + diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 9c3efdebd..70b38d4a2 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -16,6 +16,7 @@ class GeneralSettingViewController: NSViewController { @IBOutlet weak var reduceNotificationsButton: NSButton! @IBOutlet weak var useiCloudButton: NSButton! + @IBOutlet weak var allowApiLanUsageSwitcher: NSButton! @IBOutlet weak var proxyPortTextField: NSTextField! @IBOutlet weak var apiPortTextField: NSTextField! @@ -75,6 +76,10 @@ class GeneralSettingViewController: NSViewController { .bind { Settings.apiPort = $0 }.disposed(by: disposeBag) + + allowApiLanUsageSwitcher.rx.state.bind { state in + Settings.apiPortAllowLan = state == .on + }.disposed(by: disposeBag) } override func viewDidAppear() { From 5c9b35fbaccb90230ec1af52fe831c99958a971c Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:22:43 +0800 Subject: [PATCH 046/122] misc: allow user define standalone proxy port --- ClashX/goClash/main.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 66ee869b1..30de6c9f5 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -100,8 +100,12 @@ func parseDefaultConfigThenStart(checkPort, allowLan bool, proxyPort uint32, ext if proxyPort > 0 { rawCfg.MixedPort = int(proxyPort) - rawCfg.SocksPort = 0 - rawCfg.Port = 0 + if rawCfg.Port == rawCfg.MixedPort { + rawCfg.Port = 0 + } + if rawCfg.SocksPort == rawCfg.MixedPort { + rawCfg.SocksPort = 0 + } } else { if rawCfg.MixedPort == 0 { if rawCfg.Port > 0 { From 2f5dc5c642f508bb4a6fd7bb5f1e9caa1a6cf904 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Fri, 24 Mar 2023 16:31:04 +0800 Subject: [PATCH 047/122] misc: some core does not have mean delay --- ClashX/Models/ClashProxy.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index 3b8e897b3..61181688a 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -59,7 +59,7 @@ typealias ClashProviderName = String class ClashProxySpeedHistory: Codable { let time: Date let delay: Int - let meanDelay: Int + let meanDelay: Int? class HisDateFormaterInstance { static let shared = HisDateFormaterInstance() @@ -71,9 +71,16 @@ class ClashProxySpeedHistory: Codable { } lazy var delayDisplay: String = { - switch meanDelay { - case 0: return NSLocalizedString("fail", comment: "") - default: return "\(meanDelay) ms" + if let meanDelay, meanDelay > 0 { + switch meanDelay { + case 0: return NSLocalizedString("fail", comment: "") + default: return "\(meanDelay) ms" + } + } else { + switch delay { + case 0: return NSLocalizedString("fail", comment: "") + default: return "\(delay) ms" + } } }() From d6a04af93052350030fdca0eef8419848c79bb43 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 27 Mar 2023 09:08:37 +0800 Subject: [PATCH 048/122] misc: fix speed issue when use remote conifg --- ClashX/AppDelegate.swift | 6 ++++-- ClashX/General/ApiRequest.swift | 14 ++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index a357f513d..39a9805f5 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -121,8 +121,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { clashSetupLogger() clash_setTrafficBlock { [weak self] up, down in - DispatchQueue.main.async { - self?.didUpdateTraffic(up: Int(up), down: Int(down)) + if RemoteControlManager.selectConfig == nil { + DispatchQueue.main.async { + self?.didUpdateTraffic(up: Int(up), down: Int(down)) + } } } clashSetupTraffic() diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index ff3d8e05b..27774276a 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -346,10 +346,13 @@ extension ApiRequest { } private func requestTrafficInfo() { - if ApiRequest.useDirectApi() { return } + if ApiRequest.useDirectApi() { + trafficWebSocket?.disconnect(forceTimeout: 0.5) + return + } trafficWebSocketRetryTimer?.invalidate() trafficWebSocketRetryTimer = nil - trafficWebSocket?.disconnect(forceTimeout: 0, closeCode: 0) + trafficWebSocket?.disconnect(forceTimeout: 0.5) let socket = WebSocket(url: URL(string: ConfigManager.apiUrl.appending("/traffic"))!) @@ -362,10 +365,13 @@ extension ApiRequest { } private func requestLog() { - if ApiRequest.useDirectApi() { return } + if ApiRequest.useDirectApi() { + loggingWebSocket?.disconnect(forceTimeout: 1) + return + } loggingWebSocketRetryTimer?.invalidate() loggingWebSocketRetryTimer = nil - loggingWebSocket?.disconnect() + loggingWebSocket?.disconnect(forceTimeout: 1) let uriString = "/logs?level=".appending(ConfigManager.selectLoggingApiLevel.rawValue) let socket = WebSocket(url: URL(string: ConfigManager.apiUrl.appending(uriString))!) From 7c15841e80ba59a3530adfb34a65c8ed081e18d1 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 9 Jan 2021 22:28:21 +0800 Subject: [PATCH 049/122] chore: acceptsFirstMouse on dashboard webview --- .../ClashWebViewContoller.swift | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index dd8f7a08b..73dc89ac0 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -201,13 +201,24 @@ extension ClashWebViewContoller: WKUIDelegate, WKNavigationDelegate { class CustomWKWebView: WKWebView { var dragableAreaHeight: CGFloat = 30 let alwaysDragableLeftAreaWidth: CGFloat = 150 - - override func mouseDown(with event: NSEvent) { - super.mouseDown(with: event) + + private func isInDargArea(with event:NSEvent?) -> Bool { + guard let event = event else { return false } let x = event.locationInWindow.x let y = (window?.frame.size.height ?? 0) - event.locationInWindow.y + return x < alwaysDragableLeftAreaWidth || y < dragableAreaHeight + } + + override func acceptsFirstMouse(for event: NSEvent?) -> Bool { + if isInDargArea(with: event) { + return true + } + return super.acceptsFirstMouse(for: event) + } - if x < alwaysDragableLeftAreaWidth || y < dragableAreaHeight { + override func mouseDown(with event: NSEvent) { + super.mouseDown(with: event) + if isInDargArea(with: event) { window?.performDrag(with: event) } } From 19a4f8bfaf1dcfc263b0bc02f59bc0feb8b7f6d3 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:29:14 +0800 Subject: [PATCH 050/122] misc: update core, enable crash collect --- .github/workflows/main.yml | 5 +- ClashX/AppDelegate.swift | 9 +- .../ClashWebViewContoller.swift | 4 +- ClashX/goClash/go.mod | 23 ++-- ClashX/goClash/go.sum | 119 ++++-------------- Podfile | 1 + Podfile.lock | 13 +- 7 files changed, 58 insertions(+), 116 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe2178f33..e33fd2a90 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -83,6 +83,7 @@ jobs: APPCENTER_API_TOKEN: ${{ secrets.APPCENTER_API_TOKEN }} APPCENTER_DISTRIBUTE_FILE: ClashX.dmg APPCENTER_OWNER_NAME: ${{ secrets.APPCENTER_OWNER_NAME }} + APPCENTER_DISTRIBUTE_DSYM: "ClashX.app.dSYM.zip" run: | appversion=`defaults read \`pwd\`/ClashX.app/Contents/Info.plist CFBundleShortVersionString` buildVersion=`defaults read \`pwd\`/ClashX.app/Contents/Info.plist CFBundleVersion` @@ -96,7 +97,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - files: ClashX.dmg + files: | + ClashX.app.dSYM.zip + ClashX.dmg draft: true prerelease: true diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 39a9805f5..effdda31f 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -15,6 +15,7 @@ import CocoaLumberjack import AppCenter import AppCenterAnalytics +import AppCenterCrashes let statusItemLengthWithSpeed: CGFloat = 72 @@ -59,8 +60,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { var dashboardWindowController: ClashWebViewWindowController? func applicationWillFinishLaunching(_ notification: Notification) { + Logger.log("applicationWillFinishLaunching") signal(SIGPIPE, SIG_IGN) - // crash recorder failLaunchProtect() NSAppleEventManager.shared() .setEventHandler(self, @@ -82,13 +83,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { } statusItemView.updateSize(width: statusItemLengthWithSpeed) statusMenu.delegate = self - registCrashLogger() DispatchQueue.main.async { self.postFinishLaunching() } } func postFinishLaunching() { + Logger.log("postFinishLaunching") defer { statusItem.menu = statusMenu DispatchQueue.main.asyncAfter(deadline: .now()+1) { @@ -151,6 +152,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { RemoteConfigManager.shared.autoUpdateCheck() setupNetworkNotifier() + registCrashLogger() } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { @@ -877,7 +879,8 @@ extension AppDelegate { #else DispatchQueue.main.asyncAfter(deadline: .now() + 5) { AppCenter.start(withAppSecret: "dce6e9a3-b6e3-4fd2-9f2d-35c767a99663", services: [ - Analytics.self + Analytics.self, + Crashes.self ]) } diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index 73dc89ac0..66773537e 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -201,14 +201,14 @@ extension ClashWebViewContoller: WKUIDelegate, WKNavigationDelegate { class CustomWKWebView: WKWebView { var dragableAreaHeight: CGFloat = 30 let alwaysDragableLeftAreaWidth: CGFloat = 150 - + private func isInDargArea(with event:NSEvent?) -> Bool { guard let event = event else { return false } let x = event.locationInWindow.x let y = (window?.frame.size.height ?? 0) - event.locationInWindow.y return x < alwaysDragableLeftAreaWidth || y < dragableAreaHeight } - + override func acceptsFirstMouse(for event: NSEvent?) -> Bool { if isInDargArea(with: event) { return true diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 5e5a9e887..c3c7d4ac5 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.13.1-0.20230228052842-f78a7cb2cbe3 + github.com/Dreamacro/clash v1.14.1-0.20230326082223-7e2974f02ffa github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) @@ -13,27 +13,28 @@ require ( github.com/go-chi/chi/v5 v5.0.8 // indirect github.com/go-chi/cors v1.2.1 // indirect github.com/go-chi/render v1.0.2 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 // indirect + github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 // indirect github.com/josharian/native v1.1.0 // indirect github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/miekg/dns v1.1.50 // indirect + github.com/miekg/dns v1.1.52 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect + github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/samber/lo v1.37.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect - github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect + github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.6.0 // indirect + golang.org/x/crypto v0.7.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/tools v0.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 2b9ef2682..f0a9cb64c 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,143 +1,74 @@ -github.com/Dreamacro/clash v1.13.1-0.20230228052842-f78a7cb2cbe3 h1:ZIRDYdwaOHfpgcbZlHzov+c/4BIcftiYfdpuPqmA2O0= -github.com/Dreamacro/clash v1.13.1-0.20230228052842-f78a7cb2cbe3/go.mod h1:0hkl4QlO1dxG82u72o5VWihNL1MZOq8MOndr8Hpv17A= +github.com/Dreamacro/clash v1.14.1-0.20230326082223-7e2974f02ffa h1:ha9reJ8txD0zD420txU3fOoFdm6KZ9iBRJ48PnHrBvc= +github.com/Dreamacro/clash v1.14.1-0.20230326082223-7e2974f02ffa/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= +github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg= -github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE= +github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M= +github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= -github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4= github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ= -github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= +github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= +github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww= -github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= +github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/Podfile b/Podfile index e5e55e772..2b2d376da 100644 --- a/Podfile +++ b/Podfile @@ -27,6 +27,7 @@ target 'ClashX' do pod 'WebViewJavascriptBridge' pod 'Starscream','3.1.1' pod 'AppCenter/Analytics' + pod 'AppCenter/Crashes' pod 'Sparkle','~>1.0' pod "FlexibleDiff" pod 'GzipSwift' diff --git a/Podfile.lock b/Podfile.lock index 5b79655d4..7d8bdb667 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,8 +1,10 @@ PODS: - Alamofire (5.6.4) - - AppCenter/Analytics (5.0.1): + - AppCenter/Analytics (5.0.2): + - AppCenter/Core + - AppCenter/Core (5.0.2) + - AppCenter/Crashes (5.0.2): - AppCenter/Core - - AppCenter/Core (5.0.1) - CocoaLumberjack/Core (3.8.0) - CocoaLumberjack/Swift (3.8.0): - CocoaLumberjack/Core @@ -23,6 +25,7 @@ PODS: DEPENDENCIES: - Alamofire (~> 5.0) - AppCenter/Analytics + - AppCenter/Crashes - CocoaLumberjack/Swift - FlexibleDiff - GzipSwift @@ -52,7 +55,7 @@ SPEC REPOS: SPEC CHECKSUMS: Alamofire: 4e95d97098eacb88856099c4fc79b526a299e48c - AppCenter: 18153bb6bc4241d14c8cce57466ac1c88136b476 + AppCenter: 355ba776b273d30147c3ac385c558bec60a7d4b1 CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa @@ -65,6 +68,6 @@ SPEC CHECKSUMS: SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 -PODFILE CHECKSUM: 8723148a909e836604a13ea038cf5206d1d450a0 +PODFILE CHECKSUM: 9b9952fcf44ecd26932a2cf4bb79d03f8850d67b -COCOAPODS: 1.11.0 +COCOAPODS: 1.11.3 From b9c45071b6195822ab99cdd02fb8bce4a50cbe02 Mon Sep 17 00:00:00 2001 From: Miigon Date: Sat, 25 Mar 2023 21:31:11 +0800 Subject: [PATCH 051/122] ClashLogLevel.warning = "warning" instead of "warn" `log-level` should be `info / warning / error / debug / silent`, `warn` is not a valid log level. fixes #966 #982 --- ClashX/Models/ClashConfig.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClashX/Models/ClashConfig.swift b/ClashX/Models/ClashConfig.swift index af112d214..85333c9c3 100644 --- a/ClashX/Models/ClashConfig.swift +++ b/ClashX/Models/ClashConfig.swift @@ -26,7 +26,7 @@ extension ClashProxyMode { enum ClashLogLevel: String, Codable { case info - case warning = "warn" + case warning case error case debug case silent From af2b28163fed67f7ce42e5618654a26f38741af3 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:56:03 +0800 Subject: [PATCH 052/122] misc: update core --- .github/workflows/main.yml | 2 +- ClashX.xcodeproj/project.pbxproj | 4 ++++ ClashX/goClash/go.mod | 19 ++++++++------- ClashX/goClash/go.sum | 40 +++++++++++++++++++------------- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e33fd2a90..e05a47b50 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ env: jobs: build: - runs-on: macos-12 + runs-on: macos-13 steps: - uses: actions/checkout@v3 - name: import certs diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 3310bbce4..52fd54731 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4908087A29F8F3FF007A4944 /* libresolv.tbd */; }; 4913C82321157D0200F6B87C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913C82221157D0200F6B87C /* Notification.swift */; }; 491E6203258A424D00313AEF /* CommonUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 491E61FD258A424500313AEF /* CommonUtils.m */; }; 49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */; }; @@ -131,6 +132,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4908087A29F8F3FF007A4944 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; }; 4913C82221157D0200F6B87C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 491E61FC258A424500313AEF /* CommonUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = ""; }; 491E61FD258A424500313AEF /* CommonUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUtils.m; sourceTree = ""; }; @@ -245,6 +247,7 @@ files = ( F9C180A3243C6590005EE8C4 /* goClash.a in Frameworks */, 9E0E2F1481CB327D3753969D /* libPods-ClashX.a in Frameworks */, + 4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -512,6 +515,7 @@ CF1AC9FACC36FCE7663C5583 /* Frameworks */ = { isa = PBXGroup; children = ( + 4908087A29F8F3FF007A4944 /* libresolv.tbd */, CAE7981A9F34B5E210C549CB /* libPods-ClashX.a */, ); name = Frameworks; diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index c3c7d4ac5..b7c4f15bc 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,12 +3,13 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.14.1-0.20230326082223-7e2974f02ffa + github.com/Dreamacro/clash v1.15.2-0.20230425121611-48b77b28475d github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) require ( + github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd // indirect github.com/ajg/form v1.5.1 // indirect github.com/go-chi/chi/v5 v5.0.8 // indirect github.com/go-chi/cors v1.2.1 // indirect @@ -16,25 +17,27 @@ require ( github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 // indirect + github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 // indirect github.com/josharian/native v1.1.0 // indirect github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/miekg/dns v1.1.52 // indirect + github.com/miekg/dns v1.1.53 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect - github.com/samber/lo v1.37.0 // indirect + github.com/samber/lo v1.38.1 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect + github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 // indirect + github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.7.0 // indirect + golang.org/x/crypto v0.8.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.9.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/tools v0.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index f0a9cb64c..398d943a2 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,7 @@ -github.com/Dreamacro/clash v1.14.1-0.20230326082223-7e2974f02ffa h1:ha9reJ8txD0zD420txU3fOoFdm6KZ9iBRJ48PnHrBvc= -github.com/Dreamacro/clash v1.14.1-0.20230326082223-7e2974f02ffa/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I= +github.com/Dreamacro/clash v1.15.2-0.20230425121611-48b77b28475d h1:Ccn4mWbJMqp/hCobP2CGqfwUfgLwp5oaYcRAB0nnuT8= +github.com/Dreamacro/clash v1.15.2-0.20230425121611-48b77b28475d/go.mod h1:pnkY25XNTM5Xo/IvZnIXJOf8M5rpjBBfACB+wJD9/mI= +github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= +github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -17,8 +19,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M= -github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= +github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E= +github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -26,8 +28,8 @@ github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcR github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= -github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= +github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= @@ -38,8 +40,8 @@ github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= -github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -47,26 +49,32 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 h1:F9xjJm4IH8VjcqG4ujciOF+GIM4mjPkHhWLLzOghPtM= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From dd5143bea82bca670489f4b24930d7bccdeefa1e Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:14:19 +0800 Subject: [PATCH 053/122] fix: temp remove swiftlint for ci fail --- .../xcshareddata/xcschemes/ClashX.xcscheme | 20 +------------------ .../Managers/PrivilegedHelperManager.swift | 3 +-- ClashX/General/Managers/Settings.swift | 8 ++++---- .../General/Utils/NetworkChangeNotifier.swift | 4 ++-- .../LoginServiceKit/LoginServiceKit.swift | 6 +++--- .../ClashWebViewContoller.swift | 2 +- .../Views/StatusItem/NewStatusItemView.swift | 6 +++--- 7 files changed, 15 insertions(+), 34 deletions(-) diff --git a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme index 83ba271a9..5fe716f38 100644 --- a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme +++ b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme @@ -1,28 +1,10 @@ + version = "1.3"> - - - - - - - - - - Void)) { + private func getHelperStatus(callback: @escaping ((HelperStatus) -> Void)) { var called = false let reply: ((HelperStatus) -> Void) = { status in @@ -279,7 +279,6 @@ private enum DaemonInstallResult { case kSMErrorToolNotValid: return "blessError: kSMErrorToolNotValid" case kSMErrorJobNotFound: return "blessError: kSMErrorJobNotFound" case kSMErrorServiceUnavailable: return "blessError: kSMErrorServiceUnavailable" - case kSMErrorJobNotFound: return "blessError: kSMErrorJobNotFound" case kSMErrorJobMustBeEnabled: return "ClashX Helper is disabled by other process. Please run \"sudo launchctl enable system/\(PrivilegedHelperManager.machServiceName)\" in your terminal. The command has been copied to your pasteboard" case kSMErrorInvalidPlist: return "blessError: kSMErrorInvalidPlist" default: diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index 2f8489051..c3fd859b2 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -9,16 +9,16 @@ import Foundation enum Settings { @UserDefault("mmdbDownloadUrl", defaultValue: "") - static var mmdbDownloadUrl:String + static var mmdbDownloadUrl: String @UserDefault("filterInterface", defaultValue: true) - static var filterInterface:Bool + static var filterInterface: Bool @UserDefault("disableNoti", defaultValue: false) - static var disableNoti:Bool + static var disableNoti: Bool @UserDefault("usePacMode", defaultValue: false) - static var usePacMode:Bool + static var usePacMode: Bool @UserDefault("configAutoUpdateInterval", defaultValue: 48*60*60) static var configAutoUpdateInterval: TimeInterval diff --git a/ClashX/General/Utils/NetworkChangeNotifier.swift b/ClashX/General/Utils/NetworkChangeNotifier.swift index 492e3a56c..6f1e5e787 100644 --- a/ClashX/General/Utils/NetworkChangeNotifier.swift +++ b/ClashX/General/Utils/NetworkChangeNotifier.swift @@ -78,7 +78,7 @@ class NetworkChangeNotifier { return (httpProxy, httpsProxy, socksProxy) } - static func isCurrentSystemSetToClash(looser:Bool = false) -> Bool { + static func isCurrentSystemSetToClash(looser: Bool = false) -> Bool { let (http, https, socks) = NetworkChangeNotifier.currentSystemProxySetting() let currentPort = ConfigManager.shared.currentConfig?.usedHttpPort ?? 0 let currentSocks = ConfigManager.shared.currentConfig?.usedSocksPort ?? 0 @@ -99,7 +99,7 @@ class NetworkChangeNotifier { let sets = SCPreferencesGetValue(prefRef, kSCPrefNetworkServices) { for key in sets.allKeys { let dict = sets[key] as? NSDictionary - let proxySettings = dict?["Proxies"] as? [String:Any] + let proxySettings = dict?["Proxies"] as? [String: Any] if currentPort != nil { if proxySettings?[kCFNetworkProxiesHTTPPort as String] as? Int == currentPort || proxySettings?[kCFNetworkProxiesHTTPSPort as String] as? Int == currentPort { diff --git a/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift b/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift index 6e78bd0fb..a3265dea5 100644 --- a/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift +++ b/ClashX/Vendor/LoginServiceKit/LoginServiceKit.swift @@ -69,7 +69,7 @@ public final class LoginServiceKit { guard isExistLoginItems(at: path) == false else { return false } - + if #available(macOS 13.0, *) { do { try SMAppService.mainApp.register() @@ -79,7 +79,7 @@ public final class LoginServiceKit { return false } } - + guard let (list, _) = snapshot else { return false } @@ -93,7 +93,7 @@ public final class LoginServiceKit { guard isExistLoginItems(at: path) == true else { return false } - + if #available(macOS 13.0, *) { do { try SMAppService.mainApp.unregister() diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index 66773537e..bcfc616b3 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -202,7 +202,7 @@ class CustomWKWebView: WKWebView { var dragableAreaHeight: CGFloat = 30 let alwaysDragableLeftAreaWidth: CGFloat = 150 - private func isInDargArea(with event:NSEvent?) -> Bool { + private func isInDargArea(with event: NSEvent?) -> Bool { guard let event = event else { return false } let x = event.locationInWindow.x let y = (window?.frame.size.height ?? 0) - event.locationInWindow.y diff --git a/ClashX/Views/StatusItem/NewStatusItemView.swift b/ClashX/Views/StatusItem/NewStatusItemView.swift index dc1b500a6..ca5f4ea9c 100644 --- a/ClashX/Views/StatusItem/NewStatusItemView.swift +++ b/ClashX/Views/StatusItem/NewStatusItemView.swift @@ -9,7 +9,7 @@ import AppKit import SwiftUI @available(macOS 10.15, *) -class NewStatusMenuView:NSHostingView, StatusItemViewProtocol { +class NewStatusMenuView: NSHostingView, StatusItemViewProtocol { private var viewModel: StatusMenuViewModel! static func create(on button: NSView) -> NewStatusMenuView { @@ -45,7 +45,7 @@ class NewStatusMenuView:NSHostingView, StatusItemViewProtocol { } @available(macOS 10.15, *) -class StatusMenuViewModel:ObservableObject { +class StatusMenuViewModel: ObservableObject { @Published var image = StatusItemTool.getMenuImage(enableProxy: false) @Published var upSpeed = "0KB/s" @Published var downSpeed = "0KB/s" @@ -56,7 +56,7 @@ class StatusMenuViewModel:ObservableObject { struct SwiftUIView: View { @ObservedObject var viewModel: StatusMenuViewModel var body: some View { - HStack(alignment:.center) { + HStack(alignment: .center) { Image(nsImage: $viewModel.image.wrappedValue).renderingMode(.template) if $viewModel.showSpeed.wrappedValue { Spacer(minLength: 0) From 5d83896080a98b1bd05fa97f11cb74594b0cedb3 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:57:43 +0800 Subject: [PATCH 054/122] ci: update SMJobBlessUtil.py from https://gist.github.com/mikeyh/89a1e2ecc6849ff6056b7391c5216799 --- .github/workflows/main.yml | 2 +- SMJobBlessUtil.py | 265 ++++++++++++++++++++----------------- 2 files changed, 143 insertions(+), 124 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e05a47b50..82090dd05 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,7 +49,7 @@ jobs: cd .. bundle exec fastlane build echo "Checking SMJobBless Vailded" - python SMJobBlessUtil.py check ClashX.app + python3 SMJobBlessUtil.py check ClashX.app echo "Check done" - name: setup node diff --git a/SMJobBlessUtil.py b/SMJobBlessUtil.py index fd54a506e..349750f7e 100644 --- a/SMJobBlessUtil.py +++ b/SMJobBlessUtil.py @@ -1,20 +1,20 @@ -#! /usr/bin/python -# +#! /usr/bin/python3 +# # File: SMJobBlessUtil.py -# +# # Contains: Tool for checking and correcting apps that use SMJobBless. -# +# # Written by: DTS -# +# # Copyright: Copyright (c) 2012 Apple Inc. All Rights Reserved. -# +# # Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. # ("Apple") in consideration of your agreement to the following # terms, and your use, installation, modification or # redistribution of this Apple software constitutes acceptance of # these terms. If you do not agree with these terms, please do # not use, install, modify or redistribute this Apple software. -# +# # In consideration of your agreement to abide by the following # terms, and subject to these terms, Apple grants you a personal, # non-exclusive license, under Apple's copyrights in this @@ -32,14 +32,14 @@ # are granted by Apple herein, including but not limited to any # patent rights that may be infringed by your derivative works or # by other works in which the Apple Software may be incorporated. -# -# The Apple Software is provided by Apple on an "AS IS" basis. +# +# The Apple Software is provided by Apple on an "AS IS" basis. # APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING # WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING # THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN # COMBINATION WITH YOUR PRODUCTS. -# +# # IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, # INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, @@ -49,7 +49,7 @@ # OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR # OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. -# +# import sys import os @@ -57,17 +57,18 @@ import subprocess import plistlib import operator +import platform class UsageException (Exception): """ - Raised when the progam detects a usage issue; the top-level code catches this + Raised when the progam detects a usage issue; the top-level code catches this and prints a usage message. """ pass class CheckException (Exception): """ - Raised when the "check" subcommand detects a problem; the top-level code catches + Raised when the "check" subcommand detects a problem; the top-level code catches this and prints a nice error message. """ def __init__(self, message, path=None): @@ -77,38 +78,38 @@ def __init__(self, message, path=None): def checkCodeSignature(programPath, programType): """Checks the code signature of the referenced program.""" - # Use the codesign tool to check the signature. The second "-v" is required to enable - # verbose mode, which causes codesign to do more checking. By default it does the minimum - # amount of checking ("Is the program properly signed?"). If you enabled verbose mode it - # does other sanity checks, which we definitely want. The specific thing I'd like to - # detect is "Does the code satisfy its own designated requirement?" and I need to enable + # Use the codesign tool to check the signature. The second "-v" is required to enable + # verbose mode, which causes codesign to do more checking. By default it does the minimum + # amount of checking ("Is the program properly signed?"). If you enabled verbose mode it + # does other sanity checks, which we definitely want. The specific thing I'd like to + # detect is "Does the code satisfy its own designated requirement?" and I need to enable # verbose mode to get that. args = [ - # "false", - "codesign", - "-v", + # "false", + "codesign", + "-v", "-v", programPath ] try: subprocess.check_call(args, stderr=open("/dev/null")) - except subprocess.CalledProcessError, e: + except subprocess.CalledProcessError as e: raise CheckException("%s code signature invalid" % programType, programPath) - + def readDesignatedRequirement(programPath, programType): """Returns the designated requirement of the program as a string.""" args = [ - # "false", - "codesign", - "-d", - "-r", - "-", + # "false", + "codesign", + "-d", + "-r", + "-", programPath ] try: - req = subprocess.check_output(args, stderr=open("/dev/null")) - except subprocess.CalledProcessError, e: + req = subprocess.check_output(args, stderr=open("/dev/null"), encoding="utf-8") + except subprocess.CalledProcessError as e: raise CheckException("%s designated requirement unreadable" % programType, programPath) reqLines = req.splitlines() @@ -119,77 +120,97 @@ def readDesignatedRequirement(programPath, programType): def readInfoPlistFromPath(infoPath): """Reads an "Info.plist" file from the specified path.""" try: - info = plistlib.readPlist(infoPath) + with open(infoPath, 'rb') as fp: + info = plistlib.load(fp) except: raise CheckException("'Info.plist' not readable", infoPath) if not isinstance(info, dict): raise CheckException("'Info.plist' root must be a dictionary", infoPath) return info - + def readPlistFromToolSection(toolPath, segmentName, sectionName): """Reads a dictionary property list from the specified section within the specified executable.""" - + # Run otool -s to get a hex dump of the section. - + args = [ - # "false", - "otool", - "-s", - segmentName, - sectionName, + # "false", + "otool", + "-V", + "-arch", + platform.machine(), + "-s", + segmentName, + sectionName, toolPath ] try: - plistDump = subprocess.check_output(args) - except subprocess.CalledProcessError, e: + plistDump = subprocess.check_output(args, encoding="utf-8") + except subprocess.CalledProcessError as e: raise CheckException("tool %s / %s section unreadable" % (segmentName, sectionName), toolPath) - # Convert that hex dump to an property list. - - plistLines = plistDump.splitlines() - if len(plistLines) < 3 or plistLines[1] != ("Contents of (%s,%s) section" % (segmentName, sectionName)): + # Convert that dump to an property list. + + plistLines = plistDump.strip().splitlines(keepends=True) + + if len(plistLines) < 3: raise CheckException("tool %s / %s section dump malformed (1)" % (segmentName, sectionName), toolPath) + + header = plistLines[1].strip() + + if not header.endswith("(%s,%s) section" % (segmentName, sectionName)): + raise CheckException("tool %s / %s section dump malformed (2)" % (segmentName, sectionName), toolPath) + del plistLines[0:2] try: - bytes = [] - for line in plistLines: - # line looks like this: - # - # '0000000100000b80\t3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 ' - columns = line.split("\t") - assert len(columns) == 2 - for hexStr in columns[1].split(): - bytes.append(int(hexStr, 16)) - plist = plistlib.readPlistFromString(bytearray(bytes)) + + if header.startswith('Contents of'): + data = [] + for line in plistLines: + # line looks like this: + # + # '100000000 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 |= 2 + del columns[0] + for hexStr in columns: + data.append(int(hexStr, 16)) + data = bytes(data) + else: + data = bytes("".join(plistLines), encoding="utf-8") + + plist = plistlib.loads(data) except: - raise CheckException("tool %s / %s section dump malformed (2)" % (segmentName, sectionName), toolPath) + raise CheckException("tool %s / %s section dump malformed (3)" % (segmentName, sectionName), toolPath) # Check the root of the property list. - + if not isinstance(plist, dict): raise CheckException("tool %s / %s property list root must be a dictionary" % (segmentName, sectionName), toolPath) return plist - + def checkStep1(appPath): """Checks that the app and the tool are both correctly code signed.""" - + if not os.path.isdir(appPath): raise CheckException("app not found", appPath) - + # Check the app's code signature. - + checkCodeSignature(appPath, "app") - + # Check the tool directory. - + toolDirPath = os.path.join(appPath, "Contents", "Library", "LaunchServices") if not os.path.isdir(toolDirPath): raise CheckException("tool directory not found", toolDirPath) # Check each tool's code signature. - + toolPathList = [] for toolName in os.listdir(toolDirPath): if toolName != ".DS_Store": @@ -200,50 +221,50 @@ def checkStep1(appPath): toolPathList.append(toolPath) # Check that we have at least one tool. - + if len(toolPathList) == 0: raise CheckException("no tools found", toolDirPath) return toolPathList - + def checkStep2(appPath, toolPathList): """Checks the SMPrivilegedExecutables entry in the app's "Info.plist".""" # Create a map from the tool name (not path) to its designated requirement. - + toolNameToReqMap = dict() for toolPath in toolPathList: req = readDesignatedRequirement(toolPath, "tool") toolNameToReqMap[os.path.basename(toolPath)] = req - + # Read the Info.plist for the app and extract the SMPrivilegedExecutables value. - + infoPath = os.path.join(appPath, "Contents", "Info.plist") info = readInfoPlistFromPath(infoPath) - if not info.has_key("SMPrivilegedExecutables"): + if "SMPrivilegedExecutables" not in info: raise CheckException("'SMPrivilegedExecutables' not found", infoPath) infoToolDict = info["SMPrivilegedExecutables"] if not isinstance(infoToolDict, dict): raise CheckException("'SMPrivilegedExecutables' must be a dictionary", infoPath) - + # Check that the list of tools matches the list of SMPrivilegedExecutables entries. - + if sorted(infoToolDict.keys()) != sorted(toolNameToReqMap.keys()): raise CheckException("'SMPrivilegedExecutables' and tools in 'Contents/Library/LaunchServices' don't match") - + # Check that all the requirements match. - - # This is an interesting policy choice. Technically the tool just needs to match - # the requirement listed in SMPrivilegedExecutables, and we can check that by + + # This is an interesting policy choice. Technically the tool just needs to match + # the requirement listed in SMPrivilegedExecutables, and we can check that by # putting the requirement into tmp.req and then running # # $ codesign -v -R tmp.req /path/to/tool # - # However, for a Developer ID signed tool we really want to have the SMPrivilegedExecutables - # entry contain the tool's designated requirement because Xcode has built a - # more complex DR that does lots of useful and important checks. So, as a matter + # However, for a Developer ID signed tool we really want to have the SMPrivilegedExecutables + # entry contain the tool's designated requirement because Xcode has built a + # more complex DR that does lots of useful and important checks. So, as a matter # of policy we require that the value in SMPrivilegedExecutables match the tool's DR. - + for toolName in infoToolDict: if infoToolDict[toolName] != toolNameToReqMap[toolName]: raise CheckException("tool designated requirement (%s) doesn't match entry in 'SMPrivilegedExecutables' (%s)" % (toolNameToReqMap[toolName], infoToolDict[toolName])) @@ -252,29 +273,29 @@ def checkStep3(appPath, toolPathList): """Checks the "Info.plist" embedded in each helper tool.""" # First get the app's designated requirement. - + appReq = readDesignatedRequirement(appPath, "app") - # Then check that the tool's SMAuthorizedClients value matches it. - + # Then check that the tool's SMAuthorizedClients value matches it. + for toolPath in toolPathList: info = readPlistFromToolSection(toolPath, "__TEXT", "__info_plist") - if not info.has_key("CFBundleInfoDictionaryVersion") or info["CFBundleInfoDictionaryVersion"] != "6.0": + if "CFBundleInfoDictionaryVersion" not in info or info["CFBundleInfoDictionaryVersion"] != "6.0": raise CheckException("'CFBundleInfoDictionaryVersion' in tool __TEXT / __info_plist section must be '6.0'", toolPath) - if not info.has_key("CFBundleIdentifier") or info["CFBundleIdentifier"] != os.path.basename(toolPath): + if "CFBundleIdentifier" not in info or info["CFBundleIdentifier"] != os.path.basename(toolPath): raise CheckException("'CFBundleIdentifier' in tool __TEXT / __info_plist section must match tool name", toolPath) - if not info.has_key("SMAuthorizedClients"): + if "SMAuthorizedClients" not in info: raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section not found", toolPath) infoClientList = info["SMAuthorizedClients"] if not isinstance(infoClientList, list): raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section must be an array", toolPath) if len(infoClientList) != 1: raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section must have one entry", toolPath) - - # Again, as a matter of policy we require that the SMAuthorizedClients entry must + + # Again, as a matter of policy we require that the SMAuthorizedClients entry must # match exactly the designated requirement of the app. if infoClientList[0] != appReq: @@ -286,22 +307,22 @@ def checkStep4(appPath, toolPathList): for toolPath in toolPathList: launchd = readPlistFromToolSection(toolPath, "__TEXT", "__launchd_plist") - if not launchd.has_key("Label") or launchd["Label"] != os.path.basename(toolPath): + if "Label" not in launchd or launchd["Label"] != os.path.basename(toolPath): raise CheckException("'Label' in tool __TEXT / __launchd_plist section must match tool name", toolPath) - # We don't need to check that the label matches the bundle identifier because - # we know it matches the tool name and step 4 checks that the tool name matches + # We don't need to check that the label matches the bundle identifier because + # we know it matches the tool name and step 4 checks that the tool name matches # the bundle identifier. def checkStep5(appPath): """There's nothing to do here; we effectively checked for this is steps 1 and 2.""" pass - + def check(appPath): """Checks the SMJobBless setup of the specified app.""" # Each of the following steps matches a bullet point in the SMJobBless header doc. - + toolPathList = checkStep1(appPath) checkStep2(appPath, toolPathList) @@ -314,7 +335,7 @@ def check(appPath): def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths): """ - Reads information from the built app and uses it to set the SMJobBless setup + Reads information from the built app and uses it to set the SMJobBless setup in the specified app and tool Info.plist source files. """ @@ -328,20 +349,16 @@ def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths): raise CheckException("app 'Info.plist' not found", toolInfoPlistPath) # Get the designated requirement for the app and each of the tools. - + appReq = readDesignatedRequirement(appPath, "app") toolDirPath = os.path.join(appPath, "Contents", "Library", "LaunchServices") if not os.path.isdir(toolDirPath): raise CheckException("tool directory not found", toolDirPath) - + toolNameToReqMap = {} - print os.listdir(toolDirPath) for toolName in os.listdir(toolDirPath): req = readDesignatedRequirement(os.path.join(toolDirPath, toolName), "tool") - print '-----' - print toolName - print '-----' toolNameToReqMap[toolName] = req if len(toolNameToReqMap) > len(toolInfoPlistPaths): @@ -350,58 +367,60 @@ def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths): raise CheckException("tool directory has fewer tools (%d) than you've supplied tool 'Info.plist' paths (%d)" % (len(toolNameToReqMap), len(toolInfoPlistPaths)), toolDirPath) # Build the new value for SMPrivilegedExecutables. - + appToolDict = {} toolInfoPlistPathToToolInfoMap = {} for toolInfoPlistPath in toolInfoPlistPaths: toolInfo = readInfoPlistFromPath(toolInfoPlistPath) toolInfoPlistPathToToolInfoMap[toolInfoPlistPath] = toolInfo - if not toolInfo.has_key("CFBundleIdentifier"): + if "CFBundleIdentifier" not in toolInfo: raise CheckException("'CFBundleIdentifier' not found", toolInfoPlistPath) bundleID = toolInfo["CFBundleIdentifier"] - if not isinstance(bundleID, basestring): + if not isinstance(bundleID, str): raise CheckException("'CFBundleIdentifier' must be a string", toolInfoPlistPath) appToolDict[bundleID] = toolNameToReqMap[bundleID] # Set the SMPrivilegedExecutables value in the app "Info.plist". appInfo = readInfoPlistFromPath(appInfoPlistPath) - needsUpdate = not appInfo.has_key("SMPrivilegedExecutables") + needsUpdate = "SMPrivilegedExecutables" not in appInfo if not needsUpdate: oldAppToolDict = appInfo["SMPrivilegedExecutables"] if not isinstance(oldAppToolDict, dict): raise CheckException("'SMPrivilegedExecutables' must be a dictionary", appInfoPlistPath) - appToolDictSorted = sorted(appToolDict.iteritems(), key=operator.itemgetter(0)) - oldAppToolDictSorted = sorted(oldAppToolDict.iteritems(), key=operator.itemgetter(0)) + appToolDictSorted = sorted(appToolDict.items(), key=operator.itemgetter(0)) + oldAppToolDictSorted = sorted(oldAppToolDict.items(), key=operator.itemgetter(0)) needsUpdate = (appToolDictSorted != oldAppToolDictSorted) - + if needsUpdate: appInfo["SMPrivilegedExecutables"] = appToolDict - plistlib.writePlist(appInfo, appInfoPlistPath) - print >> sys.stdout, "%s: updated" % appInfoPlistPath - + with open(appInfoPlistPath, 'wb') as fp: + plistlib.dump(appInfo, fp) + print ("%s: updated" % appInfoPlistPath, file = sys.stdout) + # Set the SMAuthorizedClients value in each tool's "Info.plist". toolAppListSorted = [ appReq ] # only one element, so obviously sorted (-: for toolInfoPlistPath in toolInfoPlistPaths: toolInfo = toolInfoPlistPathToToolInfoMap[toolInfoPlistPath] - - needsUpdate = not toolInfo.has_key("SMAuthorizedClients") + + needsUpdate = "SMAuthorizedClients" not in toolInfo if not needsUpdate: oldToolAppList = toolInfo["SMAuthorizedClients"] if not isinstance(oldToolAppList, list): raise CheckException("'SMAuthorizedClients' must be an array", toolInfoPlistPath) oldToolAppListSorted = sorted(oldToolAppList) needsUpdate = (toolAppListSorted != oldToolAppListSorted) - + if needsUpdate: toolInfo["SMAuthorizedClients"] = toolAppListSorted - plistlib.writePlist(toolInfo, toolInfoPlistPath) - print >> sys.stdout, "%s: updated" % toolInfoPlistPath + with open(toolInfoPlistPath, 'wb') as f: + plistlib.dump(toolInfo, f) + print("%s: updated" % toolInfoPlistPath, file = sys.stdout) def main(): options, appArgs = getopt.getopt(sys.argv[1:], "d") - + debug = False for opt, val in options: if opt == "-d": @@ -426,16 +445,16 @@ def main(): if __name__ == "__main__": try: main() - except CheckException, e: + except CheckException as e: if e.path is None: - print >> sys.stderr, "%s: %s" % (os.path.basename(sys.argv[0]), e.message) + print("%s: %s" % (os.path.basename(sys.argv[0]), e.message), file = sys.stderr) else: path = e.path if path.endswith("/"): path = path[:-1] - print >> sys.stderr, "%s: %s" % (path, e.message) - sys.exit(1) - except UsageException, e: - print >> sys.stderr, "usage: %s check /path/to/app" % os.path.basename(sys.argv[0]) - print >> sys.stderr, " %s setreq /path/to/app /path/to/app/Info.plist /path/to/tool/Info.plist..." % os.path.basename(sys.argv[0]) + print("%s: %s" % (path, e.message), file = sys.stderr) sys.exit(1) + except UsageException as e: + print("usage: %s check /path/to/app" % os.path.basename(sys.argv[0]), file = sys.stderr) + print(" %s setreq /path/to/app /path/to/app/Info.plist /path/to/tool/Info.plist..." % os.path.basename(sys.argv[0]), file = sys.stderr) + sys.exit(1) \ No newline at end of file From db1b0c995ff2f0cf618ebd171b7372ffceb937d2 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 27 Apr 2023 15:04:42 +0800 Subject: [PATCH 055/122] fix custom image size --- ClashX/Views/StatusItem/NewStatusItemView.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ClashX/Views/StatusItem/NewStatusItemView.swift b/ClashX/Views/StatusItem/NewStatusItemView.swift index ca5f4ea9c..6ca348f9a 100644 --- a/ClashX/Views/StatusItem/NewStatusItemView.swift +++ b/ClashX/Views/StatusItem/NewStatusItemView.swift @@ -56,8 +56,10 @@ class StatusMenuViewModel: ObservableObject { struct SwiftUIView: View { @ObservedObject var viewModel: StatusMenuViewModel var body: some View { - HStack(alignment: .center) { - Image(nsImage: $viewModel.image.wrappedValue).renderingMode(.template) + HStack(alignment:.center) { + Image(nsImage: $viewModel.image.wrappedValue).renderingMode(.template) + .resizable().aspectRatio(contentMode: .fit).frame(width: 16,height: 16) + if $viewModel.showSpeed.wrappedValue { Spacer(minLength: 0) VStack(alignment: .trailing) { From ceefa2cad56c7d424956ec5b46102a91e0d9c540 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Fri, 12 May 2023 16:19:25 +0800 Subject: [PATCH 056/122] misc: update core --- ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index b7c4f15bc..65f02dfa2 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.15.2-0.20230425121611-48b77b28475d + github.com/Dreamacro/clash v1.15.2-0.20230511104224-10dcb7a3ada4 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 398d943a2..39b1280b3 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.15.2-0.20230425121611-48b77b28475d h1:Ccn4mWbJMqp/hCobP2CGqfwUfgLwp5oaYcRAB0nnuT8= -github.com/Dreamacro/clash v1.15.2-0.20230425121611-48b77b28475d/go.mod h1:pnkY25XNTM5Xo/IvZnIXJOf8M5rpjBBfACB+wJD9/mI= +github.com/Dreamacro/clash v1.15.2-0.20230511104224-10dcb7a3ada4 h1:OyJjHe329qz9CNvxmFD/CrXqMl5MMiE/ch2HpgitP3E= +github.com/Dreamacro/clash v1.15.2-0.20230511104224-10dcb7a3ada4/go.mod h1:pnkY25XNTM5Xo/IvZnIXJOf8M5rpjBBfACB+wJD9/mI= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= From 51053055c9d7ff0f5e9d9983944aa45449c62772 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 24 May 2023 11:07:26 +0800 Subject: [PATCH 057/122] feat: ssid suspend --- ClashX.xcodeproj/project.pbxproj | 4 + ClashX/AppDelegate.swift | 15 +++- ClashX/Base.lproj/Main.storyboard | 88 +++++++++++++++++-- ClashX/General/Managers/ConfigManager.swift | 1 + ClashX/General/Managers/Settings.swift | 3 + .../General/Managers/SystemProxyManager.swift | 4 + .../General/Utils/NetworkChangeNotifier.swift | 5 ++ ClashX/General/Utils/SSIDSuspendTool.swift | 59 +++++++++++++ .../GeneralSettingViewController.swift | 13 ++- ClashX/zh-Hans.lproj/Main.strings | 6 ++ 10 files changed, 184 insertions(+), 14 deletions(-) create mode 100644 ClashX/General/Utils/SSIDSuspendTool.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 52fd54731..f04955ed9 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -66,6 +66,7 @@ 49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */; }; 49D176A9235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176A8235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift */; }; 49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */; }; + 49D223392A1DA5F10002FFCB /* SSIDSuspendTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */; }; 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45129AEEC15006487EF /* StatusItemTool.swift */; }; 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */; }; 49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */; }; @@ -199,6 +200,7 @@ 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkChangeNotifier.swift; sourceTree = ""; }; 49D176A8235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupSpeedTestMenuItem.swift; sourceTree = ""; }; 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupMenuItemView.swift; sourceTree = ""; }; + 49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSIDSuspendTool.swift; sourceTree = ""; }; 49D6A45129AEEC15006487EF /* StatusItemTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemTool.swift; sourceTree = ""; }; 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusItemView.swift; sourceTree = ""; }; 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemViewProtocol.swift; sourceTree = ""; }; @@ -294,6 +296,7 @@ 493AEAE2221AE3420016FE98 /* AppVersionUtil.swift */, 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */, 49B445152457CDF000B27E3E /* ClashStatusTool.swift */, + 49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */, ); path = Utils; sourceTree = ""; @@ -775,6 +778,7 @@ 499A486522EEA3FD00F6C675 /* Array+Safe.swift in Sources */, F92D0B24236BC12000575E15 /* SavedProxyModel.swift in Sources */, F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */, + 49D223392A1DA5F10002FFCB /* SSIDSuspendTool.swift in Sources */, F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */, 4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */, 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */, diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index effdda31f..69972fbd9 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -296,6 +296,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func setupData() { + SSIDSuspendTool.shared.setup() ConfigManager.shared .showNetSpeedIndicatorObservable.skip(1) .bind { @@ -305,9 +306,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { Observable .merge([ConfigManager.shared.proxyPortAutoSetObservable, - ConfigManager.shared.isProxySetByOtherVariable.asObservable()]) + ConfigManager.shared.isProxySetByOtherVariable.asObservable(), + ConfigManager.shared.proxyShouldPaused.asObservable()]) + .observe(on: MainScheduler.instance) .map { _ -> NSControl.StateValue in - if ConfigManager.shared.isProxySetByOtherVariable.value && ConfigManager.shared.proxyPortAutoSet { + if (ConfigManager.shared.isProxySetByOtherVariable.value || ConfigManager.shared.proxyShouldPaused.value) && ConfigManager.shared.proxyPortAutoSet { return .mixed } return ConfigManager.shared.proxyPortAutoSet ? .on : .off @@ -429,7 +432,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { .asObservable() .filter { _ in ConfigManager.shared.proxyPortAutoSet } .distinctUntilChanged() - .filter { $0 }.bind { _ in + .filter { $0 } + .filter { _ in !ConfigManager.shared.proxyShouldPaused.value } + .bind { _ in let rawProxy = NetworkChangeNotifier.getRawProxySetting() Logger.log("proxy changed to no clashX setting: \(rawProxy)", level: .warning) NSUserNotificationCenter.default.postProxyChangeByOtherAppNotice() @@ -700,7 +705,9 @@ extension AppDelegate { @IBAction func actionSetSystemProxy(_ sender: Any) { var canSaveProxy = true - if ConfigManager.shared.isProxySetByOtherVariable.value { + if ConfigManager.shared.proxyPortAutoSet && ConfigManager.shared.proxyShouldPaused.value { + ConfigManager.shared.proxyPortAutoSet = false + } else if ConfigManager.shared.isProxySetByOtherVariable.value { // should reset proxy to clashx ConfigManager.shared.isProxySetByOtherVariable.accept(false) ConfigManager.shared.proxyPortAutoSet = true diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index e4284e6dc..557290bd1 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -39,10 +39,10 @@ - + - + @@ -93,7 +93,7 @@ - + @@ -204,6 +204,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -288,16 +354,19 @@ + + + @@ -315,6 +384,7 @@ + @@ -1000,7 +1070,7 @@ - + @@ -1039,7 +1109,7 @@ - + @@ -1078,7 +1148,7 @@ - + @@ -1235,7 +1305,7 @@ - + @@ -1280,7 +1350,7 @@ - + @@ -1322,7 +1392,7 @@ - + diff --git a/ClashX/General/Managers/ConfigManager.swift b/ClashX/General/Managers/ConfigManager.swift index c07da53f2..bf972c3ab 100644 --- a/ClashX/General/Managers/ConfigManager.swift +++ b/ClashX/General/Managers/ConfigManager.swift @@ -81,6 +81,7 @@ class ConfigManager { let proxyPortAutoSetObservable = UserDefaults.standard.rx.observe(Bool.self, "proxyPortAutoSet").map({ $0 ?? false }) var isProxySetByOtherVariable = BehaviorRelay(value: false) + var proxyShouldPaused = BehaviorRelay(value: false) var showNetSpeedIndicator: Bool { get { diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index c3fd859b2..998754ae0 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -46,5 +46,8 @@ enum Settings { @UserDefault("apiPortAllowLan", defaultValue: false) static var apiPortAllowLan: Bool + + @UserDefault("disableSSIDList", defaultValue: []) + static var disableSSIDList:[String] } diff --git a/ClashX/General/Managers/SystemProxyManager.swift b/ClashX/General/Managers/SystemProxyManager.swift index 40a3ee9c2..ea476ca7c 100644 --- a/ClashX/General/Managers/SystemProxyManager.swift +++ b/ClashX/General/Managers/SystemProxyManager.swift @@ -56,6 +56,10 @@ class SystemProxyManager: NSObject { Logger.log("enableProxy fail: \(port) \(socksPort)", level: .error) return } + if SSIDSuspendTool.shared.shouldSuspend() { + Logger.log("not enableProxy due to ssid in disabled list", level: .info) + return + } Logger.log("enableProxy", level: .debug) helper?.enableProxy(withPort: Int32(port), socksPort: Int32(socksPort), diff --git a/ClashX/General/Utils/NetworkChangeNotifier.swift b/ClashX/General/Utils/NetworkChangeNotifier.swift index 6f1e5e787..fc82ad55a 100644 --- a/ClashX/General/Utils/NetworkChangeNotifier.swift +++ b/ClashX/General/Utils/NetworkChangeNotifier.swift @@ -8,6 +8,7 @@ import Cocoa import SystemConfiguration +import CoreWLAN class NetworkChangeNotifier { static func start() { @@ -179,4 +180,8 @@ class NetworkChangeNotifier { } return allowIPV6 ? ipv6 : nil } + + static func getCurrentSSID() -> String? { + return CWWiFiClient.shared().interface()?.ssid() + } } diff --git a/ClashX/General/Utils/SSIDSuspendTool.swift b/ClashX/General/Utils/SSIDSuspendTool.swift new file mode 100644 index 000000000..5f26aa3a5 --- /dev/null +++ b/ClashX/General/Utils/SSIDSuspendTool.swift @@ -0,0 +1,59 @@ +// +// SSIDSuspendTool.swift +// ClashX Pro +// +// Created by yicheng on 2023/5/24. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation +import RxSwift +import RxCocoa + +class SSIDSuspendTool { + static let shared = SSIDSuspendTool() + var disposeBag = DisposeBag() + func setup() { + NotificationCenter + .default + .rx + .notification(.systemNetworkStatusDidChange) + .observe(on: MainScheduler.instance) + .delay(.seconds(2), scheduler: MainScheduler.instance) + .bind { [weak self] _ in + self?.update() + }.disposed(by: disposeBag) + + + ConfigManager.shared + .proxyShouldPaused + .asObservable() + .distinctUntilChanged() + .filter { _ in ConfigManager.shared.proxyPortAutoSet } + .bind { pause in + if pause { + SystemProxyManager.shared.disableProxy() + } else { + SystemProxyManager.shared.enableProxy() + } + }.disposed(by: disposeBag) + + update() + } + + func update() { + if shouldSuspend() { + ConfigManager.shared.proxyShouldPaused.accept(true) + } else { + ConfigManager.shared.proxyShouldPaused.accept(false) + } + } + + func shouldSuspend() -> Bool { + if let currentSSID = NetworkChangeNotifier.getCurrentSSID() { + return Settings.disableSSIDList.contains(currentSSID) + } else { + return false + } + } +} diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 70b38d4a2..803ffdfb3 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -19,7 +19,8 @@ class GeneralSettingViewController: NSViewController { @IBOutlet weak var allowApiLanUsageSwitcher: NSButton! @IBOutlet weak var proxyPortTextField: NSTextField! @IBOutlet weak var apiPortTextField: NSTextField! - + @IBOutlet var ssidSuspendTextField: NSTextView! + var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() @@ -31,6 +32,16 @@ class GeneralSettingViewController: NSViewController { Settings.proxyIgnoreList = arr }.disposed(by: disposeBag) + + ssidSuspendTextField.string = Settings.disableSSIDList.joined(separator: ",") + ssidSuspendTextField.rx + .string.debounce(.milliseconds(500), scheduler: MainScheduler.instance) + .map { $0.components(separatedBy: ",").filter {!$0.isEmpty} } + .subscribe { arr in + Settings.disableSSIDList = arr + SSIDSuspendTool.shared.update() + }.disposed(by: disposeBag) + LaunchAtLogin.shared.isEnableVirable .map { $0 ? .on : .off } .bind(to: launchAtLoginButton.rx.state) diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 3f74a245e..9803906c8 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -237,3 +237,9 @@ /* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ "Gnh-m8-PAz.title" = "Box"; + +/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ +"F1s-SF-dqX.title" = "在特定 WiFi SSID 下自动暂停"; + +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ +"xnL-ma-vFo.title" = "使用英文逗号(,)分隔"; From 1c7bebae71963b719344c67008022d4b13c632e8 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 25 May 2023 11:47:17 +0800 Subject: [PATCH 058/122] feat: add switcher for swiftui menu bar --- ClashX.xcodeproj/project.pbxproj | 4 ++ ClashX/AppDelegate.swift | 2 +- ClashX/Base.lproj/Main.storyboard | 72 +++++++++++++++++-- ClashX/General/Managers/Settings.swift | 3 + .../Settings/DebugSettingViewController.swift | 22 ++++++ .../GeneralSettingViewController.swift | 2 +- ClashX/zh-Hans.lproj/Main.strings | 12 ++++ 7 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 ClashX/ViewControllers/Settings/DebugSettingViewController.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index f04955ed9..c34d32884 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 4913C82321157D0200F6B87C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913C82221157D0200F6B87C /* Notification.swift */; }; 491E6203258A424D00313AEF /* CommonUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 491E61FD258A424500313AEF /* CommonUtils.m */; }; 49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */; }; + 49281C802A1F01FA00F60935 /* DebugSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49281C7F2A1F01FA00F60935 /* DebugSettingViewController.swift */; }; 4929F610258CD22E00A435F6 /* menu_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4929F60F258CD22E00A435F6 /* menu_icon@2x.png */; }; 4929F677258CD89B00A435F6 /* Country.mmdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4929F676258CD89B00A435F6 /* Country.mmdb.gz */; }; 4929F67F258CE04700A435F6 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4929F67E258CE04700A435F6 /* Settings.swift */; }; @@ -138,6 +139,7 @@ 491E61FC258A424500313AEF /* CommonUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = ""; }; 491E61FD258A424500313AEF /* CommonUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUtils.m; sourceTree = ""; }; 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigUpdateIntervalSettingView.swift; sourceTree = ""; }; + 49281C7F2A1F01FA00F60935 /* DebugSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugSettingViewController.swift; sourceTree = ""; }; 4929F60F258CD22E00A435F6 /* menu_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icon@2x.png"; sourceTree = ""; }; 4929F676258CD89B00A435F6 /* Country.mmdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = Country.mmdb.gz; sourceTree = ""; }; 4929F67E258CE04700A435F6 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; @@ -414,6 +416,7 @@ children = ( 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */, 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */, + 49281C7F2A1F01FA00F60935 /* DebugSettingViewController.swift */, ); path = Settings; sourceTree = ""; @@ -752,6 +755,7 @@ 495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */, F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */, 495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */, + 49281C802A1F01FA00F60935 /* DebugSettingViewController.swift in Sources */, 4929F67F258CE04700A435F6 /* Settings.swift in Sources */, 49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */, 493AEAE3221AE3420016FE98 /* AppVersionUtil.swift in Sources */, diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 69972fbd9..192234e17 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -76,7 +76,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { ProcessInfo.processInfo.disableSuddenTermination() // setup menu item first statusItem = NSStatusBar.system.statusItem(withLength: statusItemLengthWithSpeed) - if #available(macOS 11, *), let button = statusItem.button { + if #available(macOS 13, *), Settings.useSwiftUiMenuBar, let button = statusItem.button { statusItemView = NewStatusMenuView.create(on: button) } else { statusItemView = StatusItemView.create(statusItem: statusItem) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 557290bd1..3fe73f44a 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -12,6 +12,7 @@ + @@ -24,6 +25,7 @@ + @@ -1070,7 +1072,7 @@ - + @@ -1109,7 +1111,7 @@ - + @@ -1148,7 +1150,7 @@ - + @@ -1305,7 +1307,7 @@ - + @@ -1350,7 +1352,7 @@ - + @@ -1392,7 +1394,7 @@ - + @@ -1512,9 +1514,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index 998754ae0..e4fff6353 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -49,5 +49,8 @@ enum Settings { @UserDefault("disableSSIDList", defaultValue: []) static var disableSSIDList:[String] + + @UserDefault("useSwiftUiMenuBar", defaultValue: true) + static var useSwiftUiMenuBar: Bool } diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift new file mode 100644 index 000000000..2b44cea09 --- /dev/null +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -0,0 +1,22 @@ +// +// DebugSettingViewController.swift +// ClashX Pro +// +// Created by yicheng on 2023/5/25. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import RxSwift + +class DebugSettingViewController: NSViewController { + @IBOutlet weak var swiftuiMenuBarButton: NSButton! + var disposeBag = DisposeBag() + override func viewDidLoad() { + super.viewDidLoad() + swiftuiMenuBarButton.state = Settings.useSwiftUiMenuBar ? .on : .off + swiftuiMenuBarButton.rx.state.bind { state in + Settings.useSwiftUiMenuBar = state == .on + }.disposed(by: disposeBag) + } +} diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 803ffdfb3..d323893db 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -87,7 +87,7 @@ class GeneralSettingViewController: NSViewController { .bind { Settings.apiPort = $0 }.disposed(by: disposeBag) - + allowApiLanUsageSwitcher.state = Settings.apiPortAllowLan ? .on : .off allowApiLanUsageSwitcher.rx.state.bind { state in Settings.apiPortAllowLan = state == .on }.disposed(by: disposeBag) diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 9803906c8..8ac86ecb0 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -243,3 +243,15 @@ /* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ "xnL-ma-vFo.title" = "使用英文逗号(,)分隔"; + +/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ +"3Om-yT-A83.label" = "调试"; + +/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ +"NLT-FZ-48V.title" = "调试设置"; + +/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ +"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染"; + +/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ +"kdV-Em-qBi.title" = "调试"; From d2ef7aaf7d35850ea77db47d32a7f2948eafaa51 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 25 May 2023 14:29:13 +0800 Subject: [PATCH 059/122] chore: update version --- ClashX/goClash/go.mod | 22 ++++++++++----------- ClashX/goClash/go.sum | 46 +++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 65f02dfa2..8308965cb 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.15.2-0.20230511104224-10dcb7a3ada4 + github.com/Dreamacro/clash v1.16.1-0.20230524014443-ccd6d321cddd github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) @@ -17,26 +17,26 @@ require ( github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 // indirect + github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb // indirect github.com/josharian/native v1.1.0 // indirect - github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 // indirect - github.com/mdlayher/socket v0.4.0 // indirect - github.com/miekg/dns v1.1.53 // indirect + github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/socket v0.4.1 // indirect + github.com/miekg/dns v1.1.54 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/samber/lo v1.38.1 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/sirupsen/logrus v1.9.2 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 // indirect github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect go.etcd.io/bbolt v1.3.7 // indirect - go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.8.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/tools v0.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 39b1280b3..f820528f2 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.15.2-0.20230511104224-10dcb7a3ada4 h1:OyJjHe329qz9CNvxmFD/CrXqMl5MMiE/ch2HpgitP3E= -github.com/Dreamacro/clash v1.15.2-0.20230511104224-10dcb7a3ada4/go.mod h1:pnkY25XNTM5Xo/IvZnIXJOf8M5rpjBBfACB+wJD9/mI= +github.com/Dreamacro/clash v1.16.1-0.20230524014443-ccd6d321cddd h1:PFPwpoyh7Jzdx/a78X9dJbx38yQXrzOJOGl8L4G+AdQ= +github.com/Dreamacro/clash v1.16.1-0.20230524014443-ccd6d321cddd/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -19,17 +19,17 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E= -github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= +github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q= +github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4= -github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ= -github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= -github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= -github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= +github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= @@ -42,11 +42,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 h1:F9xjJm4IH8VjcqG4ujciOF+GIM4mjPkHhWLLzOghPtM= @@ -55,24 +55,24 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3C github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= From b13a703a8a9b8d7e04a7345540468b5f66b5a9f0 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 25 May 2023 17:04:49 +0800 Subject: [PATCH 060/122] misc: update deps --- Podfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index 7d8bdb667..d3eb80e8c 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - Alamofire (5.6.4) + - Alamofire (5.7.1) - AppCenter/Analytics (5.0.2): - AppCenter/Core - AppCenter/Core (5.0.2) @@ -54,7 +54,7 @@ SPEC REPOS: - WebViewJavascriptBridge SPEC CHECKSUMS: - Alamofire: 4e95d97098eacb88856099c4fc79b526a299e48c + Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88 AppCenter: 355ba776b273d30147c3ac385c558bec60a7d4b1 CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 @@ -70,4 +70,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 9b9952fcf44ecd26932a2cf4bb79d03f8850d67b -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 From 2dc1003f3602aa4123ed7ddba7c825aa02d82f56 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Fri, 26 May 2023 15:45:33 +0800 Subject: [PATCH 061/122] feat: support global shortcut --- ClashX.xcodeproj/project.pbxproj | 37 +++++- .../xcshareddata/swiftpm/Package.resolved | 14 +++ ClashX/AppDelegate.swift | 11 +- ClashX/Base.lproj/Main.storyboard | 94 +++++++++++++-- ClashX/Basic/NSView+Layout.swift | 35 ++++++ .../en.lproj/Localizable.strings | 30 +++++ .../zh-Hans.lproj/Localizable.strings | 30 +++++ .../GlobalShortCutViewController.swift | 112 ++++++++++++++++++ ClashX/zh-Hans.lproj/Main.strings | 15 +++ 9 files changed, 367 insertions(+), 11 deletions(-) create mode 100644 ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 ClashX/Basic/NSView+Layout.swift create mode 100644 ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index c34d32884..8e784061e 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -3,10 +3,13 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ + 4905A2C52A2058B000AEDA2E /* GlobalShortCutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4905A2C42A2058B000AEDA2E /* GlobalShortCutViewController.swift */; }; + 4905A2C82A2058D400AEDA2E /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */; }; + 4905A2CA2A20841B00AEDA2E /* NSView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */; }; 4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4908087A29F8F3FF007A4944 /* libresolv.tbd */; }; 4913C82321157D0200F6B87C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913C82221157D0200F6B87C /* Notification.swift */; }; 491E6203258A424D00313AEF /* CommonUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 491E61FD258A424500313AEF /* CommonUtils.m */; }; @@ -134,6 +137,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4905A2C42A2058B000AEDA2E /* GlobalShortCutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalShortCutViewController.swift; sourceTree = ""; }; + 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSView+Layout.swift"; sourceTree = ""; }; 4908087A29F8F3FF007A4944 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; }; 4913C82221157D0200F6B87C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 491E61FC258A424500313AEF /* CommonUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = ""; }; @@ -250,6 +255,7 @@ buildActionMask = 2147483647; files = ( F9C180A3243C6590005EE8C4 /* goClash.a in Frameworks */, + 4905A2C82A2058D400AEDA2E /* KeyboardShortcuts in Frameworks */, 9E0E2F1481CB327D3753969D /* libPods-ClashX.a in Frameworks */, 4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */, ); @@ -417,6 +423,7 @@ 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */, 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */, 49281C7F2A1F01FA00F60935 /* DebugSettingViewController.swift */, + 4905A2C42A2058B000AEDA2E /* GlobalShortCutViewController.swift */, ); path = Settings; sourceTree = ""; @@ -427,6 +434,7 @@ 495A44D220D267D000888A0A /* LaunchAtLogin.swift */, 4966E9E5211824F300A391FB /* NSImage+extension.swift */, 496BDEDF21196F1E00C5207F /* Logger.swift */, + 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */, 49B10869216A356D0064FFCE /* String+Extension.swift */, 49ABB748236B0F9E00535CD7 /* UnsafePointer+bridge.swift */, ); @@ -575,6 +583,9 @@ F935B2EC2307B7CD009E4D33 /* PBXTargetDependency */, ); name = ClashX; + packageProductDependencies = ( + 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */, + ); productName = ClashX; productReference = 49CF3B1D20CD7463001EBF94 /* ClashX.app */; productType = "com.apple.product-type.application"; @@ -633,6 +644,9 @@ "zh-Hans", ); mainGroup = 49CF3B1420CD7463001EBF94; + packageReferences = ( + 4905A2C62A2058D400AEDA2E /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */, + ); productRefGroup = 49CF3B1E20CD7463001EBF94 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -761,8 +775,10 @@ 493AEAE3221AE3420016FE98 /* AppVersionUtil.swift in Sources */, 49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */, 496BDEE021196F1E00C5207F /* Logger.swift in Sources */, + 4905A2CA2A20841B00AEDA2E /* NSView+Layout.swift in Sources */, 49722FEF211F338B00650A41 /* FileEvent.swift in Sources */, 49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */, + 4905A2C52A2058B000AEDA2E /* GlobalShortCutViewController.swift in Sources */, 4913C82321157D0200F6B87C /* Notification.swift in Sources */, 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */, 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */, @@ -1161,6 +1177,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 4905A2C62A2058D400AEDA2E /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sindresorhus/KeyboardShortcuts.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */ = { + isa = XCSwiftPackageProductDependency; + package = 4905A2C62A2058D400AEDA2E /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */; + productName = KeyboardShortcuts; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 49CF3B1520CD7463001EBF94 /* Project object */; } diff --git a/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..3aa64d11b --- /dev/null +++ b/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "keyboardshortcuts", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/KeyboardShortcuts.git", + "state" : { + "revision" : "018e445b340dac78dfc2a4c97c45d9571507c3b5", + "version" : "1.11.0" + } + } + ], + "version" : 2 +} diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 192234e17..2a57b8c6a 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -153,6 +153,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { setupNetworkNotifier() registCrashLogger() + KeyboardShortCutManager.setup() } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { @@ -654,7 +655,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { // MARK: Main actions extension AppDelegate { - @IBAction func actionDashboard(_ sender: NSMenuItem) { + @IBAction func actionDashboard(_ sender: NSMenuItem?) { if dashboardWindowController == nil { dashboardWindowController = ClashWebViewWindowController.create() dashboardWindowController?.onWindowClose = { @@ -690,6 +691,10 @@ extension AppDelegate { default: return } + switchProxyMode(mode: mode) + } + + func switchProxyMode(mode: ClashProxyMode) { let config = ConfigManager.shared.currentConfig?.copy() config?.mode = mode ApiRequest.updateOutBoundMode(mode: mode) { _ in @@ -703,7 +708,7 @@ extension AppDelegate { ConfigManager.shared.showNetSpeedIndicator = !(sender.state == .on) } - @IBAction func actionSetSystemProxy(_ sender: Any) { + @IBAction func actionSetSystemProxy(_ sender: Any?) { var canSaveProxy = true if ConfigManager.shared.proxyPortAutoSet && ConfigManager.shared.proxyShouldPaused.value { ConfigManager.shared.proxyPortAutoSet = false @@ -792,7 +797,7 @@ extension AppDelegate: ApiRequestStreamDelegate { // MARK: Help actions extension AppDelegate { - @IBAction func actionShowLog(_ sender: Any) { + @IBAction func actionShowLog(_ sender: Any?) { NSWorkspace.shared.openFile(Logger.shared.logFilePath()) } } diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 3fe73f44a..0f50afd66 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -12,6 +12,7 @@ + @@ -25,6 +26,7 @@ + @@ -297,15 +299,15 @@ - + - + - + @@ -319,7 +321,7 @@ - + @@ -1517,7 +1519,7 @@ - + @@ -1571,10 +1573,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ClashX/Basic/NSView+Layout.swift b/ClashX/Basic/NSView+Layout.swift new file mode 100644 index 000000000..7f2728704 --- /dev/null +++ b/ClashX/Basic/NSView+Layout.swift @@ -0,0 +1,35 @@ +// +// UIView+Layout.swift +// BilibiliLive +// +// Created by Etan Chen on 2021/4/4. +// + +import AppKit + +extension NSView { + @discardableResult + func makeConstraints(_ block: (NSView) -> [NSLayoutConstraint]) -> Self { + translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate(block(self)) + return self + } + + @discardableResult + func makeConstraintsToBindToSuperview(_ inset: NSEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: 0)) -> Self { + return makeConstraints { [ + $0.leftAnchor.constraint(equalTo: $0.superview!.leftAnchor, constant: inset.left), + $0.rightAnchor.constraint(equalTo: $0.superview!.rightAnchor, constant: -inset.right), + $0.topAnchor.constraint(equalTo: $0.superview!.topAnchor, constant: inset.top), + $0.bottomAnchor.constraint(equalTo: $0.superview!.bottomAnchor, constant: -inset.bottom), + ] } + } + + @discardableResult + func makeConstraintsBindToCenterOfSuperview() -> Self { + return makeConstraints { [ + $0.centerXAnchor.constraint(equalTo: $0.superview!.centerXAnchor), + $0.centerYAnchor.constraint(equalTo: $0.superview!.centerYAnchor), + ] } + } +} diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 5fb17e410..b1112978f 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -43,6 +43,12 @@ /* No comment provided by engineer. */ "Config loading Fail!" = "Config loading Fail!"; +/* No comment provided by engineer. */ +"Copy Shell Command" = "Copy Shell Command"; + +/* No comment provided by engineer. */ +"Copy Shell Command (External)" = "Copy Shell Command (External)"; + /* No comment provided by engineer. */ "Custom your GEOIP MMDB download address." = "Custom your GEOIP MMDB download address."; @@ -52,6 +58,9 @@ /* No comment provided by engineer. */ "Direct" = "Direct"; +/* No comment provided by engineer. */ +"Direct Mode" = "Direct Mode"; + /* No comment provided by engineer. */ "Disable Restore Proxy Setting" = "Disable Restore Proxy Setting"; @@ -73,6 +82,9 @@ /* No comment provided by engineer. */ "Global" = "Global"; +/* No comment provided by engineer. */ +"Global Mode" = "Global Mode"; + /* No comment provided by engineer. */ "hours" = "hours"; @@ -105,6 +117,15 @@ /* No comment provided by engineer. */ "OK" = "OK"; +/* No comment provided by engineer. */ +"Open Dashboard" = "Open Dashboard"; + +/* No comment provided by engineer. */ +"Open Log" = "Open Log"; + +/* No comment provided by engineer. */ +"Open Menu" = "Open Menu"; + /* No comment provided by engineer. */ "Open System Login Item Setting" = "Open System Login Item Setting"; @@ -150,9 +171,15 @@ /* No comment provided by engineer. */ "Rule" = "Rule"; +/* No comment provided by engineer. */ +"Rule Mode" = "Rule Mode"; + /* No comment provided by engineer. */ "Script" = "Script"; +/* No comment provided by engineer. */ +"Script Mode" = "Script Mode"; + /* No comment provided by engineer. */ "Should be a least 1 hour" = "Should be a least 1 hour"; @@ -171,6 +198,9 @@ /* No comment provided by engineer. */ "Success!" = "Success!"; +/* No comment provided by engineer. */ +"System Proxy" = "System Proxy"; + /* No comment provided by engineer. */ "System Proxy Changed" = "System Proxy Changed"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 8ad8b19fa..cc21ccb1a 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -43,6 +43,12 @@ /* No comment provided by engineer. */ "Config loading Fail!" = "配置文件加载失败"; +/* No comment provided by engineer. */ +"Copy Shell Command" = "复制终端代理命令"; + +/* No comment provided by engineer. */ +"Copy Shell Command (External)" = "复制终端代理命令(外部IP)"; + /* No comment provided by engineer. */ "Custom your GEOIP MMDB download address." = "自定义下载地址"; @@ -52,6 +58,9 @@ /* No comment provided by engineer. */ "Direct" = "直连"; +/* No comment provided by engineer. */ +"Direct Mode" = "直接连接"; + /* No comment provided by engineer. */ "Disable Restore Proxy Setting" = "关闭自动还原之前代理"; @@ -73,6 +82,9 @@ /* No comment provided by engineer. */ "Global" = "全局"; +/* No comment provided by engineer. */ +"Global Mode" = "全局连接"; + /* No comment provided by engineer. */ "hours" = "小时"; @@ -105,6 +117,15 @@ /* No comment provided by engineer. */ "OK" = "确定"; +/* No comment provided by engineer. */ +"Open Dashboard" = "打开控制台"; + +/* No comment provided by engineer. */ +"Open Log" = "打开日志"; + +/* No comment provided by engineer. */ +"Open Menu" = "打开菜单"; + /* No comment provided by engineer. */ "Open System Login Item Setting" = "打开系统登录项设置"; @@ -150,9 +171,15 @@ /* No comment provided by engineer. */ "Rule" = "规则"; +/* No comment provided by engineer. */ +"Rule Mode" = "规则判断"; + /* No comment provided by engineer. */ "Script" = "脚本"; +/* No comment provided by engineer. */ +"Script Mode" = "脚本模式"; + /* No comment provided by engineer. */ "Should be a least 1 hour" = "至少需要1小时间隔"; @@ -171,6 +198,9 @@ /* No comment provided by engineer. */ "Success!" = "成功!"; +/* No comment provided by engineer. */ +"System Proxy" = "设置系统代理"; + /* No comment provided by engineer. */ "System Proxy Changed" = "系统代理设置已修改"; diff --git a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift new file mode 100644 index 000000000..303adf2fa --- /dev/null +++ b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift @@ -0,0 +1,112 @@ +// +// GlobalShortCutViewController.swift +// ClashX Pro +// +// Created by yicheng on 2023/5/26. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import KeyboardShortcuts + +extension KeyboardShortcuts.Name { + static let toggleSystemProxyMode = Self("shortCut.toggleSystemProxyMode") + static let copyShellCommand = Self("shortCut.copyShellCommand") + static let copyExternalShellCommand = Self("shortCut.copyExternalShellCommand") + + static let modeDirect = Self("shortCut.modeDirect") + static let modeRule = Self("shortCut.modeRule") + static let modeGlobal = Self("shortCut.modeGlobal") + + static let log = Self("shortCut.log") + static let dashboard = Self("shortCut.dashboard") + static let openMenu = Self("shortCut.openMenu") + +} + + +enum KeyboardShortCutManager { + static func setup() { + KeyboardShortcuts.onKeyUp(for: .toggleSystemProxyMode) { + AppDelegate.shared.actionSetSystemProxy(nil) + } + + KeyboardShortcuts.onKeyUp(for: .copyShellCommand) { + AppDelegate.shared.actionCopyExportCommand(AppDelegate.shared.copyExportCommandMenuItem) + } + + KeyboardShortcuts.onKeyUp(for: .copyExternalShellCommand) { + AppDelegate.shared.actionCopyExportCommand(AppDelegate.shared.copyExportCommandExternalMenuItem) + } + + KeyboardShortcuts.onKeyUp(for: .modeDirect) { + AppDelegate.shared.switchProxyMode(mode: .direct) + } + + KeyboardShortcuts.onKeyUp(for: .modeRule) { + AppDelegate.shared.switchProxyMode(mode: .rule) + } + + KeyboardShortcuts.onKeyUp(for: .modeGlobal) { + AppDelegate.shared.switchProxyMode(mode: .global) + } + + KeyboardShortcuts.onKeyUp(for: .log) { + AppDelegate.shared.actionShowLog(nil) + } + + KeyboardShortcuts.onKeyUp(for: .dashboard) { + AppDelegate.shared.actionDashboard(nil) + } + + KeyboardShortcuts.onKeyUp(for: .openMenu) { + AppDelegate.shared.statusItem.button?.performClick(nil) + } + } +} + +class GlobalShortCutViewController: NSViewController { + + @IBOutlet weak var proxyBox: NSBox! + @IBOutlet weak var modeBoxView: NSView! + @IBOutlet weak var otherBoxView: NSView! + + override func viewDidLoad() { + super.viewDidLoad() + + let systemProxy = KeyboardShortcuts.RecorderCocoa(for: .toggleSystemProxyMode) + let copyShellCommand = KeyboardShortcuts.RecorderCocoa(for: .copyShellCommand) + let copyShellCommandExternal = KeyboardShortcuts.RecorderCocoa(for: .copyExternalShellCommand) + addGridView(in: proxyBox.contentView!, with: [ + [NSTextField(labelWithString: NSLocalizedString("System Proxy", comment: "")),systemProxy], + [NSTextField(labelWithString: NSLocalizedString("Copy Shell Command", comment: "")),copyShellCommand], + [NSTextField(labelWithString: NSLocalizedString("Copy Shell Command (External)", comment: "")),copyShellCommandExternal], + ]) + + + addGridView(in: modeBoxView, with: [ + [NSTextField(labelWithString: NSLocalizedString("Direct Mode", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .modeDirect)], + [NSTextField(labelWithString: NSLocalizedString("Rule Mode", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .modeRule)], + [NSTextField(labelWithString: NSLocalizedString("Global Mode", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .modeGlobal)], + ]) + + addGridView(in: otherBoxView, with: [ + [NSTextField(labelWithString: NSLocalizedString("Open Menu", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .openMenu)], + [NSTextField(labelWithString: NSLocalizedString("Open Log", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .log)], + [NSTextField(labelWithString: NSLocalizedString("Open Dashboard", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .dashboard)], + ]) + } + + + func addGridView(in superView:NSView, with views: [[NSView]]) { + let gridView = NSGridView(views: views) + gridView.rowSpacing = 10 + gridView.rowAlignment = .firstBaseline + superView.addSubview(gridView) + gridView.makeConstraintsToBindToSuperview(NSEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)) + gridView.setContentHuggingPriority(.required, for: .vertical) + gridView.setContentCompressionResistancePriority(.required, for: .vertical) + gridView.xPlacement = .trailing + gridView.column(at: 0).xPlacement = .leading + } +} diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 8ac86ecb0..1ec14447f 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -255,3 +255,18 @@ /* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ "kdV-Em-qBi.title" = "调试"; + +/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ +"1d5-NL-UsJ.title" = "代理模式"; + +/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ +"AMT-oc-r8A.title" = "其他"; + +/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ +"BFE-Qq-B2H.title" = "代理设置"; + +/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ +"wKZ-TE-sf8.title" = "全局快捷键"; + +/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ +"GGx-F2-7kE.title" = "请务必注意处理可能存在的快捷键冲突,全局快捷键将优先于普通快捷键。"; From b55de35e3bf3e20d6631459c5bea64aef38ea352 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 3 Jun 2023 13:38:58 +0800 Subject: [PATCH 062/122] docs: update readme --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 63b4515ae..96915e95a 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,11 @@ A rule based proxy For Mac base on [Clash](https://github.com/Dreamacro/clash). ClashX 旨在提供一个简单轻量化的代理客户端,如果需要更多的定制化,可以考虑使用 [CFW Mac 版](https://github.com/Fndroid/clash_for_windows_pkg/releases) + +## 注意 +- ClashX / ClashX Pro 只是一个代理工具,不提供任何代理服务器。如果服务器不可用或与服务器续费有关的问题,请与您的提供商联系。 +- ClashX / ClashX Pro 目前并没有创建官网。凡是声称是 ClashX / ClashX Pro 官网的一定是骗子。 + ## Features - HTTP/HTTPS and SOCKS protocol @@ -22,8 +27,9 @@ ClashX 旨在提供一个简单轻量化的代理客户端,如果需要更多 You can download from [Release](https://github.com/yichengchen/clashX/releases) page -**Download ClashX Pro With enhanced mode and Native Apple Silicon support at [AppCenter](https://install.appcenter.ms/users/clashx/apps/clashx-pro/distribution_groups/public) for free permanently.** +**Download ClashX Pro With enhanced mode and other clash premium feature at [AppCenter](https://install.appcenter.ms/users/clashx/apps/clashx-pro/distribution_groups/public) for free permanently.** +**在 [AppCenter](https://install.appcenter.ms/users/clashx/apps/clashx-pro/distribution_groups/public) 免费下载ClashX Pro版本,支持增强模式以及更多Clash Premium Core特性。** ## Build - Make sure have python3 and golang installed in your computer. @@ -105,5 +111,5 @@ script: Note:强烈不推荐这么做,这可能导致clashx的很多重要错误提醒无法显示。 ### 全局快捷键 - -- 设置详情点击 [全局快捷键](Shortcuts.md) \ No newline at end of file +- 在菜单栏配置->更多配置中,自定义对应功能的快捷键。(需要1.116.1之后的版本) +- 使用AppleScript设置, 详情点击 [全局快捷键](Shortcuts.md) From 3a5f61b02ca9c50934e7f67b925f10f54f73774b Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Fri, 26 May 2023 16:40:25 +0800 Subject: [PATCH 063/122] feat: add uninstall proxy helper --- ClashX/Base.lproj/Main.storyboard | 41 +++++++++++++++++-- .../PrivilegedHelperManager+Legacy.swift | 8 ++-- .../Settings/DebugSettingViewController.swift | 3 ++ ClashX/zh-Hans.lproj/Main.strings | 8 +++- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 0f50afd66..ef457fb3d 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1521,31 +1521,64 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift index e4576dc0a..55dd25a29 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift @@ -88,10 +88,12 @@ extension PrivilegedHelperManager { Thread.sleep(forTimeInterval: 5) } let script = """ - launchctl remove \(PrivilegedHelperManager.machServiceName) || true - rm -rf /Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist - rm -rf /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName) + /bin/launchctl remove \(PrivilegedHelperManager.machServiceName) || true + /usr/bin/killall -u root -9 \(PrivilegedHelperManager.machServiceName) + /bin/rm -rf /Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist + /bin/rm -rf /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName) """ + runScriptWithRootPermission(script: script) } } diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index 2b44cea09..10e870e73 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -19,4 +19,7 @@ class DebugSettingViewController: NSViewController { Settings.useSwiftUiMenuBar = state == .on }.disposed(by: disposeBag) } + @IBAction func actionUnInstallProxyHelper(_ sender: Any) { + PrivilegedHelperManager.shared.removeInstallHelper() + } } diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 1ec14447f..3fa8a0923 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -251,7 +251,7 @@ "NLT-FZ-48V.title" = "调试设置"; /* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ -"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染"; +"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染 (MacOS 13+)"; /* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ "kdV-Em-qBi.title" = "调试"; @@ -270,3 +270,9 @@ /* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ "GGx-F2-7kE.title" = "请务必注意处理可能存在的快捷键冲突,全局快捷键将优先于普通快捷键。"; + +/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ +"AY0-nP-cGT.title" = "移除助手程序"; + +/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ +"aSG-9A-eeG.title" = "助手程序"; From d910c3413eaf9258e9f25ca3bd7d69b14f1e0f32 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:37:18 +0800 Subject: [PATCH 064/122] feat: add open log folder button --- ClashX/Base.lproj/Main.storyboard | 50 ++++++++++++++++--- ClashX/Basic/Logger.swift | 4 ++ .../Settings/DebugSettingViewController.swift | 3 ++ ClashX/zh-Hans.lproj/Main.strings | 6 +++ 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index ef457fb3d..4a9a75798 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1521,27 +1521,27 @@ - + - + - + - - + + - + @@ -1571,14 +1571,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ClashX/Basic/Logger.swift b/ClashX/Basic/Logger.swift index e99720be8..1c0568b4b 100644 --- a/ClashX/Basic/Logger.swift +++ b/ClashX/Basic/Logger.swift @@ -48,4 +48,8 @@ class Logger { func logFilePath() -> String { return fileLogger.logFileManager.sortedLogFilePaths.first ?? "" } + + func logFolder() -> String { + return fileLogger.logFileManager.logsDirectory + } } diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index 10e870e73..b87e0677c 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -22,4 +22,7 @@ class DebugSettingViewController: NSViewController { @IBAction func actionUnInstallProxyHelper(_ sender: Any) { PrivilegedHelperManager.shared.removeInstallHelper() } + @IBAction func actionOpenLogFolder(_ sender: Any) { + NSWorkspace.shared.openFile(Logger.shared.logFolder()) + } } diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 3fa8a0923..445053165 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -276,3 +276,9 @@ /* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ "aSG-9A-eeG.title" = "助手程序"; + +/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ +"afj-4G-usr.title" = "打开日志文件夹"; + +/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ +"c01-0L-1SQ.title" = "日志"; From 6fb8a1a62781583df83f57184c63c3370abddc2d Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:01:14 +0800 Subject: [PATCH 065/122] misc: update core --- ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 8308965cb..a7b62f852 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.16.1-0.20230524014443-ccd6d321cddd + github.com/Dreamacro/clash v1.16.1-0.20230528061203-289025c6eeb3 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index f820528f2..0bfa2158b 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.16.1-0.20230524014443-ccd6d321cddd h1:PFPwpoyh7Jzdx/a78X9dJbx38yQXrzOJOGl8L4G+AdQ= -github.com/Dreamacro/clash v1.16.1-0.20230524014443-ccd6d321cddd/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= +github.com/Dreamacro/clash v1.16.1-0.20230528061203-289025c6eeb3 h1:Of6EomzsYbxnG88T/LdEhT+FtVex/gakoTtPK8QMUkw= +github.com/Dreamacro/clash v1.16.1-0.20230528061203-289025c6eeb3/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= From 34f9a16acaf9a1c38181d7133244c11908263bae Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:24:24 +0800 Subject: [PATCH 066/122] fix: menu selecting state not update in time in mac os 14 --- ClashX/Views/MenuItemBaseView.swift | 8 +++++++- Podfile | 2 +- Podfile.lock | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ClashX/Views/MenuItemBaseView.swift b/ClashX/Views/MenuItemBaseView.swift index d69e9f1a2..fa8446adf 100644 --- a/ClashX/Views/MenuItemBaseView.swift +++ b/ClashX/Views/MenuItemBaseView.swift @@ -15,7 +15,13 @@ class MenuItemBaseView: NSView { // MARK: Public - var isHighlighted: Bool = false + var isHighlighted: Bool = false { + didSet { + if isHighlighted != oldValue { + setNeedsDisplay() + } + } + } let effectView: NSVisualEffectView = { let effectView = NSVisualEffectView() diff --git a/Podfile b/Podfile index 2b2d376da..a44a467b2 100644 --- a/Podfile +++ b/Podfile @@ -8,7 +8,7 @@ post_install do |installer| config.build_settings['SWIFT_VERSION'] = '5' end end - if config.build_settings['MACOSX_DEPLOYMENT_TARGET'] == '' + if config.build_settings['MACOSX_DEPLOYMENT_TARGET'] == '' || Gem::Version.new(config.build_settings['MACOSX_DEPLOYMENT_TARGET']) < Gem::Version.new("10.13") config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.13' end end diff --git a/Podfile.lock b/Podfile.lock index d3eb80e8c..995bce311 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -68,6 +68,6 @@ SPEC CHECKSUMS: SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 -PODFILE CHECKSUM: 9b9952fcf44ecd26932a2cf4bb79d03f8850d67b +PODFILE CHECKSUM: 7a4b4f95913a2481c21fe728bf31871a3b75fb6c COCOAPODS: 1.12.1 From 2e012da813fff31df027ba9e6a56c9b33dc3ff5e Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 10 Jun 2023 10:40:22 +0800 Subject: [PATCH 067/122] misc: add zh-hant --- ClashX.xcodeproj/project.pbxproj | 5 + .../zh-Hant.lproj/Localizable.strings | 241 +++++++++++++++ ClashX/zh-Hant.lproj/Main.strings | 287 ++++++++++++++++++ 3 files changed, 533 insertions(+) create mode 100644 ClashX/Support Files/zh-Hant.lproj/Localizable.strings create mode 100644 ClashX/zh-Hant.lproj/Main.strings diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 8e784061e..df87504c3 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -165,6 +165,8 @@ 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserNotificationCenter+Extension.swift"; sourceTree = ""; }; 4966E9E5211824F300A391FB /* NSImage+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+extension.swift"; sourceTree = ""; }; 496BDEDF21196F1E00C5207F /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; + 496C16462A3418C80052064A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = ""; }; + 496C16472A3418C80052064A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; 49722FEA211F338B00650A41 /* FileEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileEvent.swift; sourceTree = ""; }; 49722FEB211F338B00650A41 /* EventStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventStream.swift; sourceTree = ""; }; 49722FEC211F338B00650A41 /* Witness.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Witness.swift; sourceTree = ""; }; @@ -642,6 +644,7 @@ en, Base, "zh-Hans", + "zh-Hant", ); mainGroup = 49CF3B1420CD7463001EBF94; packageReferences = ( @@ -859,6 +862,7 @@ children = ( 4981C88C216BAE4A008CC14A /* en */, 4981C88E216BAE4D008CC14A /* zh-Hans */, + 496C16472A3418C80052064A /* zh-Hant */, ); name = Localizable.strings; sourceTree = ""; @@ -868,6 +872,7 @@ children = ( 49CF3B2720CD7465001EBF94 /* Base */, 4981C887216BAB8A008CC14A /* zh-Hans */, + 496C16462A3418C80052064A /* zh-Hant */, ); name = Main.storyboard; sourceTree = ""; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings new file mode 100644 index 000000000..e5715e590 --- /dev/null +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -0,0 +1,241 @@ +/* No comment provided by engineer. */ +"Add a remote config" = "添加託管配置文件"; + +/* No comment provided by engineer. */ +"Add a remote control config" = "添加遠程控制器配置"; + +/* No comment provided by engineer. */ +"Apply and Quit" = "應用並退出"; + +/* No comment provided by engineer. */ +"Auto Close Connection" = "切換代理時中斷連接"; + +/* No comment provided by engineer. */ +"Benchmark" = "延遲測速"; + +/* No comment provided by engineer. */ +"Benchmark Finished!" = "延遲測速完成"; + +/* No comment provided by engineer. */ +"Benchmark has begun, please wait." = "延遲測速開始,請稍候"; + +/* No comment provided by engineer. */ +"Benchmark is processing, please wait." = "延遲測速正在進行中,請稍候。"; + +/* No comment provided by engineer. */ +"Cancel" = "取消"; + +/* No comment provided by engineer. */ +"ClashX fail to create ~/.config/clash folder. Please check privileges or manually create folder and restart ClashX." = "ClashX 創建 ~/.config/clash 文件夾失敗,請檢查權限設定或者手動創建文件夾後重啟 ClashX"; + +/* No comment provided by engineer. */ +"ClashX needs to install/update a helper tool with administrator privileges, otherwise ClashX won't be able to configure system proxy." = "ClashX 需要使用管理員權限安裝/更新一個幫助程序,否則 ClashX 將無法設定系統代理。"; + +/* No comment provided by engineer. */ +"ClashX Start Error!" = "ClashX 啟動出錯"; + +/* No comment provided by engineer. */ +"ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app" = "ClashX需要通過後台Daemon進程來設定系統代理,請在\"系統偏好設定->登錄項->允許在後台 中\"允許ClashX"; + +/* No comment provided by engineer. */ +"Config file have been changed" = "配置文件已被修改"; + +/* No comment provided by engineer. */ +"Config loading Fail!" = "配置文件加載失敗"; + +/* No comment provided by engineer. */ +"Copy Shell Command" = "複製終端代理命令"; + +/* No comment provided by engineer. */ +"Copy Shell Command (External)" = "複製終端代理命令(外部IP)"; + +/* No comment provided by engineer. */ +"Custom your GEOIP MMDB download address." = "自定義下載地址"; + +/* No comment provided by engineer. */ +"Details" = "查看詳情"; + +/* No comment provided by engineer. */ +"Direct" = "直連"; + +/* No comment provided by engineer. */ +"Direct Mode" = "直接連接"; + +/* No comment provided by engineer. */ +"Disable Restore Proxy Setting" = "關閉自動還原之前代理"; + +/* No comment provided by engineer. */ +"Download fail" = "下載失敗"; + +/* No comment provided by engineer. */ +"Enhance proxy list render" = "增強渲染代理列表"; + +/* No comment provided by engineer. */ +"fail" = "失敗"; + +/* No comment provided by engineer. */ +"Fail:" = "失敗:"; + +/* No comment provided by engineer. */ +"fails: %@" = "失敗: %@"; + +/* No comment provided by engineer. */ +"Global" = "全局"; + +/* No comment provided by engineer. */ +"Global Mode" = "全局連接"; + +/* No comment provided by engineer. */ +"hours" = "小時"; + +/* No comment provided by engineer. */ +"If you can not find ClashX in the settings, you can try reset daemon" = "如果在設定裡沒找到ClashX,可以嘗試重置守護程序"; + +/* No comment provided by engineer. */ +"Install" = "安裝"; + +/* No comment provided by engineer. */ +"Invalid input" = "輸入不合法"; + +/* No comment provided by engineer. */ +"Legacy Install" = "傳統安裝"; + +/* No comment provided by engineer. */ +"Load Balance" = "負載均衡"; + +"Mode" = "模式"; + +/* No comment provided by engineer. */ +"Need to Restart the ClashX to Take effect, Please start clashX manually" = "需要重啟ClashX生效,請手動啟動ClashX"; + +/* No comment provided by engineer. */ +"Never" = "從未"; + +/* No comment provided by engineer. */ +"Never show again" = "不再提示"; + +/* No comment provided by engineer. */ +"OK" = "確定"; + +/* No comment provided by engineer. */ +"Open Dashboard" = "打開控制台"; + +/* No comment provided by engineer. */ +"Open Log" = "打開日誌"; + +/* No comment provided by engineer. */ +"Open Menu" = "打開菜單"; + +/* No comment provided by engineer. */ +"Open System Login Item Setting" = "打開系統登錄項設定"; + +/* No comment provided by engineer. */ +"Ports Open Fail, Please try to restart ClashX" = "端口打開失敗,請嘗試重啟ClashX"; + +/* No comment provided by engineer. */ +"Prelease" = "Prelease"; + +/* No comment provided by engineer. */ +"Proxy Mode" = "出站模式"; + +/* No comment provided by engineer. */ +"Proxy settings are changed by another process. ClashX is no longer the default system proxy." = "代理設定被另一個程式更改。ClashX 不再是默認的系統代理"; + +/* No comment provided by engineer. */ +"Quit" = "退出"; + +/* No comment provided by engineer. */ +"Reduce alerts if notification permission is disabled" = "在通知權限關閉時減少提示彈窗"; + +/* No comment provided by engineer. */ +"Reload Config Fail" = "重新加載配置失敗"; + +/* No comment provided by engineer. */ +"Reload Config Succeed" = "重新加載配置成功"; + +/* No comment provided by engineer. */ +"Remote Config Format Error" = "遠端配置格式錯誤"; + +/* No comment provided by engineer. */ +"Remote Config Update" = "遠程配置更新"; + +/* No comment provided by engineer. */ +"Remote Config Update Fail" = ""遠程配置更新失敗"; + +/* No comment provided by engineer. */ +"Reset Daemon" = "重置守護進程"; + +/* No comment provided by engineer. */ +"ReTest" = "重測"; + +/* No comment provided by engineer. */ +"Rule" = "規則"; + +/* No comment provided by engineer. */ +"Rule Mode" = "規則模式"; + +/* No comment provided by engineer. */ +"Script" = "腳本"; + +/* No comment provided by engineer. */ +"Script Mode" = "腳本模式"; + +/* No comment provided by engineer. */ +"Should be a least 1 hour" = "應該至少 1 小時"; + +/* No comment provided by engineer. */ +"Show speedTest at top" = "在頂部顯示 SpeedTest"; + +/* No comment provided by engineer. */ +"Stable" = "穩定"; + +/* No comment provided by engineer. */ +"Succeed!" = "成功!"; + +/* No comment provided by engineer. */ +"Success" = "成功"; + +/* No comment provided by engineer. */ +"Success!" = "成功!"; + +/* No comment provided by engineer. */ +"System Proxy" = "系統代理"; + +/* No comment provided by engineer. */ +"System Proxy Changed" = "系統代理已更改"; + +/* No comment provided by engineer. */ +"Tap to reload config" = "點擊重新加載配置"; + +/* No comment provided by engineer. */ +"Testing" = "測試中"; + +/* No comment provided by engineer. */ +"The remote config name is duplicated" = "遠端配置名稱重複"; + +/* No comment provided by engineer. */ +"The status icon is coverd or hide by other app." = "狀態圖標被其他應用覆蓋或隱藏"; + +/* No comment provided by engineer. */ +"This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly." = "此版本的 ClashX 包含因 clash core 1.0 發布而導致的中斷更改。請檢查您的配置是否無法正常工作"; + +/* No comment provided by engineer. */ +"Update GEOIP Database" = "更新 GEOIP 數據庫"; + +/* No comment provided by engineer. */ +"Update remote config update interval" = "更新遠程配置更新間隔"; + +/* No comment provided by engineer. */ +"Updating" = "更新中"; + +/* No comment provided by engineer. */ +"Upgrade Channel" = "升級通道"; + +/* No comment provided by engineer. */ +"URL is not valid" = "網址無效"; + +/* No comment provided by engineer. */ +"Use iCloud" = "使用 iCloud"; + +/* No comment provided by engineer. */ +"Use reload config to try reconnect." = "使用重新加載配置嘗試重新連接"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings new file mode 100644 index 000000000..744636145 --- /dev/null +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -0,0 +1,287 @@ +/* Class = "NSMenu"; title = "Experimental"; ObjectID = "sbS-Fj-gxn"; */ +"sbS-Fj-gxn.title" = "試驗性功能"; + +/* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ +"tck-zU-JKQ.title" = "配置"; + +/* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ +"tL1-bl-LXd.title" = "託管的配置文件:"; + +/* Class = "NSMenu"; title = "API Connect Error"; ObjectID = "UU2-uE-YB4"; */ +"UU2-uE-YB4.title" = "API Connect Error"; + +/* Class = "NSMenuItem"; title = "Allow connect from Lan"; ObjectID = "Vz8-7n-vx6"; */ +"Vz8-7n-vx6.title" = "允許局域網連接"; + +/* Class = "NSMenu"; title = "Log level"; ObjectID = "wqo-3T-4qO"; */ +"wqo-3T-4qO.title" = "日誌等級"; + +/* Class = "NSMenuItem"; title = "Use built in api"; ObjectID = "xG5-B4-mlw"; */ +"xG5-B4-mlw.title" = "使用內置接口"; + +/* Class = "NSMenuItem"; title = "Dashboard"; ObjectID = "XG6-2M-PNi"; */ +"XG6-2M-PNi.title" = "控制台"; + +/* Class = "NSMenuItem"; title = "ERROR"; ObjectID = "0iu-lB-eZN"; */ +"0iu-lB-eZN.title" = "錯誤"; + +/* Class = "NSButtonCell"; title = "Update"; ObjectID = "2Rx-ih-aGW"; */ +"2Rx-ih-aGW.title" = "更新"; + +/* Class = "NSMenuItem"; title = "Log level"; ObjectID = "3Da-fL-Mzr"; */ +"3Da-fL-Mzr.title" = "日誌等級"; + +/* Class = "NSButtonCell"; title = "Add"; ObjectID = "51K-nB-xLS"; */ +"51K-nB-xLS.title" = "添加"; + +/* Class = "NSViewController"; title = "Remote Configs"; ObjectID = "6WI-Hi-v9j"; */ +"6WI-Hi-v9j.title" = "託管的配置文件"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "7G6-oO-vNs"; */ +"7G6-oO-vNs.title" = "文本單元格"; + +/* Class = "NSMenuItem"; title = "Rule"; ObjectID = "89n-bD-JHk"; */ +"89n-bD-JHk.title" = "規則判斷"; + +/* Class = "NSMenuItem"; title = "Set as system proxy"; ObjectID = "8se-yr-wmp"; */ +"8se-yr-wmp.title" = "設定為系統代理"; + +/* Class = "NSMenuItem"; title = "Ports"; ObjectID = "9i0-LH-x04"; */ +"9i0-LH-x04.title" = "端口"; + +/* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */ +"AYu-sK-qS6.title" = "主菜單"; + +/* Class = "NSMenu"; title = "Remote config"; ObjectID = "az2-wz-yyy"; */ +"az2-wz-yyy.title" = "託管配置"; + +/* Class = "NSMenuItem"; title = "Start at login"; ObjectID = "B1J-XB-BiZ"; */ +"B1J-XB-BiZ.title" = "開機啟動"; + +/* Class = "NSTableColumn"; headerCell.title = "Url"; ObjectID = "C79-J5-30z"; */ +"C79-J5-30z.headerCell.title" = "鏈接"; + +/* Class = "NSMenuItem"; title = "Benchmark"; ObjectID = "COu-UX-bww"; */ +"COu-UX-bww.title" = "延遲測速"; + +/* Class = "NSMenuItem"; title = "INFO"; ObjectID = "d8V-h0-0zF"; */ +"d8V-h0-0zF.title" = "INFO"; + +/* Class = "NSMenuItem"; title = "Help"; ObjectID = "Dd9-2F-FVY"; */ +"Dd9-2F-FVY.title" = "幫助"; + +/* Class = "NSMenuItem"; title = "SILENT"; ObjectID = "dVr-Xp-C0C"; */ +"dVr-Xp-C0C.title" = "SILENT"; + +/* Class = "NSMenuItem"; title = "Open config folder"; ObjectID = "DwE-WX-ETZ"; */ +"DwE-WX-ETZ.title" = "打開配置文件夾"; + +/* Class = "NSMenuItem"; title = "Manage"; ObjectID = "Dwg-Qb-2AU"; */ +"Dwg-Qb-2AU.title" = "管理"; + +/* Class = "NSMenuItem"; title = "Remote config"; ObjectID = "h1C-R6-Y9w"; */ +"h1C-R6-Y9w.title" = "託管配置"; + +/* Class = "NSMenuItem"; title = "About"; ObjectID = "hUb-k9-TEf"; */ +"hUb-k9-TEf.title" = "關於"; + +/* Class = "NSMenuItem"; title = "Update"; ObjectID = "I2P-Wd-Ns7"; */ +"I2P-Wd-Ns7.title" = "更新"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "id7-f0-u56"; */ +"id7-f0-u56.title" = "Text Cell"; + +/* Class = "NSMenuItem"; title = "Show current proxy in menu"; ObjectID = "j9o-36-NTd"; */ +"j9o-36-NTd.title" = "快捷展示策略組策略"; + +/* Class = "NSMenuItem"; title = "API Connect Error"; ObjectID = "jGT-1M-xJu"; */ +"jGT-1M-xJu.title" = "API Connect Error"; + +/* Class = "NSMenuItem"; title = "Copy shell command"; ObjectID = "Jmb-PK-rMW"; */ +"Jmb-PK-rMW.title" = "複製終端代理命令"; + +/* Class = "NSMenuItem"; title = "Config"; ObjectID = "JMV-Dy-CI0"; */ +"JMV-Dy-CI0.title" = "配置"; + +/* Class = "NSMenuItem"; title = "WARNING"; ObjectID = "ko2-Ir-DxA"; */ +"ko2-Ir-DxA.title" = "WARNING"; + +/* Class = "NSTableColumn"; headerCell.title = "Config Name"; ObjectID = "lRE-Xa-euB"; */ +"lRE-Xa-euB.headerCell.title" = "配置文件名稱"; + +/* Class = "NSMenuItem"; title = "Direct"; ObjectID = "Np6-Pm-Lo3"; */ +"Np6-Pm-Lo3.title" = "直接連接"; + +/* Class = "NSMenuItem"; title = "Quit"; ObjectID = "NXU-86-Eem"; */ +"NXU-86-Eem.title" = "退出"; + +/* Class = "NSMenu"; title = "Help"; ObjectID = "ogW-pn-jeR"; */ +"ogW-pn-jeR.title" = "幫助"; + +/* Class = "NSMenuItem"; title = "Experimental"; ObjectID = "OLP-Uv-at6"; */ +"OLP-Uv-at6.title" = "試驗性功能"; + +/* Class = "NSMenuItem"; title = "Check Update"; ObjectID = "p0T-J8-Emx"; */ +"p0T-J8-Emx.title" = "檢查更新"; + +/* Class = "NSMenuItem"; title = "Reload config"; ObjectID = "q3G-VH-eyy"; */ +"q3G-VH-eyy.title" = "重載配置文件"; + +/* Class = "NSMenuItem"; title = "Auto Update"; ObjectID = "r8s-OI-tgf"; */ +"r8s-OI-tgf.title" = "自動更新"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RCv-zz-HKW"; */ +"RCv-zz-HKW.title" = "Text Cell"; + +/* Class = "NSMenuItem"; title = "Set benchmark url"; ObjectID = "rls-O1-mpQ"; */ +"rls-O1-mpQ.title" = "設定延遲測速鏈接"; + +/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ +"1He-Eq-fSy.title" = "遙控"; + +/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ +"1d5-NL-UsJ.title" = "模式"; + +/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ +"3Om-yT-A83.label" = "調試"; + +/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ +"5hn-k8-CWe.headerCell.title" = "Api Secret"; + +/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ +"7wl-vK-5JO.title" = "複製shell命令(外網IP)"; + +/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ +"9gE-NX-2wJ.title" = "外部控制"; + +/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ +"AMT-oc-r8A.title" = "其他"; + +/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ +"AY0-nP-cGT.title" = "卸載代理程式"; + +/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ +"B2w-4r-5Kh.title" = "刪除"; + +/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ +"BFE-Qq-B2H.title" = "代理"; + +/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ +"BRR-WK-aeP.title" = "遙控"; + +/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ +"E8B-e5-K0A.title" = "允許局域網設備"; + +/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ +"F1s-SF-dqX.title" = "SSID 掛起"; + +/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ +"GGx-F2-7kE.title" = "請確保解決任何潛在的快捷方式衝突,全局快捷方式優先於常規快捷方式"; + +/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ +"Gnh-m8-PAz.title" = "盒子"; + +/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ +"LAj-p8-9gd.title" = "設定"; + +/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ +"Ltt-Vq-Hh1.label" = "一般設定"; + +/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ +"NLT-FZ-48V.title" = "調試設定"; + +/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ +"NPu-V9-f3r.title" = "繞過這些主機和域的代理設定"; + +/* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ +"WkL-aX-66E.title" = "這允許你控制在不同機器上運行的衝突核心"; + +/* Class = "NSMenuItem"; title = "DEBUG"; ObjectID = "XIR-Go-fWA"; */ +"XIR-Go-fWA.title" = "調試"; + +/* Class = "NSMenuItem"; title = "Show network indicator"; ObjectID = "YIO-Vj-64f"; */ +"YIO-Vj-64f.title" = "顯示網絡指示器"; + +/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ +"ZcF-10-jsl.title" = ""添加; + +/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ +"aSG-9A-eeG.title" = "代理助手"; + +/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ +"afj-4G-usr.title" = "打開日誌文件夾"; + +/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "端口設定"; + +/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ +"c01-0L-1SQ.title" = "日誌"; + +/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ +"dV6-4Z-2SO.title" = "登錄時啟動"; + +/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ +"eY9-1i-i7P.title" = "使用 SwiftUI 渲染狀態欄圖標"; + +/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ +"h1H-7k-9HS.title" = "設定更新間隔"; + +/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ +"hlb-KQ-Fdr.title" = "管理"; + +/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ +"jsL-HC-6ne.title" = "減少通知"; + +/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ +"kdV-Em-qBi.title" = "調試"; + +/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ +"kma-mp-ncL.title" = "一般設定"; + +/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ +"krh-QF-pqZ.title" = "設定"; + +/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ +"mbn-tK-UQa.title" = "API 端口"; + +/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ +"p7q-KN-kIv.title" = "使用iCloud 存儲配置文件"; + +/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ +"s6y-wL-pnr.title" = "外部管理"; + +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ +"sfe-wu-UXp.title" = "以逗號(,)分隔"; + +/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ +"uUA-LS-Hu8.title" = "代理端口"; + +/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ +"wKZ-TE-sf8.title" = "全局快捷方式"; + +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ +"xnL-ma-vFo.title" = "以逗號(,)分隔"; + +/* Class = "NSTableColumn"; headerCell.title = "Update Time"; ObjectID = "xoc-hs-9qa"; */ +"xoc-hs-9qa.headerCell.title" = "更新時間"; + +/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "xxZ-9l-69m"; */ +"xxZ-9l-69m.title" = "顯示日誌"; + +/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "yGD-AG-oYU"; */ +"yGD-AG-oYU.title" = "刪除"; + +/* Class = "NSTableColumn"; headerCell.title = "Api Url"; ObjectID = "yO6-uZ-IRv"; */ +"yO6-uZ-IRv.headerCell.title" = "API 網址"; + +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "yXh-2Y-aTS"; */ +"yXh-2Y-aTS.title" = "重置"; + +/* Class = "NSMenuItem"; title = "Global"; ObjectID = "yiM-U4-MNg"; */ +"yiM-U4-MNg.title" = "全局"; + +/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ +"zwo-q5-k5N.title" = "核心版本"; + +/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ +"aSG-9A-eeG.title" = "代理程式"; From c3f4e15a6082d7310c274fe4a30f444d1714642e Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:54:10 +0800 Subject: [PATCH 068/122] feat: add default api password [appcenter] [notarize] --- ClashX/AppDelegate.swift | 12 ++ ClashX/Base.lproj/Main.storyboard | 107 +++++++++++++----- ClashX/General/Managers/Settings.swift | 11 ++ .../zh-Hant.lproj/Localizable.strings | 2 +- .../GeneralSettingViewController.swift | 15 +++ ClashX/goClash/main.go | 31 ++++- ClashX/zh-Hans.lproj/Main.strings | 6 + ClashX/zh-Hant.lproj/Main.strings | 6 + 8 files changed, 156 insertions(+), 34 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 2a57b8c6a..53467b6b5 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -489,6 +489,18 @@ class AppDelegate: NSObject, NSApplicationDelegate { func startProxy() { if ConfigManager.shared.isRunning { return } + + if !Settings.isApiSecretSet { + if #available(macOS 11.0, *), let password = SecCreateSharedWebCredentialPassword() as? String { + Settings.apiSecret = password + } else { + Settings.apiSecret = UUID().uuidString + } + } + + if clash_checkSecret().toString() == "" || Settings.overrideConfigSecret { + clash_setSecret(Settings.apiSecret.goStringBuffer()) + } struct StartProxyResp: Codable { let externalController: String diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 4a9a75798..1ff25e661 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -43,16 +43,16 @@ - + - + - + - + + + + + + + + + + + + + + + + + @@ -209,13 +254,13 @@ - + - + - + @@ -223,7 +268,7 @@ - + @@ -231,18 +276,18 @@ - + - + - + - - + + @@ -255,7 +300,7 @@ - + @@ -275,13 +320,13 @@ - + - + - + @@ -289,7 +334,7 @@ - + @@ -297,18 +342,18 @@ - + - + - + - - + + @@ -321,12 +366,12 @@ - + - - + + - + @@ -1598,7 +1598,7 @@ - + + + + + + + + + + + + + - + + + diff --git a/ClashX/General/Managers/ICloudManager.swift b/ClashX/General/Managers/ICloudManager.swift index 97c64313c..0505ef53e 100644 --- a/ClashX/General/Managers/ICloudManager.swift +++ b/ClashX/General/Managers/ICloudManager.swift @@ -15,7 +15,7 @@ class ICloudManager { private let queue = DispatchQueue(label: "com.clashx.icloud") private var metaQuery: NSMetadataQuery? private var enableMenuItem: NSMenuItem? - private var icloudAvailable = false { + private(set) var icloudAvailable = false { didSet { useiCloud.accept(userEnableiCloud && icloudAvailable) } } private var disposeBag = DisposeBag() diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index b1112978f..83da59234 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* No comment provided by engineer. */ "hours" = "hours"; +/* No comment provided by engineer. */ +"iCloud not available" = "iCloud not available"; + /* No comment provided by engineer. */ "If you can not find ClashX in the settings, you can try reset daemon" = "If you can not find ClashX in the settings, you can try reset daemon"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index cc21ccb1a..5de949fcc 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* No comment provided by engineer. */ "hours" = "小时"; +/* No comment provided by engineer. */ +"iCloud not available" = "iCloud 不可用"; + /* No comment provided by engineer. */ "If you can not find ClashX in the settings, you can try reset daemon" = "如果在设置里没找到ClashX,可以尝试重置守护程序"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 99dc32cc1..3475f7287 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* No comment provided by engineer. */ "hours" = "小時"; +/* No comment provided by engineer. */ +"iCloud not available" = "iCloud 不可用"; + /* No comment provided by engineer. */ "If you can not find ClashX in the settings, you can try reset daemon" = "如果在設定裡沒找到ClashX,可以嘗試重置守護程序"; diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index b87e0677c..2d528e259 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -25,4 +25,21 @@ class DebugSettingViewController: NSViewController { @IBAction func actionOpenLogFolder(_ sender: Any) { NSWorkspace.shared.openFile(Logger.shared.logFolder()) } + @IBAction func actionOpenLocalConfig(_ sender: Any) { + NSWorkspace.shared.openFile(kConfigFolderPath) + + + } + @IBAction func actionOpenIcloudConfig(_ sender: Any) { + if ICloudManager.shared.icloudAvailable { + ICloudManager.shared.getUrl { + url in + if let url = url { + NSWorkspace.shared.open(url) + } + } + } else { + NSAlert.alert(with: NSLocalizedString("iCloud not available", comment: "")) + } + } } diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index ff21bfd64..cb7cabd82 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -288,3 +288,12 @@ /* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ "ckH-Er-PfX.title" = "Api秘钥"; + +/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ +"URV-fZ-bJf.title" = "iCloud"; + +/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ +"YF4-uZ-A0M.title" = "本地"; + +/* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ +"ZA9-qc-wi4.title" = "配置文件夹"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index e3356bf67..4af96003f 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -283,11 +283,17 @@ /* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ "zwo-q5-k5N.title" = "核心版本"; -/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ -"aSG-9A-eeG.title" = "代理程式"; - /* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ "LW4-cA-3bB.title" = "覆蓋配置文件設置"; /* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ "ckH-Er-PfX.title" = "API 秘鑰"; + +/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ +"URV-fZ-bJf.title" = "iCloud"; + +/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ +"YF4-uZ-A0M.title" = "本地"; + +/* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ +"ZA9-qc-wi4.title" = "配置文件夾"; From 209c1db1faae9035149e10ddf5a7a038c9fecf83 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:17:27 +0800 Subject: [PATCH 071/122] misc: enable swiftlint --- .swiftlint.yml | 2 ++ ClashX.xcodeproj/project.pbxproj | 22 ++++++++++++- ClashX/AppDelegate.swift | 33 +++---------------- .../General/Managers/ConnectionManager.swift | 6 ++-- .../PrivilegedHelperManager+Legacy.swift | 6 ++-- .../ClashWebViewContoller.swift | 10 +++--- ClashX/Views/MenuItemBaseView.swift | 2 +- Podfile | 1 + Podfile.lock | 6 +++- 9 files changed, 45 insertions(+), 43 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 4d10440fa..0d972988d 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -7,6 +7,8 @@ disabled_rules: # rule identifiers turned on by default to exclude from running - file_length - large_tuple - type_body_length + - cyclomatic_complexity + - function_body_length included: # paths to include during linting. `--path` is ignored if present. - ClashX excluded: # paths to ignore during linting. Takes precedence over `included`. diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index df87504c3..4a9a42125 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -578,6 +578,7 @@ 663E4677213FCDC4006F11BB /* Copy Files */, 494ED8F023EB0B36008D5D2F /* Run Script - Sparkle */, 318032FABBC2E552CB58B254 /* [CP] Copy Pods Resources */, + 49B93AD42A3965B40080967C /* Swift Lint */, ); buildRules = ( ); @@ -734,6 +735,25 @@ shellPath = /bin/sh; shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nLOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${FRAMEWORKS_FOLDER_PATH}\"\n\n# By default, use the configured code signing identity for the project/target\nIDENTITY=\"${EXPANDED_CODE_SIGN_IDENTITY_NAME}\"\nif [ \"$IDENTITY\" == \"\" ]\nthen\n # If a code signing identity is not specified, use ad hoc signing\n IDENTITY=\"-\"\nfi\ncodesign --verbose --force --deep -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"\ncodesign --verbose --force -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A\"\n"; }; + 49B93AD42A3965B40080967C /* Swift Lint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Swift Lint"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${PODS_ROOT}/SwiftLint/swiftlint --fix\n"; + }; A741C26F5755233F0D7CEC6F /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 53467b6b5..9bb5e8d2c 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -217,37 +217,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } - func buttonRectOnScreen() -> CGRect { - guard let button = statusItem.button else { return .zero } - guard let window = button.window else { return .zero } - let buttonRect = button.convert(button.bounds, to: nil) - let onScreenRect = window.convertToScreen(buttonRect) - return onScreenRect - } - - func leftScreenX() -> CGFloat { - let screens = NSScreen.screens - - var left: CGFloat = 0 - - for screen in screens { - if screen.frame.origin.x < left { - left = screen.frame.origin.x - } - } - return left - } - func checkMenuIconVisable() { guard let button = statusItem.button else {assertionFailure(); return } guard let window = button.window else {assertionFailure(); return } let buttonRect = button.convert(button.bounds, to: nil) let onScreenRect = window.convertToScreen(buttonRect) var leftScreenX: CGFloat = 0 - for screen in NSScreen.screens { - if screen.frame.origin.x < leftScreenX { - leftScreenX = screen.frame.origin.x - } + for screen in NSScreen.screens where screen.frame.origin.x < leftScreenX { + leftScreenX = screen.frame.origin.x } let isMenuIconHidden = onScreenRect.midX < leftScreenX @@ -489,7 +466,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func startProxy() { if ConfigManager.shared.isRunning { return } - + if !Settings.isApiSecretSet { if #available(macOS 11.0, *), let password = SecCreateSharedWebCredentialPassword() as? String { Settings.apiSecret = password @@ -497,7 +474,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { Settings.apiSecret = UUID().uuidString } } - + if clash_checkSecret().toString() == "" || Settings.overrideConfigSecret { clash_setSecret(Settings.apiSecret.goStringBuffer()) } @@ -705,7 +682,7 @@ extension AppDelegate { } switchProxyMode(mode: mode) } - + func switchProxyMode(mode: ClashProxyMode) { let config = ConfigManager.shared.currentConfig?.copy() config?.mode = mode diff --git a/ClashX/General/Managers/ConnectionManager.swift b/ClashX/General/Managers/ConnectionManager.swift index ce18d1047..406eae53d 100644 --- a/ClashX/General/Managers/ConnectionManager.swift +++ b/ClashX/General/Managers/ConnectionManager.swift @@ -28,10 +28,8 @@ class ConnectionManager { static func closeConnection(for group: String) { guard enableAutoClose else { return } ApiRequest.getConnections { conns in - for conn in conns { - if conn.chains.contains(group) { - ApiRequest.closeConnection(conn) - } + for conn in conns where conn.chains.contains(group) { + ApiRequest.closeConnection(conn) } } } diff --git a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift index 55dd25a29..b0aac92dc 100644 --- a/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift +++ b/ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift @@ -62,10 +62,10 @@ extension PrivilegedHelperManager { let appleScriptStr = "do shell script \"bash \(tmpPath.path) \" with administrator privileges" let appleScript = NSAppleScript(source: appleScriptStr) var dict: NSDictionary? - if let _ = appleScript?.executeAndReturnError(&dict) { - return + if appleScript?.executeAndReturnError(&dict) == nil { + Logger.log("apple script failed") } else { - Logger.log("apple script fail: \(String(describing: dict))") + Logger.log("apple script result: \(String(describing: dict))") } } catch let err { Logger.log("legacyInstallHelper create script fail: \(err)") diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index bcfc616b3..90642d1d1 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -49,17 +49,17 @@ class ClashWebViewContoller: NSViewController { let disposeBag = DisposeBag() let minSize = NSSize(width: 920, height: 580) var lastSize: CGSize? { - set { - if let size = newValue { - UserDefaults.standard.set(NSStringFromSize(size), forKey: "ClashWebViewContoller.lastSize") - } - } get { if let str = UserDefaults.standard.value(forKey: "ClashWebViewContoller.lastSize") as? String { return NSSizeFromString(str) as CGSize } return nil } + set { + if let size = newValue { + UserDefaults.standard.set(NSStringFromSize(size), forKey: "ClashWebViewContoller.lastSize") + } + } } let effectView = NSVisualEffectView() diff --git a/ClashX/Views/MenuItemBaseView.swift b/ClashX/Views/MenuItemBaseView.swift index fa8446adf..c6122b9af 100644 --- a/ClashX/Views/MenuItemBaseView.swift +++ b/ClashX/Views/MenuItemBaseView.swift @@ -133,7 +133,7 @@ class MenuItemBaseView: NSView { override func viewDidMoveToSuperview() { super.viewDidMoveToSuperview() guard autolayout else { return } - if #available(macOS 10.15, *) {} else { + if #unavailable(macOS 10.15) { if let view = superview { view.autoresizingMask = [.width] } diff --git a/Podfile b/Podfile index a44a467b2..28222bf4a 100644 --- a/Podfile +++ b/Podfile @@ -31,5 +31,6 @@ target 'ClashX' do pod 'Sparkle','~>1.0' pod "FlexibleDiff" pod 'GzipSwift' + pod 'SwiftLint' end diff --git a/Podfile.lock b/Podfile.lock index 995bce311..2eb85dc84 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -19,6 +19,7 @@ PODS: - RxSwift (6.5.0) - Sparkle (1.27.1) - Starscream (3.1.1) + - SwiftLint (0.52.2) - SwiftyJSON (5.0.1) - WebViewJavascriptBridge (6.0.3) @@ -34,6 +35,7 @@ DEPENDENCIES: - RxSwift - Sparkle (~> 1.0) - Starscream (= 3.1.1) + - SwiftLint - SwiftyJSON - WebViewJavascriptBridge @@ -50,6 +52,7 @@ SPEC REPOS: - RxSwift - Sparkle - Starscream + - SwiftLint - SwiftyJSON - WebViewJavascriptBridge @@ -65,9 +68,10 @@ SPEC CHECKSUMS: RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 Sparkle: 23f98b268284c8c03e6228230fc8f1807ef041d5 Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0 + SwiftLint: 1ac76dac888ca05cb0cf24d0c85887ec1209961d SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 -PODFILE CHECKSUM: 7a4b4f95913a2481c21fe728bf31871a3b75fb6c +PODFILE CHECKSUM: 284f3854ad1af36e9775ecb5fb562876d99ad634 COCOAPODS: 1.12.1 From f266dbf8b798674663ac27b79e86df6228daa727 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:19:09 +0800 Subject: [PATCH 072/122] misc: swiftlint auto fix --- ClashX/Basic/Logger.swift | 2 +- ClashX/Basic/NSView+Layout.swift | 4 +- ClashX/General/Managers/Settings.swift | 14 ++--- .../General/Utils/NetworkChangeNotifier.swift | 2 +- ClashX/General/Utils/SSIDSuspendTool.swift | 9 ++-- .../Settings/DebugSettingViewController.swift | 1 - .../GeneralSettingViewController.swift | 14 +++-- .../GlobalShortCutViewController.swift | 51 +++++++++---------- .../Views/StatusItem/NewStatusItemView.swift | 6 +-- 9 files changed, 48 insertions(+), 55 deletions(-) diff --git a/ClashX/Basic/Logger.swift b/ClashX/Basic/Logger.swift index 1c0568b4b..5e9ba47fc 100644 --- a/ClashX/Basic/Logger.swift +++ b/ClashX/Basic/Logger.swift @@ -48,7 +48,7 @@ class Logger { func logFilePath() -> String { return fileLogger.logFileManager.sortedLogFilePaths.first ?? "" } - + func logFolder() -> String { return fileLogger.logFileManager.logsDirectory } diff --git a/ClashX/Basic/NSView+Layout.swift b/ClashX/Basic/NSView+Layout.swift index 7f2728704..125bce13f 100644 --- a/ClashX/Basic/NSView+Layout.swift +++ b/ClashX/Basic/NSView+Layout.swift @@ -21,7 +21,7 @@ extension NSView { $0.leftAnchor.constraint(equalTo: $0.superview!.leftAnchor, constant: inset.left), $0.rightAnchor.constraint(equalTo: $0.superview!.rightAnchor, constant: -inset.right), $0.topAnchor.constraint(equalTo: $0.superview!.topAnchor, constant: inset.top), - $0.bottomAnchor.constraint(equalTo: $0.superview!.bottomAnchor, constant: -inset.bottom), + $0.bottomAnchor.constraint(equalTo: $0.superview!.bottomAnchor, constant: -inset.bottom) ] } } @@ -29,7 +29,7 @@ extension NSView { func makeConstraintsBindToCenterOfSuperview() -> Self { return makeConstraints { [ $0.centerXAnchor.constraint(equalTo: $0.superview!.centerXAnchor), - $0.centerYAnchor.constraint(equalTo: $0.superview!.centerYAnchor), + $0.centerYAnchor.constraint(equalTo: $0.superview!.centerYAnchor) ] } } } diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index 24aec8315..2f501f805 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -46,19 +46,19 @@ enum Settings { @UserDefault("apiPortAllowLan", defaultValue: false) static var apiPortAllowLan: Bool - + @UserDefault("disableSSIDList", defaultValue: []) - static var disableSSIDList:[String] - + static var disableSSIDList: [String] + @UserDefault("useSwiftUiMenuBar", defaultValue: true) static var useSwiftUiMenuBar: Bool - + static let apiSecretKey = "api-secret" - - static var isApiSecretSet:Bool { + + static var isApiSecretSet: Bool { return UserDefaults.standard.object(forKey: apiSecretKey) != nil } - + @UserDefault(apiSecretKey, defaultValue: "") static var apiSecret: String diff --git a/ClashX/General/Utils/NetworkChangeNotifier.swift b/ClashX/General/Utils/NetworkChangeNotifier.swift index fc82ad55a..39a0486b8 100644 --- a/ClashX/General/Utils/NetworkChangeNotifier.swift +++ b/ClashX/General/Utils/NetworkChangeNotifier.swift @@ -180,7 +180,7 @@ class NetworkChangeNotifier { } return allowIPV6 ? ipv6 : nil } - + static func getCurrentSSID() -> String? { return CWWiFiClient.shared().interface()?.ssid() } diff --git a/ClashX/General/Utils/SSIDSuspendTool.swift b/ClashX/General/Utils/SSIDSuspendTool.swift index 5f26aa3a5..d08e7ce67 100644 --- a/ClashX/General/Utils/SSIDSuspendTool.swift +++ b/ClashX/General/Utils/SSIDSuspendTool.swift @@ -23,8 +23,7 @@ class SSIDSuspendTool { .bind { [weak self] _ in self?.update() }.disposed(by: disposeBag) - - + ConfigManager.shared .proxyShouldPaused .asObservable() @@ -37,10 +36,10 @@ class SSIDSuspendTool { SystemProxyManager.shared.enableProxy() } }.disposed(by: disposeBag) - + update() } - + func update() { if shouldSuspend() { ConfigManager.shared.proxyShouldPaused.accept(true) @@ -48,7 +47,7 @@ class SSIDSuspendTool { ConfigManager.shared.proxyShouldPaused.accept(false) } } - + func shouldSuspend() -> Bool { if let currentSSID = NetworkChangeNotifier.getCurrentSSID() { return Settings.disableSSIDList.contains(currentSSID) diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index 2d528e259..6c01202ef 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -28,7 +28,6 @@ class DebugSettingViewController: NSViewController { @IBAction func actionOpenLocalConfig(_ sender: Any) { NSWorkspace.shared.openFile(kConfigFolderPath) - } @IBAction func actionOpenIcloudConfig(_ sender: Any) { if ICloudManager.shared.icloudAvailable { diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 42db7fc5d..0a3acf438 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -20,12 +20,11 @@ class GeneralSettingViewController: NSViewController { @IBOutlet weak var proxyPortTextField: NSTextField! @IBOutlet weak var apiPortTextField: NSTextField! @IBOutlet var ssidSuspendTextField: NSTextView! - + @IBOutlet weak var apiSecretTextField: NSTextField! - + @IBOutlet weak var apiSecretOverrideButton: NSButton! - - + var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() @@ -37,7 +36,6 @@ class GeneralSettingViewController: NSViewController { Settings.proxyIgnoreList = arr }.disposed(by: disposeBag) - ssidSuspendTextField.string = Settings.disableSSIDList.joined(separator: ",") ssidSuspendTextField.rx .string.debounce(.milliseconds(500), scheduler: MainScheduler.instance) @@ -46,7 +44,7 @@ class GeneralSettingViewController: NSViewController { Settings.disableSSIDList = arr SSIDSuspendTool.shared.update() }.disposed(by: disposeBag) - + LaunchAtLogin.shared.isEnableVirable .map { $0 ? .on : .off } .bind(to: launchAtLoginButton.rx.state) @@ -78,12 +76,12 @@ class GeneralSettingViewController: NSViewController { } else { apiPortTextField.stringValue = ConfigManager.shared.apiPort } - + apiSecretTextField.stringValue = Settings.apiSecret apiSecretTextField.rx.text.compactMap {$0}.bind { Settings.apiSecret = $0 }.disposed(by: disposeBag) - + apiSecretOverrideButton.state = Settings.overrideConfigSecret ? .on : .off apiSecretOverrideButton.rx.state.bind { state in Settings.overrideConfigSecret = state == .on diff --git a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift index 303adf2fa..d1e7a0bc8 100644 --- a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift +++ b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift @@ -24,41 +24,40 @@ extension KeyboardShortcuts.Name { } - enum KeyboardShortCutManager { static func setup() { KeyboardShortcuts.onKeyUp(for: .toggleSystemProxyMode) { AppDelegate.shared.actionSetSystemProxy(nil) } - + KeyboardShortcuts.onKeyUp(for: .copyShellCommand) { AppDelegate.shared.actionCopyExportCommand(AppDelegate.shared.copyExportCommandMenuItem) } - + KeyboardShortcuts.onKeyUp(for: .copyExternalShellCommand) { AppDelegate.shared.actionCopyExportCommand(AppDelegate.shared.copyExportCommandExternalMenuItem) } - + KeyboardShortcuts.onKeyUp(for: .modeDirect) { AppDelegate.shared.switchProxyMode(mode: .direct) } - + KeyboardShortcuts.onKeyUp(for: .modeRule) { AppDelegate.shared.switchProxyMode(mode: .rule) } - + KeyboardShortcuts.onKeyUp(for: .modeGlobal) { AppDelegate.shared.switchProxyMode(mode: .global) } - + KeyboardShortcuts.onKeyUp(for: .log) { AppDelegate.shared.actionShowLog(nil) } - + KeyboardShortcuts.onKeyUp(for: .dashboard) { AppDelegate.shared.actionDashboard(nil) } - + KeyboardShortcuts.onKeyUp(for: .openMenu) { AppDelegate.shared.statusItem.button?.performClick(nil) } @@ -66,39 +65,37 @@ enum KeyboardShortCutManager { } class GlobalShortCutViewController: NSViewController { - + @IBOutlet weak var proxyBox: NSBox! @IBOutlet weak var modeBoxView: NSView! @IBOutlet weak var otherBoxView: NSView! - + override func viewDidLoad() { super.viewDidLoad() - + let systemProxy = KeyboardShortcuts.RecorderCocoa(for: .toggleSystemProxyMode) let copyShellCommand = KeyboardShortcuts.RecorderCocoa(for: .copyShellCommand) let copyShellCommandExternal = KeyboardShortcuts.RecorderCocoa(for: .copyExternalShellCommand) addGridView(in: proxyBox.contentView!, with: [ - [NSTextField(labelWithString: NSLocalizedString("System Proxy", comment: "")),systemProxy], - [NSTextField(labelWithString: NSLocalizedString("Copy Shell Command", comment: "")),copyShellCommand], - [NSTextField(labelWithString: NSLocalizedString("Copy Shell Command (External)", comment: "")),copyShellCommandExternal], + [NSTextField(labelWithString: NSLocalizedString("System Proxy", comment: "")), systemProxy], + [NSTextField(labelWithString: NSLocalizedString("Copy Shell Command", comment: "")), copyShellCommand], + [NSTextField(labelWithString: NSLocalizedString("Copy Shell Command (External)", comment: "")), copyShellCommandExternal] ]) - - + addGridView(in: modeBoxView, with: [ - [NSTextField(labelWithString: NSLocalizedString("Direct Mode", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .modeDirect)], - [NSTextField(labelWithString: NSLocalizedString("Rule Mode", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .modeRule)], - [NSTextField(labelWithString: NSLocalizedString("Global Mode", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .modeGlobal)], + [NSTextField(labelWithString: NSLocalizedString("Direct Mode", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .modeDirect)], + [NSTextField(labelWithString: NSLocalizedString("Rule Mode", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .modeRule)], + [NSTextField(labelWithString: NSLocalizedString("Global Mode", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .modeGlobal)] ]) - + addGridView(in: otherBoxView, with: [ - [NSTextField(labelWithString: NSLocalizedString("Open Menu", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .openMenu)], - [NSTextField(labelWithString: NSLocalizedString("Open Log", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .log)], - [NSTextField(labelWithString: NSLocalizedString("Open Dashboard", comment: "")),KeyboardShortcuts.RecorderCocoa(for: .dashboard)], + [NSTextField(labelWithString: NSLocalizedString("Open Menu", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .openMenu)], + [NSTextField(labelWithString: NSLocalizedString("Open Log", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .log)], + [NSTextField(labelWithString: NSLocalizedString("Open Dashboard", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .dashboard)] ]) } - - - func addGridView(in superView:NSView, with views: [[NSView]]) { + + func addGridView(in superView: NSView, with views: [[NSView]]) { let gridView = NSGridView(views: views) gridView.rowSpacing = 10 gridView.rowAlignment = .firstBaseline diff --git a/ClashX/Views/StatusItem/NewStatusItemView.swift b/ClashX/Views/StatusItem/NewStatusItemView.swift index 6ca348f9a..87a2347df 100644 --- a/ClashX/Views/StatusItem/NewStatusItemView.swift +++ b/ClashX/Views/StatusItem/NewStatusItemView.swift @@ -56,10 +56,10 @@ class StatusMenuViewModel: ObservableObject { struct SwiftUIView: View { @ObservedObject var viewModel: StatusMenuViewModel var body: some View { - HStack(alignment:.center) { + HStack(alignment: .center) { Image(nsImage: $viewModel.image.wrappedValue).renderingMode(.template) - .resizable().aspectRatio(contentMode: .fit).frame(width: 16,height: 16) - + .resizable().aspectRatio(contentMode: .fit).frame(width: 16, height: 16) + if $viewModel.showSpeed.wrappedValue { Spacer(minLength: 0) VStack(alignment: .trailing) { From dabbb5bb956b09b3d273b0f9eec1d05ccce58e4f Mon Sep 17 00:00:00 2001 From: miniLV Date: Wed, 14 Jun 2023 13:37:53 +0800 Subject: [PATCH 073/122] misc: upgrade Sparkle version to 2.0 (#1162) * misc: upgrade Sparkle version to 2.0 * ci: fix ci on pull request * misc: remove sparkle codesign script and optimize github action script add export step * misc: fix checkForUpdates don't work issue * misc: Using `SPUStandardUpdaterController.checkForUpdates(_:)` replace `ontroller?.checkForUpdates(_:)` * misc: Since SUUpdater is init by code, remove this judge ment * misc: move judgement --------- Co-authored-by: yicheng <11733500+yichengchen@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- .github/workflows/pull_request.yaml | 29 +++ .gitignore | 2 + ClashX.xcodeproj/project.pbxproj | 1 - ClashX/AppDelegate.swift | 4 +- ClashX/Base.lproj/Main.storyboard | 6 +- .../General/Managers/AutoUpgardeManager.swift | 20 ++- Gemfile.lock | 167 +++++++++--------- Podfile | 2 +- Podfile.lock | 6 +- fastlane/Fastfile | 19 +- fastlane/README.md | 46 +++-- 12 files changed, 185 insertions(+), 119 deletions(-) create mode 100644 .github/workflows/pull_request.yaml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 82090dd05..818f3e4e3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,6 @@ name: ClashX -on: [ push, pull_request ] +on: [ push ] env: FASTLANE_SKIP_UPDATE_CHECK: true diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml new file mode 100644 index 000000000..71df35e82 --- /dev/null +++ b/.github/workflows/pull_request.yaml @@ -0,0 +1,29 @@ +name: ClashX + +on: [ pull_request ] + +env: + FASTLANE_SKIP_UPDATE_CHECK: true + +jobs: + build: + runs-on: macos-13 + steps: + - uses: actions/checkout@v3 + + - name: setup Go + uses: actions/setup-go@v3 + with: + go-version: 1.20.x + + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - name: install deps + run: | + bash install_dependency.sh + + - name: check + run: | + bundle exec fastlane check diff --git a/.gitignore b/.gitignore index 79cd50823..d67b4bde6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ fastlane/report.xml ClashX/Resources/Country.mmdb.gz .bundle/config *.app/** +ClashX.app.dSYM.zip +build_derived_data diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 4a9a42125..ed949680f 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -576,7 +576,6 @@ 49CF3B1B20CD7463001EBF94 /* Resources */, A741C26F5755233F0D7CEC6F /* [CP] Embed Pods Frameworks */, 663E4677213FCDC4006F11BB /* Copy Files */, - 494ED8F023EB0B36008D5D2F /* Run Script - Sparkle */, 318032FABBC2E552CB58B254 /* [CP] Copy Pods Resources */, 49B93AD42A3965B40080967C /* Swift Lint */, ); diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 9bb5e8d2c..4750bf58e 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -22,7 +22,8 @@ let statusItemLengthWithSpeed: CGFloat = 72 @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem! - + @IBOutlet weak var checkForUpdateMenuItem: NSMenuItem! + @IBOutlet var statusMenu: NSMenu! @IBOutlet var proxySettingMenuItem: NSMenuItem! @IBOutlet var autoStartMenuItem: NSMenuItem! @@ -587,6 +588,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } AutoUpgardeManager.shared.setup() AutoUpgardeManager.shared.addChanelMenuItem(&experimentalMenu) + AutoUpgardeManager.shared.setupCheckForUpdatesMenuItem(checkForUpdateMenuItem) updateExperimentalFeatureStatus() RemoteControlManager.setupMenuItem(separator: externalControlSeparator) } diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 99d2aa734..f1969ffa4 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -614,6 +614,7 @@ + @@ -817,9 +818,6 @@ - - - @@ -908,7 +906,7 @@ - + diff --git a/ClashX/General/Managers/AutoUpgardeManager.swift b/ClashX/General/Managers/AutoUpgardeManager.swift index 0fe8d2799..664d0b65f 100644 --- a/ClashX/General/Managers/AutoUpgardeManager.swift +++ b/ClashX/General/Managers/AutoUpgardeManager.swift @@ -10,8 +10,9 @@ import Cocoa import Sparkle class AutoUpgardeManager: NSObject { + var checkForUpdatesMenuItem: NSMenuItem? static let shared = AutoUpgardeManager() - + private var controller:SPUStandardUpdaterController? private var current: Channel = { if let value = UserDefaults.standard.object(forKey: "AutoUpgardeManager.current") as? Int, let channel = Channel(rawValue: value) { return channel } @@ -38,10 +39,14 @@ class AutoUpgardeManager: NSObject { } // MARK: Public - func setup() { - guard WebPortalManager.hasWebProtal == false, allowSelectChannel else { return } - SUUpdater.shared()?.delegate = self + controller = SPUStandardUpdaterController(updaterDelegate: self, userDriverDelegate: nil) + } + + func setupCheckForUpdatesMenuItem(_ item: NSMenuItem) { + checkForUpdatesMenuItem = item + checkForUpdatesMenuItem?.target = controller + checkForUpdatesMenuItem?.action = #selector(SPUStandardUpdaterController.checkForUpdates(_:)) } func addChanelMenuItem(_ menu: inout NSMenu) { @@ -72,12 +77,13 @@ extension AutoUpgardeManager { } } -extension AutoUpgardeManager: SUUpdaterDelegate { - func feedURLString(for updater: SUUpdater) -> String? { +extension AutoUpgardeManager: SPUUpdaterDelegate { + func feedURLString(for updater: SPUUpdater) -> String? { + guard WebPortalManager.hasWebProtal == false, allowSelectChannel else { return nil } return current.urlString } - func updaterWillRelaunchApplication(_ updater: SUUpdater) { + func updaterWillRelaunchApplication(_ updater: SPUUpdater) { SystemProxyManager.shared.disableProxy(port: 0, socksPort: 0, forceDisable: true) } } diff --git a/Gemfile.lock b/Gemfile.lock index 85d7892ae..082638597 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,47 +1,47 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) - activesupport (6.1.4.1) + CFPropertyList (3.0.6) + rexml + activesupport (7.0.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.4) + public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.500.0) - aws-sdk-core (3.121.0) + aws-partitions (1.779.0) + aws-sdk-core (3.174.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.48.0) - aws-sdk-core (~> 3, >= 3.120.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.66.0) + aws-sdk-core (~> 3, >= 3.174.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.102.0) - aws-sdk-core (~> 3, >= 3.120.0) + aws-sdk-s3 (1.124.0) + aws-sdk-core (~> 3, >= 3.174.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) - aws-sigv4 (1.4.0) + aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - claide (1.0.3) - cocoapods (1.11.0) + claide (1.1.0) + cocoapods (1.12.1) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.11.0) + cocoapods-core (= 1.12.1) cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.4.0, < 2.0) + cocoapods-downloader (>= 1.6.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-trunk (>= 1.6.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) @@ -49,10 +49,10 @@ GEM gh_inspector (~> 1.0) molinillo (~> 0.8.0) nap (~> 1.0) - ruby-macho (>= 1.0, < 3.0) + ruby-macho (>= 2.3.0, < 3.0) xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.11.0) - activesupport (>= 5.0, < 7) + cocoapods-core (1.12.1) + activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) concurrent-ruby (~> 1.1) @@ -62,7 +62,7 @@ GEM public_suffix (~> 4.0) typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) - cocoapods-downloader (1.5.1) + cocoapods-downloader (1.6.3) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.1) @@ -74,28 +74,29 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.1.9) + concurrent-ruby (1.2.2) declarative (0.0.20) digest-crc (0.6.4) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) - emoji_regex (3.2.2) + dotenv (2.8.1) + emoji_regex (3.2.3) escape (0.0.4) - ethon (0.14.0) + ethon (0.16.0) ffi (>= 1.15.0) - excon (0.85.0) - faraday (1.7.1) + excon (0.100.0) + faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) + faraday-net_http_persistent (~> 1.0) faraday-patron (~> 1.0) faraday-rack (~> 1.0) - multipart-post (>= 1.2, < 3) + faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) faraday-cookie_jar (0.0.7) faraday (>= 0.8.0) @@ -104,14 +105,17 @@ GEM faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) faraday-net_http (1.0.1) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) - faraday_middleware (1.1.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.5) - fastlane (2.193.1) + fastimage (2.2.7) + fastlane (2.213.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -135,7 +139,7 @@ GEM json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) + multipart-post (>= 2.0.0, < 3.0.0) naturally (~> 2.2) optparse (~> 0.1.1) plist (>= 3.1.0, < 4.0.0) @@ -150,16 +154,16 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-appcenter (1.11.0) + fastlane-plugin-appcenter (2.1.0) fastlane-plugin-update_xcodeproj (1.0.1) - fastlane-plugin-versioning (0.4.4) - ffi (1.15.4) + fastlane-plugin-versioning (0.5.1) + ffi (1.15.5) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.11.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-core (0.4.1) + google-apis-androidpublisher_v3 (0.43.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.0) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -168,59 +172,59 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.7.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-playcustomapp_v1 (0.5.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-storage_v1 (0.6.0) - google-apis-core (>= 0.4, < 2.a) + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.5.0) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.1.0) - google-cloud-storage (1.34.1) - addressable (~> 2.5) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.44.0) + addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (0.17.1) - faraday (>= 0.17.3, < 2.0) + googleauth (1.5.2) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.15) + signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.4) + http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.8.10) + i18n (1.14.1) concurrent-ruby (~> 1.0) - jmespath (1.4.0) - json (2.5.1) - jwt (2.2.3) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.1) memoist (0.16.2) - mini_magick (4.11.0) - mini_mime (1.1.1) - minitest (5.14.4) + mini_magick (4.12.0) + mini_mime (1.1.2) + minitest (5.18.0) molinillo (0.8.0) multi_json (1.15.0) - multipart-post (2.0.0) + multipart-post (2.3.0) nanaimo (0.3.0) nap (1.1.0) naturally (2.2.1) netrc (0.11.0) optparse (0.1.1) - os (1.1.1) - plist (3.6.0) - public_suffix (4.0.6) + os (1.1.4) + plist (3.7.0) + public_suffix (4.0.7) rake (13.0.6) - representable (3.1.1) + representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) @@ -231,34 +235,34 @@ GEM ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.16.0) + signet (0.17.0) addressable (~> 2.8) - faraday (>= 0.17.3, < 2.0) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.8) + simctl (1.6.10) CFPropertyList naturally terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - trailblazer-option (0.1.1) + trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (2.0.4) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) - webrick (1.7.0) + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) + webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.21.0) + xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -269,7 +273,6 @@ GEM rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) - zeitwerk (2.4.2) PLATFORMS ruby @@ -282,4 +285,4 @@ DEPENDENCIES fastlane-plugin-versioning BUNDLED WITH - 2.2.27 + 2.2.32 diff --git a/Podfile b/Podfile index 28222bf4a..af1414d24 100644 --- a/Podfile +++ b/Podfile @@ -28,7 +28,7 @@ target 'ClashX' do pod 'Starscream','3.1.1' pod 'AppCenter/Analytics' pod 'AppCenter/Crashes' - pod 'Sparkle','~>1.0' + pod 'Sparkle','~>2.0' pod "FlexibleDiff" pod 'GzipSwift' pod 'SwiftLint' diff --git a/Podfile.lock b/Podfile.lock index 2eb85dc84..d48f609b7 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -17,7 +17,7 @@ PODS: - RxRelay (6.5.0): - RxSwift (= 6.5.0) - RxSwift (6.5.0) - - Sparkle (1.27.1) + - Sparkle (2.4.1) - Starscream (3.1.1) - SwiftLint (0.52.2) - SwiftyJSON (5.0.1) @@ -33,7 +33,7 @@ DEPENDENCIES: - LetsMove - RxCocoa - RxSwift - - Sparkle (~> 1.0) + - Sparkle (~> 2.0) - Starscream (= 3.1.1) - SwiftLint - SwiftyJSON @@ -66,7 +66,7 @@ SPEC CHECKSUMS: RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 - Sparkle: 23f98b268284c8c03e6228230fc8f1807ef041d5 + Sparkle: 02038653ca2cb9b64bc68952657c28c4d4c8c75d Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0 SwiftLint: 1ac76dac888ca05cb0cf24d0c85887ec1209961d SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e diff --git a/fastlane/Fastfile b/fastlane/Fastfile index bf775b179..ccef350e2 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -6,13 +6,24 @@ build_app( workspace: "ClashX.xcworkspace", scheme: "ClashX", export_method: "developer-id", - skip_package_pkg: "true", - clean: "true" + skip_package_pkg: "false", + clean: "true", + derived_data_path: "./build_derived_data" ) - -sh(command: "rm -vfr ~/Library/Developer/Xcode/Archives/*") end +lane :check do + build_app( + workspace: "ClashX.xcworkspace", + scheme: "ClashX", + codesigning_identity: "-", + export_method: "developer-id", + skip_package_pkg: "true", + clean: "true", + derived_data_path: "./build_derived_data" + ) + end + lane :beta do current_version = get_version_number( target: "ClashX" diff --git a/fastlane/README.md b/fastlane/README.md index 1c118220a..66d96fe9d 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -1,38 +1,54 @@ fastlane documentation -================ +---- + # Installation Make sure you have the latest version of the Xcode command line tools installed: -``` +```sh xcode-select --install ``` -Install _fastlane_ using -``` -[sudo] gem install fastlane -NV -``` -or alternatively using `brew install fastlane` +For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) # Available Actions + ### build + +```sh +[bundle exec] fastlane build ``` -fastlane build + + + +### check + +```sh +[bundle exec] fastlane check ``` + + ### beta + +```sh +[bundle exec] fastlane beta ``` -fastlane beta -``` + + ### addKeyChain + +```sh +[bundle exec] fastlane addKeyChain ``` -fastlane addKeyChain -``` + ---- -This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. -More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). -The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. + +More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). + +The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). From cf2008a26509563357a522a22ad3138829c80609 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:50:34 +0800 Subject: [PATCH 074/122] misc: disable dashboard in macos 10.14 due to webkit support --- ClashX/AppDelegate.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 4750bf58e..532d83c7d 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -98,6 +98,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + if #unavailable(macOS 10.15) { + // dashboard is not support in macOS 10.15 below + self.dashboardMenuItem.isHidden = true + } setupStatusMenuItemData() AppVersionUtil.showUpgradeAlert() ICloudManager.shared.setup() From 69a5db7a2c341df6bc67d9542bfa2f9e453281f8 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 14 Jun 2023 14:52:10 +0800 Subject: [PATCH 075/122] misc: update core --- ClashX.xcodeproj/project.pbxproj | 18 ------------------ ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index ed949680f..5b74837c9 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -716,24 +716,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ClashX/Pods-ClashX-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 494ED8F023EB0B36008D5D2F /* Run Script - Sparkle */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 12; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run Script - Sparkle"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nLOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${FRAMEWORKS_FOLDER_PATH}\"\n\n# By default, use the configured code signing identity for the project/target\nIDENTITY=\"${EXPANDED_CODE_SIGN_IDENTITY_NAME}\"\nif [ \"$IDENTITY\" == \"\" ]\nthen\n # If a code signing identity is not specified, use ad hoc signing\n IDENTITY=\"-\"\nfi\ncodesign --verbose --force --deep -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"\ncodesign --verbose --force -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A\"\n"; - }; 49B93AD42A3965B40080967C /* Swift Lint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index a7b62f852..852aec0dc 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.16.1-0.20230528061203-289025c6eeb3 + github.com/Dreamacro/clash v1.16.1-0.20230613154448-18c666a1abb7 github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 0bfa2158b..6e03aca2c 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.16.1-0.20230528061203-289025c6eeb3 h1:Of6EomzsYbxnG88T/LdEhT+FtVex/gakoTtPK8QMUkw= -github.com/Dreamacro/clash v1.16.1-0.20230528061203-289025c6eeb3/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= +github.com/Dreamacro/clash v1.16.1-0.20230613154448-18c666a1abb7 h1:9m9KShVQEdmLq/B6vFPNRtW6Sn8caomJi0WrNnrW2uk= +github.com/Dreamacro/clash v1.16.1-0.20230613154448-18c666a1abb7/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= From 008c92e9c1d177471df32f8f6154a30e151a1987 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:08:45 +0800 Subject: [PATCH 076/122] fix: delete webview cache when version update, remove git dir in dashboard folder, add more log in dashboard window --- ClashX.xcodeproj/project.pbxproj | 8 +-- ClashX/General/Utils/AppVersionUtil.swift | 1 + .../ClashWebViewContoller.swift | 55 +++++++++++++------ install_dependency.sh | 2 + 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 5b74837c9..68cf9ff9f 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -1008,7 +1008,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; - CURRENT_PROJECT_VERSION = 1.97.0; + CURRENT_PROJECT_VERSION = 1.116.2; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -1030,7 +1030,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.97.0; + MARKETING_VERSION = 1.116.2; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1055,7 +1055,7 @@ COMBINE_HIDPI_IMAGES = YES; COMPRESS_PNG_FILES = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1.97.0; + CURRENT_PROJECT_VERSION = 1.116.2; DEPLOYMENT_POSTPROCESSING = YES; DEVELOPMENT_TEAM = MEWHFZ92DY; ENABLE_HARDENED_RUNTIME = YES; @@ -1078,7 +1078,7 @@ "$(PROJECT_DIR)/ClashX/goClash", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.97.0; + MARKETING_VERSION = 1.116.2; OTHER_CODE_SIGN_FLAGS = "--timestamp"; PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/ClashX/General/Utils/AppVersionUtil.swift b/ClashX/General/Utils/AppVersionUtil.swift index 9ef4ff4fa..dcb9600e3 100644 --- a/ClashX/General/Utils/AppVersionUtil.swift +++ b/ClashX/General/Utils/AppVersionUtil.swift @@ -44,6 +44,7 @@ class AppVersionUtil: NSObject { extension AppVersionUtil { static func showUpgradeAlert() { if let lastVersion = shared.lastVersionNumber, hasVersionChanged { + WebCacheCleaner.clean() guard lastVersion.compare("1.30.0", options: .numeric) == .orderedAscending else { return } let alert = NSAlert() alert.messageText = NSLocalizedString("This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly.", comment: "") diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index 90642d1d1..0d89f1f6d 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -12,6 +12,19 @@ import RxSwift import WebKit import WebViewJavascriptBridge +enum WebCacheCleaner { + static func clean() { + HTTPCookieStorage.shared.removeCookies(since: Date.distantPast) + Logger.log("[WebCacheCleaner] All cookies deleted") + WKWebsiteDataStore.default().fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in + records.forEach { record in + WKWebsiteDataStore.default().removeData(ofTypes: record.dataTypes, for: [record], completionHandler: {}) + Logger.log("[WebCacheCleaner] Record \(record) deleted") + } + } + } +} + class ClashWebViewWindowController: NSWindowController { var onWindowClose: (() -> Void)? @@ -83,12 +96,13 @@ class ClashWebViewContoller: NSViewController { webview.navigationDelegate = self webview.customUserAgent = "ClashX Runtime" - - if NSAppKitVersion.current.rawValue > 1500 { - webview.setValue(false, forKey: "drawsBackground") - } else { - webview.setValue(true, forKey: "drawsTransparentBackground") + if #available(macOS 13.3, *) { + webview.isInspectable = true } + webview.setValue(false, forKey: "drawsBackground") + let script = WKUserScript(source: "console.log(\"dashboard loaded\")", injectionTime: .atDocumentStart, forMainFrameOnly: false) + + webview.configuration.userContentController.addUserScript(script) bridge = JsBridgeUtil.initJSbridge(webview: webview, delegate: self) registerExtenalJSBridgeFunction() @@ -119,12 +133,10 @@ class ClashWebViewContoller: NSViewController { view.window?.styleMask.insert(.closable) view.window?.styleMask.insert(.resizable) view.window?.styleMask.insert(.miniaturizable) - if #available(OSX 10.13, *) { - view.window?.toolbar = NSToolbar() - view.window?.toolbar?.showsBaselineSeparator = false - view.wantsLayer = true - view.layer?.cornerRadius = 10 - } + view.window?.toolbar = NSToolbar() + view.window?.toolbar?.showsBaselineSeparator = false + view.wantsLayer = true + view.layer?.cornerRadius = 10 view.window?.minSize = minSize if let lastSize = lastSize, lastSize != .zero { @@ -148,12 +160,20 @@ class ClashWebViewContoller: NSViewController { } func loadWebRecourses() { + WKWebsiteDataStore.default().removeData(ofTypes: [WKWebsiteDataTypeOfflineWebApplicationCache, WKWebsiteDataTypeMemoryCache], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler: { }) // defaults write com.west2online.ClashX webviewUrl "your url" + if let userDefineUrl = UserDefaults.standard.string(forKey: "webviewUrl"), let url = URL(string: userDefineUrl) { + Logger.log("get user define url: \(url)") + webview.load(URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 0)) + return + } let defaultUrl = "http://127.0.0.1:\(ConfigManager.shared.apiPort)/ui/" - let url = UserDefaults.standard.string(forKey: "webviewUrl") ?? defaultUrl - if let url = URL(string: url) { + if let url = URL(string: defaultUrl) { + Logger.log("dashboard url:\(defaultUrl)") webview.load(URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 0)) + return } + Logger.log("load dashboard url fail", level: .error) } deinit { @@ -174,20 +194,23 @@ extension ClashWebViewContoller { } extension ClashWebViewContoller: WKUIDelegate, WKNavigationDelegate { - func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {} + func webViewWebContentProcessDidTerminate(_ webView: WKWebView) { + Logger.log("[dashboard] webview crashed", level: .error) + } func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {} func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + Logger.log("[dashboard] load request \(String(describing: navigationAction.request.url?.absoluteString))", level: .debug) decisionHandler(.allow) } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - Logger.log("\(String(describing: navigation))", level: .debug) + Logger.log("[dashboard] didFinish \(String(describing: navigation))", level: .info) } func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { - Logger.log("\(error)", level: .debug) + Logger.log("[dashboard] \(String(describing: navigation)) error: \(error)", level: .error) } func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { diff --git a/install_dependency.sh b/install_dependency.sh index a1d0fa9b2..e2c37f334 100755 --- a/install_dependency.sh +++ b/install_dependency.sh @@ -20,3 +20,5 @@ mv Country.mmdb.gz ./ClashX/Resources/Country.mmdb.gz echo "install dashboard" cd ClashX/Resources git clone -b gh-pages https://github.com/Dreamacro/clash-dashboard.git dashboard +cd dashboard +rm -rf *.webmanifest *.js CNAME .git From 8d586f3ae6a9cf5e824bc9f555a8a49277e51305 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:10:15 +0800 Subject: [PATCH 077/122] misc: update logger to log called function and update sample config --- ClashX.xcodeproj/project.pbxproj | 3 ++- .../xcshareddata/xcschemes/ClashX.xcscheme | 2 +- .../com.west2online.ClashX.ProxyConfigHelper.xcscheme | 2 +- ClashX/AppDelegate.swift | 2 +- ClashX/Basic/Logger.swift | 4 ++-- ClashX/Resources/sampleConfig.yaml | 11 +++-------- Podfile.lock | 2 +- 7 files changed, 11 insertions(+), 15 deletions(-) diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 68cf9ff9f..9a5f7652f 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -615,8 +615,9 @@ 49CF3B1520CD7463001EBF94 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1030; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = west2online; TargetAttributes = { 49CF3B1C20CD7463001EBF94 = { diff --git a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme index 5fe716f38..755fca964 100644 --- a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme +++ b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme @@ -1,6 +1,6 @@ String { diff --git a/ClashX/Resources/sampleConfig.yaml b/ClashX/Resources/sampleConfig.yaml index cece8a179..e6ac56444 100644 --- a/ClashX/Resources/sampleConfig.yaml +++ b/ClashX/Resources/sampleConfig.yaml @@ -2,18 +2,13 @@ ## 配置文件需要放置在 $HOME/.config/clash/*.yaml ## 这份文件是clashX的基础配置文件,请尽量新建配置文件进行修改。 -## !!!只有这份文件的端口设置会随ClashX启动生效 +## 端口设置请在 菜单条图标->配置->更多配置 中进行修改 -## 如果您不知道如何操作,请参阅 官方Github文档 https://github.com/Dreamacro/clash/blob/dev/README.md +## 如果您不知道如何操作,请参阅 官方Github文档 https://dreamacro.github.io/clash/ #---------------------------------------------------# -# (HTTP and SOCKS5 in one port) -mixed-port: 7890 -# RESTful API for clash -external-controller: 127.0.0.1:9090 -allow-lan: false mode: rule -log-level: warning +log-level: info proxies: diff --git a/Podfile.lock b/Podfile.lock index d48f609b7..e08603e47 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -72,6 +72,6 @@ SPEC CHECKSUMS: SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 -PODFILE CHECKSUM: 284f3854ad1af36e9775ecb5fb562876d99ad634 +PODFILE CHECKSUM: 063fb0a8853b3bc465ec93e15b75c951093e400a COCOAPODS: 1.12.1 From b28af782bb2e262617625239452ddd962237ef55 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:24:00 +0800 Subject: [PATCH 078/122] misc: add reset userdefault entry --- ClashX/AppDelegate.swift | 4 +-- ClashX/Base.lproj/Main.storyboard | 35 ++++++++++++++++++- .../en.lproj/Localizable.strings | 3 ++ .../zh-Hans.lproj/Localizable.strings | 3 ++ .../zh-Hant.lproj/Localizable.strings | 3 ++ .../Settings/DebugSettingViewController.swift | 8 +++++ ClashX/zh-Hans.lproj/Main.strings | 6 ++++ ClashX/zh-Hant.lproj/Main.strings | 6 ++++ 8 files changed, 65 insertions(+), 3 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 40a08bfee..312fa6102 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -23,7 +23,7 @@ let statusItemLengthWithSpeed: CGFloat = 72 class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem! @IBOutlet weak var checkForUpdateMenuItem: NSMenuItem! - + @IBOutlet var statusMenu: NSMenu! @IBOutlet var proxySettingMenuItem: NSMenuItem! @IBOutlet var autoStartMenuItem: NSMenuItem! @@ -93,7 +93,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { Logger.log("postFinishLaunching") defer { statusItem.menu = statusMenu - DispatchQueue.main.asyncAfter(deadline: .now()+1) { + DispatchQueue.main.asyncAfter(deadline: .now()+5) { self.checkMenuIconVisable() } diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index f1969ffa4..352dcb500 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1690,21 +1690,54 @@ + + + + + + + + + + + + + + + + + + + + + + - + + + diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 83da59234..8a05ac9cd 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -37,6 +37,9 @@ /* No comment provided by engineer. */ "ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app" = "ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app"; +/* No comment provided by engineer. */ +"Click OK to quit the app and apply change." = "Click OK to quit the app and apply change."; + /* No comment provided by engineer. */ "Config file have been changed" = "Config file have been changed"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 5de949fcc..e41e4e890 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -37,6 +37,9 @@ /* No comment provided by engineer. */ "ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app" = "ClashX需要通过后台Daemon进程来设置系统代理,请在\"系统偏好设置->登录项->允许在后台 中\"允许ClashX"; +/* No comment provided by engineer. */ +"Click OK to quit the app and apply change." = "确认清理并退出软件"; + /* No comment provided by engineer. */ "Config file have been changed" = "配置文件已被修改"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 3475f7287..63cd2a50c 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -37,6 +37,9 @@ /* No comment provided by engineer. */ "ClashX use a daemon helper to setup your system proxy. Please enable ClashX in the Login Items under the Allow in the Background section and relaunch the app" = "ClashX需要通過後台Daemon進程來設定系統代理,請在\"系統偏好設定->登錄項->允許在後台 中\"允許ClashX"; +/* No comment provided by engineer. */ +"Click OK to quit the app and apply change." = "確認清理併退出軟件"; + /* No comment provided by engineer. */ "Config file have been changed" = "配置文件已被修改"; diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index 6c01202ef..e71f4eb0b 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -41,4 +41,12 @@ class DebugSettingViewController: NSViewController { NSAlert.alert(with: NSLocalizedString("iCloud not available", comment: "")) } } + + @IBAction func actionResetUserDefault(_ sender: Any) { + guard let domain = Bundle.main.bundleIdentifier else { return } + NSAlert.alert(with: NSLocalizedString("Click OK to quit the app and apply change.", comment: "")) + UserDefaults.standard.removePersistentDomain(forName: domain) + UserDefaults.standard.synchronize() + NSApplication.shared.terminate(self) + } } diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index cb7cabd82..82901ba07 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -297,3 +297,9 @@ /* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ "ZA9-qc-wi4.title" = "配置文件夹"; + +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ +"5ws-55-f1g.title" = "重置"; + +/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ +"PF0-Gd-XbR.title" = "应用配置"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index 4af96003f..e1842963e 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -297,3 +297,9 @@ /* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ "ZA9-qc-wi4.title" = "配置文件夾"; + +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ +"5ws-55-f1g.title" = "重置"; + +/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ +"PF0-Gd-XbR.title" = "應用配置"; From 4ab50589cfd884839e5ea857e053c738cb87b692 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 17 Jun 2023 12:51:26 +0800 Subject: [PATCH 079/122] feat: update core, support alive api --- ClashX/Models/ClashProxy.swift | 3 ++- ClashX/Views/ProxyMenuItem.swift | 6 +++++- ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index 61181688a..4b8705700 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -97,6 +97,7 @@ class ClashProxy: Codable { let all: [ClashProxyName]? let history: [ClashProxySpeedHistory] let now: ClashProxyName? + let alive: Bool? weak var enclosingResp: ClashProxyResp? weak var enclosingProvider: ClashProvider? @@ -130,7 +131,7 @@ class ClashProxy: Codable { }() private enum CodingKeys: String, CodingKey { - case type, all, history, now, name + case type, all, history, now, name, alive } lazy var maxProxyNameLength: CGFloat = { diff --git a/ClashX/Views/ProxyMenuItem.swift b/ClashX/Views/ProxyMenuItem.swift index 6e04e894e..2809b9002 100644 --- a/ClashX/Views/ProxyMenuItem.swift +++ b/ClashX/Views/ProxyMenuItem.swift @@ -71,7 +71,11 @@ class ProxyMenuItem: NSMenuItem { assertionFailure() return } - updateDelay(info.history.last?.delayDisplay, rawValue: info.history.last?.delay) + if info.alive == false { + updateDelay(NSLocalizedString("fail", comment: ""), rawValue: 0) + } else { + updateDelay(info.history.last?.delayDisplay, rawValue: info.history.last?.delay) + } } @objc private func proxyGroupInfoUpdate(note: Notification) { diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 852aec0dc..065bd04a8 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.16.1-0.20230613154448-18c666a1abb7 + github.com/Dreamacro/clash v1.16.1-0.20230616131910-31fe77ee694d github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 6e03aca2c..8a0a074dd 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.16.1-0.20230613154448-18c666a1abb7 h1:9m9KShVQEdmLq/B6vFPNRtW6Sn8caomJi0WrNnrW2uk= -github.com/Dreamacro/clash v1.16.1-0.20230613154448-18c666a1abb7/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= +github.com/Dreamacro/clash v1.16.1-0.20230616131910-31fe77ee694d h1:1rdUID6QQmLq0sHdrNU8MId9wE5FzYr1I5oZqQ61dmE= +github.com/Dreamacro/clash v1.16.1-0.20230616131910-31fe77ee694d/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= From 2f9260c17db36f55ededb8b1d5a57fc469e6ead1 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 25 Jun 2023 08:35:41 +0800 Subject: [PATCH 080/122] fix: temp fix macos 14 beta2 crash issue --- ClashX.xcodeproj/project.pbxproj | 14 +++++ ClashX/Vendor/Safe/NSMutableArray+Safe.h | 17 ++++++ ClashX/Vendor/Safe/NSMutableArray+Safe.m | 73 ++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 ClashX/Vendor/Safe/NSMutableArray+Safe.h create mode 100644 ClashX/Vendor/Safe/NSMutableArray+Safe.m diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 9a5f7652f..b983fbc11 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ 4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98D20D0AE990001E564 /* sampleConfig.yaml */; }; 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */; }; 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */; }; + 4994B5522A47C1C900E595B9 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */; }; 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */; }; 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */; }; 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485722ED715200F6C675 /* RemoteConfigModel.swift */; }; @@ -181,6 +182,8 @@ 4989F98D20D0AE990001E564 /* sampleConfig.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = sampleConfig.yaml; sourceTree = ""; }; 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTabViewController.swift; sourceTree = ""; }; 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingViewController.swift; sourceTree = ""; }; + 4994B5502A47C1C900E595B9 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+Safe.h"; sourceTree = ""; }; + 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+Safe.m"; sourceTree = ""; }; 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWebViewContoller.swift; sourceTree = ""; }; 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigViewController.swift; sourceTree = ""; }; 499A485722ED715200F6C675 /* RemoteConfigModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigModel.swift; sourceTree = ""; }; @@ -376,6 +379,7 @@ 49722FDD211ED2A900650A41 /* Vendor */ = { isa = PBXGroup; children = ( + 4994B54F2A47C1A800E595B9 /* Safe */, 4929F683258CE07500A435F6 /* UserDefaultWrapper.swift */, F976275D23634F18000EDEFE /* LoginServiceKit */, 49722FE9211F338B00650A41 /* Witness */, @@ -430,6 +434,15 @@ path = Settings; sourceTree = ""; }; + 4994B54F2A47C1A800E595B9 /* Safe */ = { + isa = PBXGroup; + children = ( + 4994B5502A47C1C900E595B9 /* NSMutableArray+Safe.h */, + 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */, + ); + path = Safe; + sourceTree = ""; + }; 4997732220D251A60009B136 /* Basic */ = { isa = PBXGroup; children = ( @@ -813,6 +826,7 @@ 49B4575D244F4A2A00463C39 /* PrivilegedHelperManager.swift in Sources */, 49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */, 4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */, + 4994B5522A47C1C900E595B9 /* NSMutableArray+Safe.m in Sources */, F9E754D2239CC28D00CEE7CC /* NSAlert+Extension.swift in Sources */, 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */, 49D176A9235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift in Sources */, diff --git a/ClashX/Vendor/Safe/NSMutableArray+Safe.h b/ClashX/Vendor/Safe/NSMutableArray+Safe.h new file mode 100644 index 000000000..9476f0d46 --- /dev/null +++ b/ClashX/Vendor/Safe/NSMutableArray+Safe.h @@ -0,0 +1,17 @@ +// +// NSMutableArray+Safe.h +// ClashX +// +// Created by yicheng on 2023/6/25. +// Copyright © 2023 west2online. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSMutableArray(Safe) + +@end + +NS_ASSUME_NONNULL_END diff --git a/ClashX/Vendor/Safe/NSMutableArray+Safe.m b/ClashX/Vendor/Safe/NSMutableArray+Safe.m new file mode 100644 index 000000000..b33ea5d08 --- /dev/null +++ b/ClashX/Vendor/Safe/NSMutableArray+Safe.m @@ -0,0 +1,73 @@ +#import "NSMutableArray+Safe.h" +#import + +@implementation NSMutableArray(Safe) ++ (void)load { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 14, *)) { + swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:)); + swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(replaceObjectAtIndex:withObject:), @selector(hookReplaceObjectAtIndex:withObject:)); + } + }); +} + + +- (id)hookObjectAtIndex:(NSUInteger)index { + @synchronized (self) { + if (index < self.count) { + return [self hookObjectAtIndex:index]; + } + return nil; + } +} + +- (void) hookReplaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { + @synchronized (self) { + if (index < self.count && anObject) { + [self hookReplaceObjectAtIndex:index withObject:anObject]; + } else { + if (!anObject) { + return; + } + if (index >= self.count) { + return; + } + } + } +} + + +void swizzleInstanceMethod(Class cls, SEL origSelector, SEL newSelector) +{ + if (!cls) { + return; + } + /* if current class not exist selector, then get super*/ + Method originalMethod = class_getInstanceMethod(cls, origSelector); + Method swizzledMethod = class_getInstanceMethod(cls, newSelector); + + /* add selector if not exist, implement append with method */ + if (class_addMethod(cls, + origSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)) ) { + /* replace class instance method, added if selector not exist */ + /* for class cluster , it always add new selector here */ + class_replaceMethod(cls, + newSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + + } else { + /* swizzleMethod maybe belong to super */ + class_replaceMethod(cls, + newSelector, + class_replaceMethod(cls, + origSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)), + method_getTypeEncoding(originalMethod)); + } +} +@end From b1e41841b1da4855f25997e40285197d4ecb5ed6 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:08:01 +0800 Subject: [PATCH 081/122] fix menu bar height issue --- ClashX/Vendor/Safe/NSMutableArray+Safe.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ClashX/Vendor/Safe/NSMutableArray+Safe.m b/ClashX/Vendor/Safe/NSMutableArray+Safe.m index b33ea5d08..d3b3c378d 100644 --- a/ClashX/Vendor/Safe/NSMutableArray+Safe.m +++ b/ClashX/Vendor/Safe/NSMutableArray+Safe.m @@ -18,6 +18,9 @@ - (id)hookObjectAtIndex:(NSUInteger)index { if (index < self.count) { return [self hookObjectAtIndex:index]; } + if ([self.firstObject isKindOfClass:[NSNumber class]] || self.count == 0) { + return @(22); // menu height + } return nil; } } From 8807dacab3215d80afa3ed2149e5fb0b0565492c Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 25 Jun 2023 09:28:55 +0800 Subject: [PATCH 082/122] fix: try fix macos14 issue --- ClashX.xcodeproj/project.pbxproj | 8 ++++ ClashX/AppDelegate.swift | 1 + ClashX/General/Hotfixs.swift | 26 +++++++++++ ClashX/Views/NormalMenuItemView.swift | 62 +++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 ClashX/General/Hotfixs.swift create mode 100644 ClashX/Views/NormalMenuItemView.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index b983fbc11..5ecd98895 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */; }; 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */; }; 4994B5522A47C1C900E595B9 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */; }; + 4994B5542A47C4FF00E595B9 /* NormalMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5532A47C4FF00E595B9 /* NormalMenuItemView.swift */; }; + 4994B5562A47C5FA00E595B9 /* Hotfixs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5552A47C5FA00E595B9 /* Hotfixs.swift */; }; 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */; }; 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */; }; 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485722ED715200F6C675 /* RemoteConfigModel.swift */; }; @@ -184,6 +186,8 @@ 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingViewController.swift; sourceTree = ""; }; 4994B5502A47C1C900E595B9 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+Safe.h"; sourceTree = ""; }; 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+Safe.m"; sourceTree = ""; }; + 4994B5532A47C4FF00E595B9 /* NormalMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalMenuItemView.swift; sourceTree = ""; }; + 4994B5552A47C5FA00E595B9 /* Hotfixs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hotfixs.swift; sourceTree = ""; }; 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWebViewContoller.swift; sourceTree = ""; }; 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigViewController.swift; sourceTree = ""; }; 499A485722ED715200F6C675 /* RemoteConfigModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigModel.swift; sourceTree = ""; }; @@ -341,6 +345,7 @@ 491C250121BD561200AB5D44 /* Managers */, 491C250021BD55B900AB5D44 /* Utils */, 492C4868210EE6B9004554A0 /* ApiRequest.swift */, + 4994B5552A47C5FA00E595B9 /* Hotfixs.swift */, ); path = General; sourceTree = ""; @@ -372,6 +377,7 @@ F910AA23240134AF00116E95 /* ProxyGroupMenu.swift */, 493A9F272453E60400D35296 /* ProxyDelayHistoryMenu.swift */, 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */, + 4994B5532A47C4FF00E595B9 /* NormalMenuItemView.swift */, ); path = Views; sourceTree = ""; @@ -832,6 +838,7 @@ 49D176A9235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift in Sources */, F976275C23634DF8000EDEFE /* LoginServiceKit.swift in Sources */, 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */, + 4994B5542A47C4FF00E595B9 /* NormalMenuItemView.swift in Sources */, F92D0B2E236D35C000575E15 /* ProxyItemView.swift in Sources */, 49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */, 49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */, @@ -843,6 +850,7 @@ 499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */, F939724C23A4B33500FE5A3F /* ClashProvider.swift in Sources */, 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */, + 4994B5562A47C5FA00E595B9 /* Hotfixs.swift in Sources */, 49862FA0218418C600A1D5EC /* ClashRule.swift in Sources */, 49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */, 4929F684258CE07500A435F6 /* UserDefaultWrapper.swift in Sources */, diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 312fa6102..ca5e724ba 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -159,6 +159,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { setupNetworkNotifier() registCrashLogger() KeyboardShortCutManager.setup() + Hotfixs.applyMacOS14Hotfix(modeItem: proxyModeMenuItem) } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { diff --git a/ClashX/General/Hotfixs.swift b/ClashX/General/Hotfixs.swift new file mode 100644 index 000000000..9aa779942 --- /dev/null +++ b/ClashX/General/Hotfixs.swift @@ -0,0 +1,26 @@ +// +// Hotfixs.swift +// ClashX +// +// Created by yicheng on 2023/6/25. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import RxCocoa +import RxSwift + +enum Hotfixs { + private static var kvos = Set() + static func applyMacOS14Hotfix(modeItem: NSMenuItem) { + if #available(macOS 14.0, *) { + let itemView = NormalMenuItemView(modeItem.title, rightArrow: true) + + let observer = modeItem.observe(\.title) { item, _ in + itemView.label.stringValue = item.title + } + kvos.insert(observer) + modeItem.view = itemView + } + } +} diff --git a/ClashX/Views/NormalMenuItemView.swift b/ClashX/Views/NormalMenuItemView.swift new file mode 100644 index 000000000..d0f6ce5cf --- /dev/null +++ b/ClashX/Views/NormalMenuItemView.swift @@ -0,0 +1,62 @@ +// +// NormalMenuItemView.swift +// ClashX +// +// Created by yicheng on 2023/6/25. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +@available(macOS 11.0, *) +class NormalMenuItemView: MenuItemBaseView { + let label: NSTextField + private let arrowLabel: NSControl = { + let image = NSImage(named: NSImage.goForwardTemplateName)!.withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .small))! + return NSImageView(image: image) + }() + + init(_ title: String, rightArrow: Bool) { + label = NSTextField(labelWithString: title) + label.font = type(of: self).labelFont + label.sizeToFit() + let rect = NSRect(x: 0, y: 0, width: label.bounds.width + 40 + arrowLabel.bounds.width, height: 20) + super.init(frame: rect, autolayout: false) + addSubview(label) + label.frame = NSRect(x: 20, y: 0, width: label.bounds.width, height: 20) + label.textColor = NSColor.labelColor + if rightArrow { + addSubview(arrowLabel) + } + } + + override func layoutSubtreeIfNeeded() { + super.layoutSubtreeIfNeeded() + arrowLabel.frame = NSRect(x: bounds.width - arrowLabel.bounds.width - 12, y: 0, width: arrowLabel.bounds.width, height: 20) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var cells: [NSCell?] { + return [label.cell, arrowLabel.cell] + } + + override var labels: [NSTextField] { + return [label] + } +} + +@available(macOS 11.0, *) +extension NormalMenuItemView: ProxyGroupMenuHighlightDelegate { + func highlight(item: NSMenuItem?) { + if enclosingMenuItem?.hasSubmenu == true, let item = item { + if enclosingMenuItem?.submenu?.items.contains(item) == true { + isHighlighted = true + return + } + } + isHighlighted = item == enclosingMenuItem + } +} From 0df4573ce58baddf49f6ef43f814eef1edd39154 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 26 Jun 2023 08:38:06 +0800 Subject: [PATCH 083/122] misc: update core --- ClashX/goClash/go.mod | 3 ++- ClashX/goClash/go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 065bd04a8..67825fa3b 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.19 require ( - github.com/Dreamacro/clash v1.16.1-0.20230616131910-31fe77ee694d + github.com/Dreamacro/clash v1.16.1-0.20230625011906-5212aaf445ec github.com/oschwald/geoip2-golang v1.8.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) @@ -11,6 +11,7 @@ require ( require ( github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd // indirect github.com/ajg/form v1.5.1 // indirect + github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-chi/chi/v5 v5.0.8 // indirect github.com/go-chi/cors v1.2.1 // indirect github.com/go-chi/render v1.0.2 // indirect diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 8a0a074dd..7df810d82 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.16.1-0.20230616131910-31fe77ee694d h1:1rdUID6QQmLq0sHdrNU8MId9wE5FzYr1I5oZqQ61dmE= -github.com/Dreamacro/clash v1.16.1-0.20230616131910-31fe77ee694d/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk= +github.com/Dreamacro/clash v1.16.1-0.20230625011906-5212aaf445ec h1:WjddJYCq3BhYPCMAbTeOVIqYZORs5xkENpp/IYzRHF8= +github.com/Dreamacro/clash v1.16.1-0.20230625011906-5212aaf445ec/go.mod h1:JmNt+xgGcYPlS3+1wwn7BIbjRdThukFvhgerPDIQI2g= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -7,6 +7,8 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= From bb896b79dd749076964cb58a372dc214656fe699 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 8 Jul 2023 09:28:59 +0800 Subject: [PATCH 084/122] misc: revert macOS 14 workaround --- ClashX.xcodeproj/project.pbxproj | 18 ------ ClashX/AppDelegate.swift | 1 - ClashX/General/Hotfixs.swift | 26 -------- ClashX/Vendor/Safe/NSMutableArray+Safe.h | 17 ------ ClashX/Vendor/Safe/NSMutableArray+Safe.m | 76 ------------------------ 5 files changed, 138 deletions(-) delete mode 100644 ClashX/General/Hotfixs.swift delete mode 100644 ClashX/Vendor/Safe/NSMutableArray+Safe.h delete mode 100644 ClashX/Vendor/Safe/NSMutableArray+Safe.m diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 5ecd98895..bb53cae6c 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -46,9 +46,7 @@ 4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98D20D0AE990001E564 /* sampleConfig.yaml */; }; 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */; }; 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */; }; - 4994B5522A47C1C900E595B9 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */; }; 4994B5542A47C4FF00E595B9 /* NormalMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5532A47C4FF00E595B9 /* NormalMenuItemView.swift */; }; - 4994B5562A47C5FA00E595B9 /* Hotfixs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5552A47C5FA00E595B9 /* Hotfixs.swift */; }; 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */; }; 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */; }; 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485722ED715200F6C675 /* RemoteConfigModel.swift */; }; @@ -184,10 +182,7 @@ 4989F98D20D0AE990001E564 /* sampleConfig.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = sampleConfig.yaml; sourceTree = ""; }; 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTabViewController.swift; sourceTree = ""; }; 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingViewController.swift; sourceTree = ""; }; - 4994B5502A47C1C900E595B9 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+Safe.h"; sourceTree = ""; }; - 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+Safe.m"; sourceTree = ""; }; 4994B5532A47C4FF00E595B9 /* NormalMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalMenuItemView.swift; sourceTree = ""; }; - 4994B5552A47C5FA00E595B9 /* Hotfixs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hotfixs.swift; sourceTree = ""; }; 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWebViewContoller.swift; sourceTree = ""; }; 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigViewController.swift; sourceTree = ""; }; 499A485722ED715200F6C675 /* RemoteConfigModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigModel.swift; sourceTree = ""; }; @@ -345,7 +340,6 @@ 491C250121BD561200AB5D44 /* Managers */, 491C250021BD55B900AB5D44 /* Utils */, 492C4868210EE6B9004554A0 /* ApiRequest.swift */, - 4994B5552A47C5FA00E595B9 /* Hotfixs.swift */, ); path = General; sourceTree = ""; @@ -385,7 +379,6 @@ 49722FDD211ED2A900650A41 /* Vendor */ = { isa = PBXGroup; children = ( - 4994B54F2A47C1A800E595B9 /* Safe */, 4929F683258CE07500A435F6 /* UserDefaultWrapper.swift */, F976275D23634F18000EDEFE /* LoginServiceKit */, 49722FE9211F338B00650A41 /* Witness */, @@ -440,15 +433,6 @@ path = Settings; sourceTree = ""; }; - 4994B54F2A47C1A800E595B9 /* Safe */ = { - isa = PBXGroup; - children = ( - 4994B5502A47C1C900E595B9 /* NSMutableArray+Safe.h */, - 4994B5512A47C1C900E595B9 /* NSMutableArray+Safe.m */, - ); - path = Safe; - sourceTree = ""; - }; 4997732220D251A60009B136 /* Basic */ = { isa = PBXGroup; children = ( @@ -832,7 +816,6 @@ 49B4575D244F4A2A00463C39 /* PrivilegedHelperManager.swift in Sources */, 49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */, 4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */, - 4994B5522A47C1C900E595B9 /* NSMutableArray+Safe.m in Sources */, F9E754D2239CC28D00CEE7CC /* NSAlert+Extension.swift in Sources */, 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */, 49D176A9235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift in Sources */, @@ -850,7 +833,6 @@ 499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */, F939724C23A4B33500FE5A3F /* ClashProvider.swift in Sources */, 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */, - 4994B5562A47C5FA00E595B9 /* Hotfixs.swift in Sources */, 49862FA0218418C600A1D5EC /* ClashRule.swift in Sources */, 49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */, 4929F684258CE07500A435F6 /* UserDefaultWrapper.swift in Sources */, diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index ca5e724ba..312fa6102 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -159,7 +159,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { setupNetworkNotifier() registCrashLogger() KeyboardShortCutManager.setup() - Hotfixs.applyMacOS14Hotfix(modeItem: proxyModeMenuItem) } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { diff --git a/ClashX/General/Hotfixs.swift b/ClashX/General/Hotfixs.swift deleted file mode 100644 index 9aa779942..000000000 --- a/ClashX/General/Hotfixs.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Hotfixs.swift -// ClashX -// -// Created by yicheng on 2023/6/25. -// Copyright © 2023 west2online. All rights reserved. -// - -import AppKit -import RxCocoa -import RxSwift - -enum Hotfixs { - private static var kvos = Set() - static func applyMacOS14Hotfix(modeItem: NSMenuItem) { - if #available(macOS 14.0, *) { - let itemView = NormalMenuItemView(modeItem.title, rightArrow: true) - - let observer = modeItem.observe(\.title) { item, _ in - itemView.label.stringValue = item.title - } - kvos.insert(observer) - modeItem.view = itemView - } - } -} diff --git a/ClashX/Vendor/Safe/NSMutableArray+Safe.h b/ClashX/Vendor/Safe/NSMutableArray+Safe.h deleted file mode 100644 index 9476f0d46..000000000 --- a/ClashX/Vendor/Safe/NSMutableArray+Safe.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// NSMutableArray+Safe.h -// ClashX -// -// Created by yicheng on 2023/6/25. -// Copyright © 2023 west2online. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NSMutableArray(Safe) - -@end - -NS_ASSUME_NONNULL_END diff --git a/ClashX/Vendor/Safe/NSMutableArray+Safe.m b/ClashX/Vendor/Safe/NSMutableArray+Safe.m deleted file mode 100644 index d3b3c378d..000000000 --- a/ClashX/Vendor/Safe/NSMutableArray+Safe.m +++ /dev/null @@ -1,76 +0,0 @@ -#import "NSMutableArray+Safe.h" -#import - -@implementation NSMutableArray(Safe) -+ (void)load { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - if (@available(macOS 14, *)) { - swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:)); - swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(replaceObjectAtIndex:withObject:), @selector(hookReplaceObjectAtIndex:withObject:)); - } - }); -} - - -- (id)hookObjectAtIndex:(NSUInteger)index { - @synchronized (self) { - if (index < self.count) { - return [self hookObjectAtIndex:index]; - } - if ([self.firstObject isKindOfClass:[NSNumber class]] || self.count == 0) { - return @(22); // menu height - } - return nil; - } -} - -- (void) hookReplaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { - @synchronized (self) { - if (index < self.count && anObject) { - [self hookReplaceObjectAtIndex:index withObject:anObject]; - } else { - if (!anObject) { - return; - } - if (index >= self.count) { - return; - } - } - } -} - - -void swizzleInstanceMethod(Class cls, SEL origSelector, SEL newSelector) -{ - if (!cls) { - return; - } - /* if current class not exist selector, then get super*/ - Method originalMethod = class_getInstanceMethod(cls, origSelector); - Method swizzledMethod = class_getInstanceMethod(cls, newSelector); - - /* add selector if not exist, implement append with method */ - if (class_addMethod(cls, - origSelector, - method_getImplementation(swizzledMethod), - method_getTypeEncoding(swizzledMethod)) ) { - /* replace class instance method, added if selector not exist */ - /* for class cluster , it always add new selector here */ - class_replaceMethod(cls, - newSelector, - method_getImplementation(originalMethod), - method_getTypeEncoding(originalMethod)); - - } else { - /* swizzleMethod maybe belong to super */ - class_replaceMethod(cls, - newSelector, - class_replaceMethod(cls, - origSelector, - method_getImplementation(swizzledMethod), - method_getTypeEncoding(swizzledMethod)), - method_getTypeEncoding(originalMethod)); - } -} -@end From 860c1f82b8c4da22da290c33b74edad63b011083 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:39:53 +0800 Subject: [PATCH 085/122] feat: reduce exp menu item --- ClashX/AppDelegate.swift | 75 +--- ClashX/Base.lproj/Main.storyboard | 246 +++++++++---- ClashX/General/ApiRequest.swift | 4 +- .../General/Managers/AutoUpgardeManager.swift | 41 +-- .../Managers/ClashResourceManager.swift | 15 +- ClashX/General/Managers/ConfigManager.swift | 18 - .../General/Managers/ConnectionManager.swift | 30 +- ClashX/General/Managers/MenuItemFactory.swift | 33 +- ClashX/General/Managers/Settings.swift | 22 +- .../General/Managers/SystemProxyManager.swift | 31 +- ClashX/General/Utils/JSBridgeHandler.swift | 14 - .../en.lproj/Localizable.strings | 32 -- .../zh-Hans.lproj/Localizable.strings | 32 -- .../zh-Hant.lproj/Localizable.strings | 32 -- .../Settings/DebugSettingViewController.swift | 28 ++ .../GeneralSettingViewController.swift | 12 + ClashX/zh-Hans.lproj/Main.strings | 303 ++++++++-------- ClashX/zh-Hant.lproj/Main.strings | 325 +++++++++--------- 18 files changed, 601 insertions(+), 692 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 312fa6102..b040454b1 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -45,11 +45,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet var apiPortMenuItem: NSMenuItem! @IBOutlet var ipMenuItem: NSMenuItem! @IBOutlet var remoteConfigAutoupdateMenuItem: NSMenuItem! - @IBOutlet var buildApiModeMenuitem: NSMenuItem! - @IBOutlet var showProxyGroupCurrentMenuItem: NSMenuItem! @IBOutlet var copyExportCommandMenuItem: NSMenuItem! @IBOutlet var copyExportCommandExternalMenuItem: NSMenuItem! - @IBOutlet var experimentalMenu: NSMenu! @IBOutlet var externalControlSeparator: NSMenuItem! var disposeBag = DisposeBag() @@ -105,7 +102,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { setupStatusMenuItemData() AppVersionUtil.showUpgradeAlert() ICloudManager.shared.setup() - setupExperimentalMenuItem() + + if WebPortalManager.hasWebProtal { + WebPortalManager.shared.addWebProtalMenuItem(&statusMenu) + } + RemoteControlManager.setupMenuItem(separator: externalControlSeparator) + AutoUpgardeManager.shared.setup() + AutoUpgardeManager.shared.setupCheckForUpdatesMenuItem(checkForUpdateMenuItem) // install proxy helper _ = ClashResourceManager.check() @@ -144,7 +147,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { Logger.log("initClashCore finish") setupData() runAfterConfigReload = { [weak self] in - if !ConfigManager.builtInApiMode { + if !Settings.builtInApiMode { self?.selectAllowLanWithMenory() } } @@ -495,7 +498,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { setUIPath(uiPath.goStringBuffer()) } - Logger.log("Trying start proxy, build-in mode: \(ConfigManager.builtInApiMode), allow lan: \(ConfigManager.allowConnectFromLan) custom port: \(Settings.proxyPort)") + Logger.log("Trying start proxy, build-in mode: \(Settings.builtInApiMode), allow lan: \(ConfigManager.allowConnectFromLan) custom port: \(Settings.proxyPort)") var apiAddr = "" if Settings.apiPort > 0 { @@ -505,7 +508,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { apiAddr = "127.0.0.1:\(Settings.apiPort)" } } - let startRes = run(ConfigManager.builtInApiMode.goObject(), + let startRes = run(Settings.builtInApiMode.goObject(), ConfigManager.allowConnectFromLan.goObject(), GoUint32(Settings.proxyPort), apiAddr.goStringBuffer())? @@ -582,26 +585,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } - func setupExperimentalMenuItem() { - ConnectionManager.addCloseOptionMenuItem(&experimentalMenu) - ClashResourceManager.addUpdateMMDBMenuItem(&experimentalMenu) - SystemProxyManager.shared.addDisableRestoreProxyMenuItem(&experimentalMenu) - MenuItemFactory.addExperimentalMenuItem(&experimentalMenu) - if WebPortalManager.hasWebProtal { - WebPortalManager.shared.addWebProtalMenuItem(&statusMenu) - } - AutoUpgardeManager.shared.setup() - AutoUpgardeManager.shared.addChanelMenuItem(&experimentalMenu) - AutoUpgardeManager.shared.setupCheckForUpdatesMenuItem(checkForUpdateMenuItem) - updateExperimentalFeatureStatus() - RemoteControlManager.setupMenuItem(separator: externalControlSeparator) - } - - func updateExperimentalFeatureStatus() { - buildApiModeMenuitem.state = ConfigManager.builtInApiMode ? .on : .off - showProxyGroupCurrentMenuItem.state = ConfigManager.shared.disableShowCurrentProxyInMenu ? .off : .on - } - @objc func resetProxySettingOnWakeupFromSleep() { guard !ConfigManager.shared.isProxySetByOtherVariable.value, ConfigManager.shared.proxyPortAutoSet else { return } @@ -837,44 +820,6 @@ extension AppDelegate { @IBAction func actionSetUpdateInterval(_ sender: Any) { RemoteConfigManager.showAdd() } - - @IBAction func actionSetUseApiMode(_ sender: Any) { - let alert = NSAlert() - alert.informativeText = NSLocalizedString("Need to Restart the ClashX to Take effect, Please start clashX manually", comment: "") - alert.addButton(withTitle: NSLocalizedString("Apply and Quit", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) - if alert.runModal() == .alertFirstButtonReturn { - ConfigManager.builtInApiMode = !ConfigManager.builtInApiMode - NSApp.terminate(nil) - } - } - - @IBAction func actionUpdateProxyGroupMenu(_ sender: Any) { - ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu - updateExperimentalFeatureStatus() - print("211") - MenuItemFactory.recreateProxyMenuItems() - } - - @IBAction func actionSetBenchmarkUrl(_ sender: Any) { - let alert = NSAlert() - let textfiled = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 20)) - textfiled.stringValue = ConfigManager.shared.benchMarkUrl - alert.messageText = NSLocalizedString("Benchmark", comment: "") - alert.accessoryView = textfiled - alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) - - if alert.runModal() == .alertFirstButtonReturn { - if textfiled.stringValue.isUrlVaild() { - ConfigManager.shared.benchMarkUrl = textfiled.stringValue - } else { - let err = NSAlert() - err.messageText = NSLocalizedString("URL is not valid", comment: "") - err.runModal() - } - } - } } // MARK: crash hanlder diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 352dcb500..94f28cd9d 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -39,14 +39,14 @@ - + - + - + @@ -97,7 +97,7 @@ - + @@ -254,7 +254,7 @@ - + @@ -287,7 +287,7 @@ - + @@ -319,6 +319,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -353,7 +416,7 @@ - + @@ -404,18 +467,21 @@ + + + @@ -435,13 +501,14 @@ + - + @@ -613,13 +680,11 @@ - - @@ -633,7 +698,6 @@ - @@ -728,13 +792,6 @@ - - - - - - - @@ -764,45 +821,27 @@ - + - + - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - + + + + + + + + @@ -1566,27 +1605,47 @@ - + - + - + - + + + - + @@ -1616,8 +1675,39 @@ + + + + + + + + + + + + + + + + + + + + + + - + @@ -1648,7 +1738,7 @@ - + @@ -1691,7 +1781,7 @@ - + @@ -1721,9 +1811,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -1731,6 +1850,10 @@ + + + + @@ -1738,6 +1861,10 @@ + + + + @@ -1758,7 +1885,10 @@ + + + diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index 27774276a..029e0ac52 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -77,7 +77,7 @@ class ApiRequest { if ConfigManager.shared.overrideApiURL != nil { return false } - return ConfigManager.builtInApiMode + return Settings.builtInApiMode } static func requestConfig(completeHandler: @escaping ((ClashConfig) -> Void)) { @@ -262,7 +262,7 @@ class ApiRequest { static func getProxyDelay(proxyName: String, callback: @escaping ((Int) -> Void)) { req("/proxies/\(proxyName.encoded)/delay", method: .get, - parameters: ["timeout": 5000, "url": ConfigManager.shared.benchMarkUrl]) + parameters: ["timeout": 5000, "url": Settings.benchMarkUrl]) .responseData { res in switch res.result { case let .success(value): diff --git a/ClashX/General/Managers/AutoUpgardeManager.swift b/ClashX/General/Managers/AutoUpgardeManager.swift index 664d0b65f..2608ed197 100644 --- a/ClashX/General/Managers/AutoUpgardeManager.swift +++ b/ClashX/General/Managers/AutoUpgardeManager.swift @@ -23,17 +23,6 @@ class AutoUpgardeManager: NSObject { } } - private lazy var menuItems: [Channel: NSMenuItem] = { - var items = [Channel: NSMenuItem]() - for channel in Channel.allCases { - let item = NSMenuItem(title: channel.title, action: #selector(didSelectUpgradeChannel(_:)), keyEquivalent: "") - item.target = self - item.tag = channel.rawValue - items[channel] = item - } - return items - }() - private var allowSelectChannel: Bool { return Bundle.main.object(forInfoDictionaryKey: "SUDisallowSelectChannel") as? Bool != true } @@ -49,31 +38,19 @@ class AutoUpgardeManager: NSObject { checkForUpdatesMenuItem?.action = #selector(SPUStandardUpdaterController.checkForUpdates(_:)) } - func addChanelMenuItem(_ menu: inout NSMenu) { - guard WebPortalManager.hasWebProtal == false, allowSelectChannel else { return } - let upgradeMenu = NSMenu(title: NSLocalizedString("Upgrade Channel", comment: "")) - for (_, item) in menuItems { - upgradeMenu.addItem(item) + func addChannelMenuItem(_ button: NSPopUpButton) { + for channel in Channel.allCases { + button.addItem(withTitle: channel.title) + button.lastItem?.tag = channel.rawValue } - - let upgradeMenuItem = NSMenuItem(title: NSLocalizedString("Upgrade Channel", comment: ""), action: nil, keyEquivalent: "") - upgradeMenuItem.submenu = upgradeMenu - menu.addItem(upgradeMenuItem) - updateDisplayStatus() + button.target = self + button.action = #selector(didselectChannel(sender:)) + button.selectItem(withTag: current.rawValue) } -} -extension AutoUpgardeManager { - @objc private func didSelectUpgradeChannel(_ menuItem: NSMenuItem) { - guard let channel = Channel(rawValue: menuItem.tag) else { return } + @objc func didselectChannel(sender: NSPopUpButton) { + guard let tag = sender.selectedItem?.tag, let channel = Channel(rawValue: tag) else { return } current = channel - updateDisplayStatus() - } - - private func updateDisplayStatus() { - for (channel, menuItem) in menuItems { - menuItem.state = channel == current ? .on : .off - } } } diff --git a/ClashX/General/Managers/ClashResourceManager.swift b/ClashX/General/Managers/ClashResourceManager.swift index 6b1406c25..f04ce5b93 100644 --- a/ClashX/General/Managers/ClashResourceManager.swift +++ b/ClashX/General/Managers/ClashResourceManager.swift @@ -62,13 +62,8 @@ class ClashResourceManager { } extension ClashResourceManager { - static func addUpdateMMDBMenuItem(_ menu: inout NSMenu) { - let item = NSMenuItem(title: NSLocalizedString("Update GEOIP Database", comment: ""), action: #selector(updateGeoIP), keyEquivalent: "") - item.target = self - menu.addItem(item) - } - @objc private static func updateGeoIP() { + static func updateGeoIP() { guard let url = showCustomAlert() else { return } AF.download(url, to: { (_, _) in let path = kConfigFolderPath.appending("/Country.mmdb") @@ -98,8 +93,12 @@ extension ClashResourceManager { let alert = NSAlert() alert.messageText = NSLocalizedString("Custom your GEOIP MMDB download address.", comment: "") let inputView = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24)) - inputView.placeholderString = "https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb" - inputView.stringValue = Settings.mmdbDownloadUrl + inputView.placeholderString = Settings.defaultMmdbDownloadUrl + if Settings.mmdbDownloadUrl.count > 0 { + inputView.stringValue = Settings.mmdbDownloadUrl + } else { + inputView.stringValue = Settings.defaultMmdbDownloadUrl + } alert.accessoryView = inputView alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) diff --git a/ClashX/General/Managers/ConfigManager.swift b/ClashX/General/Managers/ConfigManager.swift index bf972c3ab..1e9ab5174 100644 --- a/ClashX/General/Managers/ConfigManager.swift +++ b/ClashX/General/Managers/ConfigManager.swift @@ -94,12 +94,6 @@ class ConfigManager { let showNetSpeedIndicatorObservable = UserDefaults.standard.rx.observe(Bool.self, "showNetSpeedIndicator") - var benchMarkUrl: String = UserDefaults.standard.string(forKey: "benchMarkUrl") ?? "http://cp.cloudflare.com/generate_204" { - didSet { - UserDefaults.standard.set(benchMarkUrl, forKey: "benchMarkUrl") - } - } - static var apiUrl: String { if let override = shared.overrideApiURL { return override.absoluteString @@ -151,18 +145,6 @@ class ConfigManager { UserDefaults.standard.set(newValue.rawValue, forKey: "selectLoggingApiLevel") } } - - static var builtInApiMode = (UserDefaults.standard.object(forKey: "kBuiltInApiMode") as? Bool) ?? true { - didSet { - UserDefaults.standard.set(builtInApiMode, forKey: "kBuiltInApiMode") - } - } - - var disableShowCurrentProxyInMenu: Bool = UserDefaults.standard.object(forKey: "kSDisableShowCurrentProxyInMenu") as? Bool ?? !AppDelegate.isAboveMacOS14 { - didSet { - UserDefaults.standard.set(disableShowCurrentProxyInMenu, forKey: "kSDisableShowCurrentProxyInMenu") - } - } } extension ConfigManager { diff --git a/ClashX/General/Managers/ConnectionManager.swift b/ClashX/General/Managers/ConnectionManager.swift index 406eae53d..4ff6f1e72 100644 --- a/ClashX/General/Managers/ConnectionManager.swift +++ b/ClashX/General/Managers/ConnectionManager.swift @@ -8,25 +8,8 @@ import Cocoa -class ConnectionManager { - static var enableAutoClose = UserDefaults.standard.object(forKey: "ConnectionManager.enableAutoClose") as? Bool ?? true { - didSet { - UserDefaults.standard.set(enableAutoClose, forKey: "ConnectionManager.enableAutoClose") - } - } - - private static var closeMenuItem: NSMenuItem? - - static func addCloseOptionMenuItem(_ menu: inout NSMenu) { - let item = NSMenuItem(title: NSLocalizedString("Auto Close Connection", comment: ""), action: #selector(optionMenuItemTap(sender:)), keyEquivalent: "") - item.target = ConnectionManager.self - menu.addItem(item) - closeMenuItem = item - updateMenuItemStatus(item) - } - +enum ConnectionManager { static func closeConnection(for group: String) { - guard enableAutoClose else { return } ApiRequest.getConnections { conns in for conn in conns where conn.chains.contains(group) { ApiRequest.closeConnection(conn) @@ -38,14 +21,3 @@ class ConnectionManager { ApiRequest.closeAllConnection() } } - -extension ConnectionManager { - static func updateMenuItemStatus(_ item: NSMenuItem? = closeMenuItem) { - item?.state = enableAutoClose ? .on : .off - } - - @objc static func optionMenuItemTap(sender: NSMenuItem) { - enableAutoClose = !enableAutoClose - updateMenuItemStatus(sender) - } -} diff --git a/ClashX/General/Managers/MenuItemFactory.swift b/ClashX/General/Managers/MenuItemFactory.swift index f6974de21..77a21e7fa 100644 --- a/ClashX/General/Managers/MenuItemFactory.swift +++ b/ClashX/General/Managers/MenuItemFactory.swift @@ -13,11 +13,7 @@ import SwiftyJSON class MenuItemFactory { private static var cachedProxyData: ClashProxyResp? - static var useViewToRenderProxy: Bool = UserDefaults.standard.object(forKey: "useViewToRenderProxy") as? Bool ?? AppDelegate.isAboveMacOS152 { - didSet { - UserDefaults.standard.set(useViewToRenderProxy, forKey: "useViewToRenderProxy") - } - } + static let useViewToRenderProxy: Bool = AppDelegate.isAboveMacOS152 // MARK: - Public @@ -123,7 +119,7 @@ class MenuItemFactory { let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "") let selectedName = proxyGroup.now ?? "" - if !ConfigManager.shared.disableShowCurrentProxyInMenu { + if !Settings.disableShowCurrentProxyInMenu { menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName, hasLeftPadding: leftPadding) } let submenu = ProxyGroupMenu(title: proxyGroup.name) @@ -153,7 +149,7 @@ class MenuItemFactory { let proxyMap = proxyInfo.proxiesMap let selectedName = proxyGroup.now ?? "" let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "") - if !ConfigManager.shared.disableShowCurrentProxyInMenu { + if !Settings.disableShowCurrentProxyInMenu { menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName, hasLeftPadding: leftPadding) } let submenu = NSMenu(title: proxyGroup.name) @@ -188,7 +184,7 @@ class MenuItemFactory { let proxyMap = proxyInfo.proxiesMap let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "") - if !ConfigManager.shared.disableShowCurrentProxyInMenu { + if !Settings.disableShowCurrentProxyInMenu { menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: NSLocalizedString("Load Balance", comment: ""), hasLeftPadding: leftPadding, observeUpdate: false) } let submenu = ProxyGroupMenu(title: proxyGroup.name) @@ -231,27 +227,6 @@ class MenuItemFactory { } } -// MARK: - Experimental - -extension MenuItemFactory { - static func addExperimentalMenuItem(_ menu: inout NSMenu) { - let useViewRender = NSMenuItem(title: NSLocalizedString("Enhance proxy list render", comment: ""), action: #selector(optionUseViewRenderMenuItemTap(sender:)), keyEquivalent: "") - useViewRender.target = self - menu.addItem(useViewRender) - updateUseViewRenderMenuItem(useViewRender) - } - - static func updateUseViewRenderMenuItem(_ item: NSMenuItem) { - item.state = useViewToRenderProxy ? .on : .off - } - - @objc static func optionUseViewRenderMenuItemTap(sender: NSMenuItem) { - useViewToRenderProxy = !useViewToRenderProxy - updateUseViewRenderMenuItem(sender) - recreateProxyMenuItems() - } -} - // MARK: - Action extension MenuItemFactory { diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index 2f501f805..aab05fbbd 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -8,7 +8,8 @@ import Foundation enum Settings { - @UserDefault("mmdbDownloadUrl", defaultValue: "") + static let defaultMmdbDownloadUrl = "https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb" + @UserDefault("mmdbDownloadUrl", defaultValue: defaultMmdbDownloadUrl) static var mmdbDownloadUrl: String @UserDefault("filterInterface", defaultValue: true) @@ -64,4 +65,23 @@ enum Settings { @UserDefault("overrideConfigSecret", defaultValue: false) static var overrideConfigSecret: Bool + + @UserDefault("kBuiltInApiMode", defaultValue: true) + static var builtInApiMode: Bool + + static let disableShowCurrentProxyInMenu = !AppDelegate.isAboveMacOS14 + + static let defaultBenchmarkUrl = "http://cp.cloudflare.com/generate_204" + @UserDefault("benchMarkUrl", defaultValue: defaultBenchmarkUrl) + static var benchMarkUrl: String { + didSet { + if benchMarkUrl.count == 0 { + benchMarkUrl = defaultBenchmarkUrl + } + } + } + + @UserDefault("kDisableRestoreProxy", defaultValue: false) + static var disableRestoreProxy: Bool + } diff --git a/ClashX/General/Managers/SystemProxyManager.swift b/ClashX/General/Managers/SystemProxyManager.swift index ea476ca7c..6d3623d50 100644 --- a/ClashX/General/Managers/SystemProxyManager.swift +++ b/ClashX/General/Managers/SystemProxyManager.swift @@ -21,21 +21,12 @@ class SystemProxyManager: NSObject { } } - private var disableRestoreProxy: Bool { - get { - return UserDefaults.standard.bool(forKey: "kDisableRestoreProxy") - } - set { - UserDefaults.standard.set(newValue, forKey: "kDisableRestoreProxy") - } - } - private var helper: ProxyConfigRemoteProcessProtocol? { PrivilegedHelperManager.shared.helper() } func saveProxy() { - guard !disableRestoreProxy else { return } + guard !Settings.disableRestoreProxy else { return } Logger.log("saveProxy", level: .debug) helper?.getCurrentProxySetting({ [weak self] info in Logger.log("saveProxy done", level: .debug) @@ -82,7 +73,7 @@ class SystemProxyManager: NSObject { func disableProxy(port: Int, socksPort: Int, forceDisable: Bool = false, complete: (() -> Void)? = nil) { Logger.log("disableProxy", level: .debug) - if disableRestoreProxy || forceDisable { + if Settings.disableRestoreProxy || forceDisable { helper?.disableProxy(withFilterInterface: Settings.filterInterface) { error in if let error = error { Logger.log("disableProxy \(error)", level: .error) @@ -99,22 +90,4 @@ class SystemProxyManager: NSObject { complete?() }) } - - // MARK: - Expriment Menu Items - - func addDisableRestoreProxyMenuItem(_ menu: inout NSMenu) { - let item = NSMenuItem(title: NSLocalizedString("Disable Restore Proxy Setting", comment: ""), action: #selector(optionMenuItemTap(sender:)), keyEquivalent: "") - item.target = self - menu.addItem(item) - updateMenuItemStatus(item) - } - - func updateMenuItemStatus(_ item: NSMenuItem) { - item.state = disableRestoreProxy ? .on : .off - } - - @objc func optionMenuItemTap(sender: NSMenuItem) { - disableRestoreProxy = !disableRestoreProxy - updateMenuItemStatus(sender) - } } diff --git a/ClashX/General/Utils/JSBridgeHandler.swift b/ClashX/General/Utils/JSBridgeHandler.swift index 835d821a5..1415bbe36 100644 --- a/ClashX/General/Utils/JSBridgeHandler.swift +++ b/ClashX/General/Utils/JSBridgeHandler.swift @@ -48,20 +48,6 @@ class JsBridgeUtil { } } - bridge.registerHandler("getBreakConnections") { _, responseCallback in - responseCallback?(ConnectionManager.enableAutoClose) - } - - bridge.registerHandler("setBreakConnections") { anydata, responseCallback in - if let enable = anydata as? Bool { - ConnectionManager.enableAutoClose = enable - ConnectionManager.updateMenuItemStatus() - responseCallback?(true) - } else { - responseCallback?(false) - } - } - bridge.registerHandler("speedTest") { anydata, responseCallback in if let proxyName = anydata as? String { ApiRequest.getProxyDelay(proxyName: proxyName) { delay in diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 8a05ac9cd..2d5c16af5 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -7,9 +7,6 @@ /* No comment provided by engineer. */ "Apply and Quit" = "Apply and Quit"; -/* No comment provided by engineer. */ -"Auto Close Connection" = "Auto Close Connection"; - /* No comment provided by engineer. */ "Benchmark" = "Benchmark"; @@ -64,24 +61,15 @@ /* No comment provided by engineer. */ "Direct Mode" = "Direct Mode"; -/* No comment provided by engineer. */ -"Disable Restore Proxy Setting" = "Disable Restore Proxy Setting"; - /* No comment provided by engineer. */ "Download fail" = "Download fail"; -/* No comment provided by engineer. */ -"Enhance proxy list render" = "Enhance proxy list render"; - /* No comment provided by engineer. */ "fail" = "fail"; /* No comment provided by engineer. */ "Fail:" = "Fail:"; -/* No comment provided by engineer. */ -"fails: %@" = "fails: %@"; - /* No comment provided by engineer. */ "Global" = "Global"; @@ -109,8 +97,6 @@ /* No comment provided by engineer. */ "Load Balance" = "Load Balance"; -"Mode" = "Mode"; - /* No comment provided by engineer. */ "Need to Restart the ClashX to Take effect, Please start clashX manually" = "Need to Restart the ClashX to Take effect, Please start clashX manually"; @@ -180,18 +166,9 @@ /* No comment provided by engineer. */ "Rule Mode" = "Rule Mode"; -/* No comment provided by engineer. */ -"Script" = "Script"; - -/* No comment provided by engineer. */ -"Script Mode" = "Script Mode"; - /* No comment provided by engineer. */ "Should be a least 1 hour" = "Should be a least 1 hour"; -/* No comment provided by engineer. */ -"Show speedTest at top" = "Show speedTest at top"; - /* No comment provided by engineer. */ "Stable" = "Stable"; @@ -234,14 +211,5 @@ /* No comment provided by engineer. */ "Updating" = "Updating"; -/* No comment provided by engineer. */ -"Upgrade Channel" = "Upgrade Channel"; - -/* No comment provided by engineer. */ -"URL is not valid" = "URL is not valid"; - -/* No comment provided by engineer. */ -"Use iCloud" = "Use iCloud"; - /* No comment provided by engineer. */ "Use reload config to try reconnect." = "Use reload config to try reconnect."; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index e41e4e890..af34246b9 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -7,9 +7,6 @@ /* No comment provided by engineer. */ "Apply and Quit" = "应用并退出"; -/* No comment provided by engineer. */ -"Auto Close Connection" = "切换代理时中断连接"; - /* No comment provided by engineer. */ "Benchmark" = "延迟测速"; @@ -64,24 +61,15 @@ /* No comment provided by engineer. */ "Direct Mode" = "直接连接"; -/* No comment provided by engineer. */ -"Disable Restore Proxy Setting" = "关闭自动还原之前代理"; - /* No comment provided by engineer. */ "Download fail" = "下载失败"; -/* No comment provided by engineer. */ -"Enhance proxy list render" = "增强渲染代理列表"; - /* No comment provided by engineer. */ "fail" = "失败"; /* No comment provided by engineer. */ "Fail:" = "失败:"; -/* No comment provided by engineer. */ -"fails: %@" = "失败: %@"; - /* No comment provided by engineer. */ "Global" = "全局"; @@ -109,8 +97,6 @@ /* No comment provided by engineer. */ "Load Balance" = "负载均衡"; -"Mode" = "模式"; - /* No comment provided by engineer. */ "Need to Restart the ClashX to Take effect, Please start clashX manually" = "需要重启ClashX生效,请手动启动ClashX"; @@ -180,18 +166,9 @@ /* No comment provided by engineer. */ "Rule Mode" = "规则判断"; -/* No comment provided by engineer. */ -"Script" = "脚本"; - -/* No comment provided by engineer. */ -"Script Mode" = "脚本模式"; - /* No comment provided by engineer. */ "Should be a least 1 hour" = "至少需要1小时间隔"; -/* No comment provided by engineer. */ -"Show speedTest at top" = "顶端显示测速按钮"; - /* No comment provided by engineer. */ "Stable" = "Stable"; @@ -234,14 +211,5 @@ /* No comment provided by engineer. */ "Updating" = "更新中"; -/* No comment provided by engineer. */ -"Upgrade Channel" = "更新通道"; - -/* No comment provided by engineer. */ -"URL is not valid" = "链接不合法"; - -/* No comment provided by engineer. */ -"Use iCloud" = "使用iCloud"; - /* No comment provided by engineer. */ "Use reload config to try reconnect." = "使用重载配置文件按钮尝试重新连接"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 63cd2a50c..01fb198f8 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -7,9 +7,6 @@ /* No comment provided by engineer. */ "Apply and Quit" = "應用並退出"; -/* No comment provided by engineer. */ -"Auto Close Connection" = "切換代理時中斷連接"; - /* No comment provided by engineer. */ "Benchmark" = "延遲測速"; @@ -64,24 +61,15 @@ /* No comment provided by engineer. */ "Direct Mode" = "直接連接"; -/* No comment provided by engineer. */ -"Disable Restore Proxy Setting" = "關閉自動還原之前代理"; - /* No comment provided by engineer. */ "Download fail" = "下載失敗"; -/* No comment provided by engineer. */ -"Enhance proxy list render" = "增強渲染代理列表"; - /* No comment provided by engineer. */ "fail" = "失敗"; /* No comment provided by engineer. */ "Fail:" = "失敗:"; -/* No comment provided by engineer. */ -"fails: %@" = "失敗: %@"; - /* No comment provided by engineer. */ "Global" = "全局"; @@ -109,8 +97,6 @@ /* No comment provided by engineer. */ "Load Balance" = "負載均衡"; -"Mode" = "模式"; - /* No comment provided by engineer. */ "Need to Restart the ClashX to Take effect, Please start clashX manually" = "需要重啟ClashX生效,請手動啟動ClashX"; @@ -180,18 +166,9 @@ /* No comment provided by engineer. */ "Rule Mode" = "規則模式"; -/* No comment provided by engineer. */ -"Script" = "腳本"; - -/* No comment provided by engineer. */ -"Script Mode" = "腳本模式"; - /* No comment provided by engineer. */ "Should be a least 1 hour" = "應該至少 1 小時"; -/* No comment provided by engineer. */ -"Show speedTest at top" = "在頂部顯示 SpeedTest"; - /* No comment provided by engineer. */ "Stable" = "穩定"; @@ -234,14 +211,5 @@ /* No comment provided by engineer. */ "Updating" = "更新中"; -/* No comment provided by engineer. */ -"Upgrade Channel" = "升級通道"; - -/* No comment provided by engineer. */ -"URL is not valid" = "網址無效"; - -/* No comment provided by engineer. */ -"Use iCloud" = "使用 iCloud"; - /* No comment provided by engineer. */ "Use reload config to try reconnect." = "使用重新加載配置嘗試重新連接"; diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index e71f4eb0b..7086ddbcb 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -11,6 +11,9 @@ import RxSwift class DebugSettingViewController: NSViewController { @IBOutlet weak var swiftuiMenuBarButton: NSButton! + @IBOutlet weak var useBuiltinApiButton: NSButton! + @IBOutlet weak var revertProxyButton: NSButton! + @IBOutlet weak var updateChannelPopButton: NSPopUpButton! var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() @@ -18,6 +21,9 @@ class DebugSettingViewController: NSViewController { swiftuiMenuBarButton.rx.state.bind { state in Settings.useSwiftUiMenuBar = state == .on }.disposed(by: disposeBag) + useBuiltinApiButton.state = Settings.builtInApiMode ? .on:.off + revertProxyButton.state = Settings.disableRestoreProxy ? .off : .on + AutoUpgardeManager.shared.addChannelMenuItem(updateChannelPopButton) } @IBAction func actionUnInstallProxyHelper(_ sender: Any) { PrivilegedHelperManager.shared.removeInstallHelper() @@ -49,4 +55,26 @@ class DebugSettingViewController: NSViewController { UserDefaults.standard.synchronize() NSApplication.shared.terminate(self) } + + @IBAction func actionSetUseApiMode(_ sender: Any) { + let alert = NSAlert() + alert.informativeText = NSLocalizedString("Need to Restart the ClashX to Take effect, Please start clashX manually", comment: "") + alert.addButton(withTitle: NSLocalizedString("Apply and Quit", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + if alert.runModal() == .alertFirstButtonReturn { + Settings.builtInApiMode = !Settings.builtInApiMode + NSApp.terminate(nil) + } else { + useBuiltinApiButton.state = Settings.builtInApiMode ? .on:.off + } + } + + @IBAction func actionUpdateGeoipDb(_ sender: Any) { + ClashResourceManager.updateGeoIP() + } + + @IBAction func actionRevertProxy(_ sender: Any) { + Settings.disableRestoreProxy.toggle() + revertProxyButton.state = Settings.disableRestoreProxy ? .off : .on + } } diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 0a3acf438..63bf2b119 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -25,9 +25,13 @@ class GeneralSettingViewController: NSViewController { @IBOutlet weak var apiSecretOverrideButton: NSButton! + @IBOutlet weak var speedTestUrlField: NSTextField! + var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() + speedTestUrlField.stringValue = Settings.benchMarkUrl + speedTestUrlField.placeholderString = Settings.defaultBenchmarkUrl ignoreListTextView.string = Settings.proxyIgnoreList.joined(separator: ",") ignoreListTextView.rx .string.debounce(.milliseconds(500), scheduler: MainScheduler.instance) @@ -111,6 +115,14 @@ class GeneralSettingViewController: NSViewController { view.window?.makeFirstResponder(nil) } + override func viewWillDisappear() { + super.viewWillDisappear() + let url = speedTestUrlField.stringValue + if url.isUrlVaild() || url.count == 0 { + Settings.benchMarkUrl = url + } + } + @IBAction func actionResetIgnoreList(_ sender: Any) { ignoreListTextView.string = Settings.proxyIgnoreListDefaultValue.joined(separator: ",") Settings.proxyIgnoreList = Settings.proxyIgnoreListDefaultValue diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 82901ba07..64b79169e 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -1,30 +1,63 @@ /* Class = "NSMenuItem"; title = "ERROR"; ObjectID = "0iu-lB-eZN"; */ "0iu-lB-eZN.title" = "ERROR"; +/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ +"1d5-NL-UsJ.title" = "代理模式"; + +/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ +"1He-Eq-fSy.title" = "远程控制器"; + /* Class = "NSButtonCell"; title = "Update"; ObjectID = "2Rx-ih-aGW"; */ "2Rx-ih-aGW.title" = "更新"; /* Class = "NSMenuItem"; title = "Log level"; ObjectID = "3Da-fL-Mzr"; */ "3Da-fL-Mzr.title" = "日志等级"; +/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ +"3Om-yT-A83.label" = "调试"; + /* Class = "NSButtonCell"; title = "Add"; ObjectID = "51K-nB-xLS"; */ "51K-nB-xLS.title" = "添加"; +/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ +"5hn-k8-CWe.headerCell.title" = "Api Secret"; + +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ +"5ws-55-f1g.title" = "重置"; + /* Class = "NSViewController"; title = "Remote Configs"; ObjectID = "6WI-Hi-v9j"; */ "6WI-Hi-v9j.title" = "托管的配置文件"; /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "7G6-oO-vNs"; */ "7G6-oO-vNs.title" = "Text Cell"; +/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ +"7wl-vK-5JO.title" = "复制终端代理命令(外部IP)"; + /* Class = "NSMenuItem"; title = "Rule"; ObjectID = "89n-bD-JHk"; */ "89n-bD-JHk.title" = "规则判断"; /* Class = "NSMenuItem"; title = "Set as system proxy"; ObjectID = "8se-yr-wmp"; */ "8se-yr-wmp.title" = "设置为系统代理"; +/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ +"9gE-NX-2wJ.title" = "远程控制器"; + /* Class = "NSMenuItem"; title = "Ports"; ObjectID = "9i0-LH-x04"; */ "9i0-LH-x04.title" = "端口"; +/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ +"afj-4G-usr.title" = "打开日志文件夹"; + +/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ +"AMT-oc-r8A.title" = "其他"; + +/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ +"aSG-9A-eeG.title" = "助手程序"; + +/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ +"AY0-nP-cGT.title" = "移除助手程序"; + /* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */ "AYu-sK-qS6.title" = "Main Menu"; @@ -34,9 +67,27 @@ /* Class = "NSMenuItem"; title = "Start at login"; ObjectID = "B1J-XB-BiZ"; */ "B1J-XB-BiZ.title" = "开机启动"; +/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ +"B2w-4r-5Kh.title" = "删除"; + +/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "端口设置(重启应用生效)"; + +/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ +"BFE-Qq-B2H.title" = "代理设置"; + +/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ +"BRR-WK-aeP.title" = "远程控制器"; + +/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ +"c01-0L-1SQ.title" = "日志"; + /* Class = "NSTableColumn"; headerCell.title = "Url"; ObjectID = "C79-J5-30z"; */ "C79-J5-30z.headerCell.title" = "链接"; +/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ +"ckH-Er-PfX.title" = "Api秘钥"; + /* Class = "NSMenuItem"; title = "Benchmark"; ObjectID = "COu-UX-bww"; */ "COu-UX-bww.title" = "延迟测速"; @@ -46,6 +97,9 @@ /* Class = "NSMenuItem"; title = "Help"; ObjectID = "Dd9-2F-FVY"; */ "Dd9-2F-FVY.title" = "帮助"; +/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ +"dV6-4Z-2SO.title" = "开机启动"; + /* Class = "NSMenuItem"; title = "SILENT"; ObjectID = "dVr-Xp-C0C"; */ "dVr-Xp-C0C.title" = "SILENT"; @@ -55,9 +109,33 @@ /* Class = "NSMenuItem"; title = "Manage"; ObjectID = "Dwg-Qb-2AU"; */ "Dwg-Qb-2AU.title" = "管理"; +/* Class = "NSTextFieldCell"; title = "Proxy Benchmark Url:"; ObjectID = "e0M-xf-ovR"; */ +"e0M-xf-ovR.title" = "代理延迟测速链接"; + +/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ +"E8B-e5-K0A.title" = "允许局域网控制(不推荐)"; + +/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ +"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染 (MacOS 13+)"; + +/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ +"F1s-SF-dqX.title" = "在特定 WiFi SSID 下自动暂停"; + +/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ +"GGx-F2-7kE.title" = "请务必注意处理可能存在的快捷键冲突,全局快捷键将优先于普通快捷键。"; + +/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ +"Gnh-m8-PAz.title" = "Box"; + /* Class = "NSMenuItem"; title = "Remote config"; ObjectID = "h1C-R6-Y9w"; */ "h1C-R6-Y9w.title" = "托管配置"; +/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ +"h1H-7k-9HS.title" = "设置更新间隔"; + +/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ +"hlb-KQ-Fdr.title" = "管理"; + /* Class = "NSMenuItem"; title = "About"; ObjectID = "hUb-k9-TEf"; */ "hUb-k9-TEf.title" = "关于"; @@ -67,8 +145,8 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "id7-f0-u56"; */ "id7-f0-u56.title" = "Text Cell"; -/* Class = "NSMenuItem"; title = "Show current proxy in menu"; ObjectID = "j9o-36-NTd"; */ -"j9o-36-NTd.title" = "快捷展示策略组策略"; +/* Class = "NSButtonCell"; title = "Use Built-in clash api"; ObjectID = "IET-Bf-hGj"; */ +"IET-Bf-hGj.title" = "使用内置Clash接口"; /* Class = "NSMenuItem"; title = "API Connect Error"; ObjectID = "jGT-1M-xJu"; */ "jGT-1M-xJu.title" = "API Connect Error"; @@ -79,27 +157,72 @@ /* Class = "NSMenuItem"; title = "Config"; ObjectID = "JMV-Dy-CI0"; */ "JMV-Dy-CI0.title" = "配置"; +/* Class = "NSButtonCell"; title = "Revert to previous proxy on proxy disabled"; ObjectID = "JOF-yU-YHc"; */ +"JOF-yU-YHc.title" = "关闭代理时还原先前系统代理设置"; + +/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ +"jsL-HC-6ne.title" = "减少通知"; + +/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ +"kdV-Em-qBi.title" = "调试"; + +/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ +"kma-mp-ncL.title" = "通用"; + /* Class = "NSMenuItem"; title = "WARNING"; ObjectID = "ko2-Ir-DxA"; */ "ko2-Ir-DxA.title" = "WARNING"; +/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ +"krh-QF-pqZ.title" = "更多设置"; + +/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ +"LAj-p8-9gd.title" = "ClashX 设置"; + /* Class = "NSTableColumn"; headerCell.title = "Config Name"; ObjectID = "lRE-Xa-euB"; */ "lRE-Xa-euB.headerCell.title" = "配置文件名称"; +/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ +"Ltt-Vq-Hh1.label" = "通用"; + +/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ +"LW4-cA-3bB.title" = "覆盖配置文件设置"; + +/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ +"mbn-tK-UQa.title" = "Api 端口"; + +/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ +"NLT-FZ-48V.title" = "调试设置"; + +/* Class = "NSTextFieldCell"; title = "Update Channel"; ObjectID = "NLt-OM-k6i"; */ +"NLt-OM-k6i.title" = "更新通道"; + /* Class = "NSMenuItem"; title = "Direct"; ObjectID = "Np6-Pm-Lo3"; */ "Np6-Pm-Lo3.title" = "直接连接"; +/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ +"NPu-V9-f3r.title" = "忽略这些主机与域的代理设置"; + /* Class = "NSMenuItem"; title = "Quit"; ObjectID = "NXU-86-Eem"; */ "NXU-86-Eem.title" = "退出"; /* Class = "NSMenu"; title = "Help"; ObjectID = "ogW-pn-jeR"; */ "ogW-pn-jeR.title" = "帮助"; -/* Class = "NSMenuItem"; title = "Experimental"; ObjectID = "OLP-Uv-at6"; */ -"OLP-Uv-at6.title" = "试验性功能"; +/* Class = "NSTextFieldCell"; title = "Geo-IP Datebase"; ObjectID = "OMy-zu-iho"; */ +"OMy-zu-iho.title" = "Geo-IP数据库"; /* Class = "NSMenuItem"; title = "Check Update"; ObjectID = "p0T-J8-Emx"; */ "p0T-J8-Emx.title" = "检查更新"; +/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ +"p7q-KN-kIv.title" = "将配置文件存储在iCloud中"; + +/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ +"PF0-Gd-XbR.title" = "应用配置"; + +/* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ +"pHl-C4-fNt.title" = "更新"; + /* Class = "NSMenuItem"; title = "Reload config"; ObjectID = "q3G-VH-eyy"; */ "q3G-VH-eyy.title" = "重载配置文件"; @@ -109,11 +232,11 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RCv-zz-HKW"; */ "RCv-zz-HKW.title" = "Text Cell"; -/* Class = "NSMenuItem"; title = "Set benchmark url"; ObjectID = "rls-O1-mpQ"; */ -"rls-O1-mpQ.title" = "设置延迟测速链接"; +/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ +"s6y-wL-pnr.title" = "远程控制器"; -/* Class = "NSMenu"; title = "Experimental"; ObjectID = "sbS-Fj-gxn"; */ -"sbS-Fj-gxn.title" = "试验性功能"; +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ +"sfe-wu-UXp.title" = "使用英文逗号(,)分隔"; /* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ "tck-zU-JKQ.title" = "配置"; @@ -121,30 +244,45 @@ /* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ "tL1-bl-LXd.title" = "托管的配置文件:"; +/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ +"URV-fZ-bJf.title" = "iCloud"; + /* Class = "NSMenu"; title = "API Connect Error"; ObjectID = "UU2-uE-YB4"; */ "UU2-uE-YB4.title" = "API Connect Error"; +/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ +"uUA-LS-Hu8.title" = "代理端口"; + /* Class = "NSMenuItem"; title = "Allow connect from Lan"; ObjectID = "Vz8-7n-vx6"; */ "Vz8-7n-vx6.title" = "允许局域网连接"; +/* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ +"WkL-aX-66E.title" = "远程控制器允许你控制其他设备上的 Clash 状态。"; + +/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ +"wKZ-TE-sf8.title" = "全局快捷键"; + /* Class = "NSMenu"; title = "Log level"; ObjectID = "wqo-3T-4qO"; */ "wqo-3T-4qO.title" = "日志等级"; -/* Class = "NSMenuItem"; title = "Use built in api"; ObjectID = "xG5-B4-mlw"; */ -"xG5-B4-mlw.title" = "使用内置接口"; - /* Class = "NSMenuItem"; title = "Dashboard"; ObjectID = "XG6-2M-PNi"; */ "XG6-2M-PNi.title" = "控制台"; /* Class = "NSMenuItem"; title = "DEBUG"; ObjectID = "XIR-Go-fWA"; */ "XIR-Go-fWA.title" = "DEBUG"; +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ +"xnL-ma-vFo.title" = "使用英文逗号(,)分隔"; + /* Class = "NSTableColumn"; headerCell.title = "Update Time"; ObjectID = "xoc-hs-9qa"; */ "xoc-hs-9qa.headerCell.title" = "更新时间"; /* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "xxZ-9l-69m"; */ "xxZ-9l-69m.title" = "显示日志"; +/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ +"YF4-uZ-A0M.title" = "本地"; + /* Class = "NSButtonCell"; title = "Delete"; ObjectID = "yGD-AG-oYU"; */ "yGD-AG-oYU.title" = "删除"; @@ -154,152 +292,17 @@ /* Class = "NSMenuItem"; title = "Show network indicator"; ObjectID = "YIO-Vj-64f"; */ "YIO-Vj-64f.title" = "显示实时速率"; -/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ -"zwo-q5-k5N.title" = "内核版本"; - -/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ -"7wl-vK-5JO.title" = "复制终端代理命令(外部IP)"; - -/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ -"5hn-k8-CWe.headerCell.title" = "Api Secret"; - -/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ -"9gE-NX-2wJ.title" = "远程控制器"; - -/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ -"B2w-4r-5Kh.title" = "删除"; - -/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ -"ZcF-10-jsl.title" = "添加"; - -/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ -"s6y-wL-pnr.title" = "远程控制器"; - /* Class = "NSTableColumn"; headerCell.title = "Api Url"; ObjectID = "yO6-uZ-IRv"; */ "yO6-uZ-IRv.headerCell.title" = "Api Url"; -/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ -"1He-Eq-fSy.title" = "远程控制器"; - -/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ -"BRR-WK-aeP.title" = "远程控制器"; - -/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ -"hlb-KQ-Fdr.title" = "管理"; - -/* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ -"WkL-aX-66E.title" = "远程控制器允许你控制其他设备上的 Clash 状态。"; - -/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ -"h1H-7k-9HS.title" = "设置更新间隔"; - -/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ -"LAj-p8-9gd.title" = "ClashX 设置"; - -/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ -"Ltt-Vq-Hh1.label" = "通用"; - -/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ -"NPu-V9-f3r.title" = "忽略这些主机与域的代理设置"; - -/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ -"dV6-4Z-2SO.title" = "开机启动"; - -/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ -"kma-mp-ncL.title" = "通用"; - -/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ -"krh-QF-pqZ.title" = "更多设置"; - -/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ -"p7q-KN-kIv.title" = "将配置文件存储在iCloud中"; - -/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ -"jsL-HC-6ne.title" = "减少通知"; - -/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ -"sfe-wu-UXp.title" = "使用英文逗号(,)分隔"; - -/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ -"E8B-e5-K0A.title" = "允许局域网控制(不推荐)"; - -/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ -"bcR-rG-52F.title" = "端口设置(重启应用生效)"; - -/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ -"mbn-tK-UQa.title" = "Api 端口"; - -/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ -"uUA-LS-Hu8.title" = "代理端口"; - /* Class = "NSButtonCell"; title = "Reset"; ObjectID = "yXh-2Y-aTS"; */ "yXh-2Y-aTS.title" = "重置"; -/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ -"Gnh-m8-PAz.title" = "Box"; - -/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ -"F1s-SF-dqX.title" = "在特定 WiFi SSID 下自动暂停"; - -/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ -"xnL-ma-vFo.title" = "使用英文逗号(,)分隔"; - -/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ -"3Om-yT-A83.label" = "调试"; - -/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ -"NLT-FZ-48V.title" = "调试设置"; - -/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ -"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染 (MacOS 13+)"; - -/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ -"kdV-Em-qBi.title" = "调试"; - -/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ -"1d5-NL-UsJ.title" = "代理模式"; - -/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ -"AMT-oc-r8A.title" = "其他"; - -/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ -"BFE-Qq-B2H.title" = "代理设置"; - -/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ -"wKZ-TE-sf8.title" = "全局快捷键"; - -/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ -"GGx-F2-7kE.title" = "请务必注意处理可能存在的快捷键冲突,全局快捷键将优先于普通快捷键。"; - -/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ -"AY0-nP-cGT.title" = "移除助手程序"; - -/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ -"aSG-9A-eeG.title" = "助手程序"; - -/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ -"afj-4G-usr.title" = "打开日志文件夹"; - -/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ -"c01-0L-1SQ.title" = "日志"; - -/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ -"LW4-cA-3bB.title" = "覆盖配置文件设置"; - -/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ -"ckH-Er-PfX.title" = "Api秘钥"; - -/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ -"URV-fZ-bJf.title" = "iCloud"; - -/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ -"YF4-uZ-A0M.title" = "本地"; - /* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ "ZA9-qc-wi4.title" = "配置文件夹"; -/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ -"5ws-55-f1g.title" = "重置"; +/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ +"ZcF-10-jsl.title" = "添加"; -/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ -"PF0-Gd-XbR.title" = "应用配置"; +/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ +"zwo-q5-k5N.title" = "内核版本"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index e1842963e..4dcf56b68 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -1,54 +1,63 @@ -/* Class = "NSMenu"; title = "Experimental"; ObjectID = "sbS-Fj-gxn"; */ -"sbS-Fj-gxn.title" = "試驗性功能"; - -/* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ -"tck-zU-JKQ.title" = "配置"; - -/* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ -"tL1-bl-LXd.title" = "託管的配置文件:"; - -/* Class = "NSMenu"; title = "API Connect Error"; ObjectID = "UU2-uE-YB4"; */ -"UU2-uE-YB4.title" = "API Connect Error"; - -/* Class = "NSMenuItem"; title = "Allow connect from Lan"; ObjectID = "Vz8-7n-vx6"; */ -"Vz8-7n-vx6.title" = "允許局域網連接"; - -/* Class = "NSMenu"; title = "Log level"; ObjectID = "wqo-3T-4qO"; */ -"wqo-3T-4qO.title" = "日誌等級"; - -/* Class = "NSMenuItem"; title = "Use built in api"; ObjectID = "xG5-B4-mlw"; */ -"xG5-B4-mlw.title" = "使用內置接口"; - -/* Class = "NSMenuItem"; title = "Dashboard"; ObjectID = "XG6-2M-PNi"; */ -"XG6-2M-PNi.title" = "控制台"; - /* Class = "NSMenuItem"; title = "ERROR"; ObjectID = "0iu-lB-eZN"; */ "0iu-lB-eZN.title" = "錯誤"; +/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ +"1d5-NL-UsJ.title" = "模式"; + +/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ +"1He-Eq-fSy.title" = "遙控"; + /* Class = "NSButtonCell"; title = "Update"; ObjectID = "2Rx-ih-aGW"; */ "2Rx-ih-aGW.title" = "更新"; /* Class = "NSMenuItem"; title = "Log level"; ObjectID = "3Da-fL-Mzr"; */ "3Da-fL-Mzr.title" = "日誌等級"; +/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ +"3Om-yT-A83.label" = "調試"; + /* Class = "NSButtonCell"; title = "Add"; ObjectID = "51K-nB-xLS"; */ "51K-nB-xLS.title" = "添加"; +/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ +"5hn-k8-CWe.headerCell.title" = "Api Secret"; + +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ +"5ws-55-f1g.title" = "重置"; + /* Class = "NSViewController"; title = "Remote Configs"; ObjectID = "6WI-Hi-v9j"; */ "6WI-Hi-v9j.title" = "託管的配置文件"; /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "7G6-oO-vNs"; */ "7G6-oO-vNs.title" = "文本單元格"; +/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ +"7wl-vK-5JO.title" = "複製shell命令(外網IP)"; + /* Class = "NSMenuItem"; title = "Rule"; ObjectID = "89n-bD-JHk"; */ "89n-bD-JHk.title" = "規則判斷"; /* Class = "NSMenuItem"; title = "Set as system proxy"; ObjectID = "8se-yr-wmp"; */ "8se-yr-wmp.title" = "設定為系統代理"; +/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ +"9gE-NX-2wJ.title" = "外部控制"; + /* Class = "NSMenuItem"; title = "Ports"; ObjectID = "9i0-LH-x04"; */ "9i0-LH-x04.title" = "端口"; +/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ +"afj-4G-usr.title" = "打開日誌文件夾"; + +/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ +"AMT-oc-r8A.title" = "其他"; + +/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ +"aSG-9A-eeG.title" = "代理助手"; + +/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ +"AY0-nP-cGT.title" = "卸載代理程式"; + /* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */ "AYu-sK-qS6.title" = "主菜單"; @@ -58,9 +67,27 @@ /* Class = "NSMenuItem"; title = "Start at login"; ObjectID = "B1J-XB-BiZ"; */ "B1J-XB-BiZ.title" = "開機啟動"; +/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ +"B2w-4r-5Kh.title" = "刪除"; + +/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "端口設定"; + +/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ +"BFE-Qq-B2H.title" = "代理"; + +/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ +"BRR-WK-aeP.title" = "遙控"; + +/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ +"c01-0L-1SQ.title" = "日誌"; + /* Class = "NSTableColumn"; headerCell.title = "Url"; ObjectID = "C79-J5-30z"; */ "C79-J5-30z.headerCell.title" = "鏈接"; +/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ +"ckH-Er-PfX.title" = "API 秘鑰"; + /* Class = "NSMenuItem"; title = "Benchmark"; ObjectID = "COu-UX-bww"; */ "COu-UX-bww.title" = "延遲測速"; @@ -70,6 +97,9 @@ /* Class = "NSMenuItem"; title = "Help"; ObjectID = "Dd9-2F-FVY"; */ "Dd9-2F-FVY.title" = "幫助"; +/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ +"dV6-4Z-2SO.title" = "登錄時啟動"; + /* Class = "NSMenuItem"; title = "SILENT"; ObjectID = "dVr-Xp-C0C"; */ "dVr-Xp-C0C.title" = "SILENT"; @@ -79,9 +109,33 @@ /* Class = "NSMenuItem"; title = "Manage"; ObjectID = "Dwg-Qb-2AU"; */ "Dwg-Qb-2AU.title" = "管理"; +/* Class = "NSTextFieldCell"; title = "Proxy Benchmark Url:"; ObjectID = "e0M-xf-ovR"; */ +"e0M-xf-ovR.title" = "代理延遲測速連結"; + +/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ +"E8B-e5-K0A.title" = "允許局域網設備"; + +/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ +"eY9-1i-i7P.title" = "使用 SwiftUI 渲染狀態欄圖標"; + +/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ +"F1s-SF-dqX.title" = "SSID 掛起"; + +/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ +"GGx-F2-7kE.title" = "請確保解決任何潛在的快捷方式衝突,全局快捷方式優先於常規快捷方式"; + +/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ +"Gnh-m8-PAz.title" = "盒子"; + /* Class = "NSMenuItem"; title = "Remote config"; ObjectID = "h1C-R6-Y9w"; */ "h1C-R6-Y9w.title" = "託管配置"; +/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ +"h1H-7k-9HS.title" = "設定更新間隔"; + +/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ +"hlb-KQ-Fdr.title" = "管理"; + /* Class = "NSMenuItem"; title = "About"; ObjectID = "hUb-k9-TEf"; */ "hUb-k9-TEf.title" = "關於"; @@ -91,8 +145,8 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "id7-f0-u56"; */ "id7-f0-u56.title" = "Text Cell"; -/* Class = "NSMenuItem"; title = "Show current proxy in menu"; ObjectID = "j9o-36-NTd"; */ -"j9o-36-NTd.title" = "快捷展示策略組策略"; +/* Class = "NSButtonCell"; title = "Use Built-in clash api"; ObjectID = "IET-Bf-hGj"; */ +"IET-Bf-hGj.title" = "使用內建Clash接口"; /* Class = "NSMenuItem"; title = "API Connect Error"; ObjectID = "jGT-1M-xJu"; */ "jGT-1M-xJu.title" = "API Connect Error"; @@ -103,27 +157,72 @@ /* Class = "NSMenuItem"; title = "Config"; ObjectID = "JMV-Dy-CI0"; */ "JMV-Dy-CI0.title" = "配置"; +/* Class = "NSButtonCell"; title = "Revert to previous proxy on proxy disabled"; ObjectID = "JOF-yU-YHc"; */ +"JOF-yU-YHc.title" = "關閉代理時還原先前系統代理設置"; + +/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ +"jsL-HC-6ne.title" = "減少通知"; + +/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ +"kdV-Em-qBi.title" = "調試"; + +/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ +"kma-mp-ncL.title" = "一般設定"; + /* Class = "NSMenuItem"; title = "WARNING"; ObjectID = "ko2-Ir-DxA"; */ "ko2-Ir-DxA.title" = "WARNING"; +/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ +"krh-QF-pqZ.title" = "設定"; + +/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ +"LAj-p8-9gd.title" = "設定"; + /* Class = "NSTableColumn"; headerCell.title = "Config Name"; ObjectID = "lRE-Xa-euB"; */ "lRE-Xa-euB.headerCell.title" = "配置文件名稱"; +/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ +"Ltt-Vq-Hh1.label" = "一般設定"; + +/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ +"LW4-cA-3bB.title" = "覆蓋配置文件設置"; + +/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ +"mbn-tK-UQa.title" = "API 端口"; + +/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ +"NLT-FZ-48V.title" = "調試設定"; + +/* Class = "NSTextFieldCell"; title = "Update Channel"; ObjectID = "NLt-OM-k6i"; */ +"NLt-OM-k6i.title" = "更新通道"; + /* Class = "NSMenuItem"; title = "Direct"; ObjectID = "Np6-Pm-Lo3"; */ "Np6-Pm-Lo3.title" = "直接連接"; +/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ +"NPu-V9-f3r.title" = "繞過這些主機和域的代理設定"; + /* Class = "NSMenuItem"; title = "Quit"; ObjectID = "NXU-86-Eem"; */ "NXU-86-Eem.title" = "退出"; /* Class = "NSMenu"; title = "Help"; ObjectID = "ogW-pn-jeR"; */ "ogW-pn-jeR.title" = "幫助"; -/* Class = "NSMenuItem"; title = "Experimental"; ObjectID = "OLP-Uv-at6"; */ -"OLP-Uv-at6.title" = "試驗性功能"; +/* Class = "NSTextFieldCell"; title = "Geo-IP Datebase"; ObjectID = "OMy-zu-iho"; */ +"OMy-zu-iho.title" = "Geo-IP資料庫"; /* Class = "NSMenuItem"; title = "Check Update"; ObjectID = "p0T-J8-Emx"; */ "p0T-J8-Emx.title" = "檢查更新"; +/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ +"p7q-KN-kIv.title" = "使用iCloud 存儲配置文件"; + +/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ +"PF0-Gd-XbR.title" = "應用配置"; + +/* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ +"pHl-C4-fNt.title" = "更新"; + /* Class = "NSMenuItem"; title = "Reload config"; ObjectID = "q3G-VH-eyy"; */ "q3G-VH-eyy.title" = "重載配置文件"; @@ -133,131 +232,44 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RCv-zz-HKW"; */ "RCv-zz-HKW.title" = "Text Cell"; -/* Class = "NSMenuItem"; title = "Set benchmark url"; ObjectID = "rls-O1-mpQ"; */ -"rls-O1-mpQ.title" = "設定延遲測速鏈接"; - -/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ -"1He-Eq-fSy.title" = "遙控"; - -/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ -"1d5-NL-UsJ.title" = "模式"; - -/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ -"3Om-yT-A83.label" = "調試"; - -/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ -"5hn-k8-CWe.headerCell.title" = "Api Secret"; - -/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ -"7wl-vK-5JO.title" = "複製shell命令(外網IP)"; - -/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ -"9gE-NX-2wJ.title" = "外部控制"; - -/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ -"AMT-oc-r8A.title" = "其他"; - -/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ -"AY0-nP-cGT.title" = "卸載代理程式"; - -/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ -"B2w-4r-5Kh.title" = "刪除"; - -/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ -"BFE-Qq-B2H.title" = "代理"; - -/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ -"BRR-WK-aeP.title" = "遙控"; - -/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ -"E8B-e5-K0A.title" = "允許局域網設備"; +/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ +"s6y-wL-pnr.title" = "外部管理"; -/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ -"F1s-SF-dqX.title" = "SSID 掛起"; +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ +"sfe-wu-UXp.title" = "以逗號(,)分隔"; -/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ -"GGx-F2-7kE.title" = "請確保解決任何潛在的快捷方式衝突,全局快捷方式優先於常規快捷方式"; +/* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ +"tck-zU-JKQ.title" = "配置"; -/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ -"Gnh-m8-PAz.title" = "盒子"; +/* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ +"tL1-bl-LXd.title" = "託管的配置文件:"; -/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ -"LAj-p8-9gd.title" = "設定"; +/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ +"URV-fZ-bJf.title" = "iCloud"; -/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ -"Ltt-Vq-Hh1.label" = "一般設定"; +/* Class = "NSMenu"; title = "API Connect Error"; ObjectID = "UU2-uE-YB4"; */ +"UU2-uE-YB4.title" = "API Connect Error"; -/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ -"NLT-FZ-48V.title" = "調試設定"; +/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ +"uUA-LS-Hu8.title" = "代理端口"; -/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ -"NPu-V9-f3r.title" = "繞過這些主機和域的代理設定"; +/* Class = "NSMenuItem"; title = "Allow connect from Lan"; ObjectID = "Vz8-7n-vx6"; */ +"Vz8-7n-vx6.title" = "允許局域網連接"; /* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ "WkL-aX-66E.title" = "這允許你控制在不同機器上運行的衝突核心"; -/* Class = "NSMenuItem"; title = "DEBUG"; ObjectID = "XIR-Go-fWA"; */ -"XIR-Go-fWA.title" = "調試"; - -/* Class = "NSMenuItem"; title = "Show network indicator"; ObjectID = "YIO-Vj-64f"; */ -"YIO-Vj-64f.title" = "顯示網絡指示器"; - -/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ -"ZcF-10-jsl.title" = "添加"; - -/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ -"aSG-9A-eeG.title" = "代理助手"; - -/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ -"afj-4G-usr.title" = "打開日誌文件夾"; - -/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ -"bcR-rG-52F.title" = "端口設定"; - -/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ -"c01-0L-1SQ.title" = "日誌"; - -/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ -"dV6-4Z-2SO.title" = "登錄時啟動"; - -/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ -"eY9-1i-i7P.title" = "使用 SwiftUI 渲染狀態欄圖標"; - -/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ -"h1H-7k-9HS.title" = "設定更新間隔"; - -/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ -"hlb-KQ-Fdr.title" = "管理"; - -/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ -"jsL-HC-6ne.title" = "減少通知"; - -/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ -"kdV-Em-qBi.title" = "調試"; - -/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ -"kma-mp-ncL.title" = "一般設定"; - -/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ -"krh-QF-pqZ.title" = "設定"; - -/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ -"mbn-tK-UQa.title" = "API 端口"; - -/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ -"p7q-KN-kIv.title" = "使用iCloud 存儲配置文件"; - -/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ -"s6y-wL-pnr.title" = "外部管理"; +/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ +"wKZ-TE-sf8.title" = "全局快捷方式"; -/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ -"sfe-wu-UXp.title" = "以逗號(,)分隔"; +/* Class = "NSMenu"; title = "Log level"; ObjectID = "wqo-3T-4qO"; */ +"wqo-3T-4qO.title" = "日誌等級"; -/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ -"uUA-LS-Hu8.title" = "代理端口"; +/* Class = "NSMenuItem"; title = "Dashboard"; ObjectID = "XG6-2M-PNi"; */ +"XG6-2M-PNi.title" = "控制台"; -/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ -"wKZ-TE-sf8.title" = "全局快捷方式"; +/* Class = "NSMenuItem"; title = "DEBUG"; ObjectID = "XIR-Go-fWA"; */ +"XIR-Go-fWA.title" = "調試"; /* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ "xnL-ma-vFo.title" = "以逗號(,)分隔"; @@ -268,38 +280,29 @@ /* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "xxZ-9l-69m"; */ "xxZ-9l-69m.title" = "顯示日誌"; +/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ +"YF4-uZ-A0M.title" = "本地"; + /* Class = "NSButtonCell"; title = "Delete"; ObjectID = "yGD-AG-oYU"; */ "yGD-AG-oYU.title" = "刪除"; -/* Class = "NSTableColumn"; headerCell.title = "Api Url"; ObjectID = "yO6-uZ-IRv"; */ -"yO6-uZ-IRv.headerCell.title" = "API 網址"; - -/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "yXh-2Y-aTS"; */ -"yXh-2Y-aTS.title" = "重置"; - /* Class = "NSMenuItem"; title = "Global"; ObjectID = "yiM-U4-MNg"; */ "yiM-U4-MNg.title" = "全局"; -/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ -"zwo-q5-k5N.title" = "核心版本"; - -/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ -"LW4-cA-3bB.title" = "覆蓋配置文件設置"; - -/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ -"ckH-Er-PfX.title" = "API 秘鑰"; +/* Class = "NSMenuItem"; title = "Show network indicator"; ObjectID = "YIO-Vj-64f"; */ +"YIO-Vj-64f.title" = "顯示網絡指示器"; -/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ -"URV-fZ-bJf.title" = "iCloud"; +/* Class = "NSTableColumn"; headerCell.title = "Api Url"; ObjectID = "yO6-uZ-IRv"; */ +"yO6-uZ-IRv.headerCell.title" = "API 網址"; -/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ -"YF4-uZ-A0M.title" = "本地"; +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "yXh-2Y-aTS"; */ +"yXh-2Y-aTS.title" = "重置"; /* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ "ZA9-qc-wi4.title" = "配置文件夾"; -/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ -"5ws-55-f1g.title" = "重置"; +/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ +"ZcF-10-jsl.title" = "新增"; -/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ -"PF0-Gd-XbR.title" = "應用配置"; +/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ +"zwo-q5-k5N.title" = "核心版本"; From 425044b20da10c87197fed0c9ae30a16101f3ff5 Mon Sep 17 00:00:00 2001 From: Yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:16:15 +0800 Subject: [PATCH 086/122] feat: add new native connection panel (#1181) * feat: add base connection window * feat: listen to clash connection api and get process information * misc: add top list tableview * fix: get no-app process * feat: add status icon * feat: add detail view * misc: init left pannel view * misc: update window background * feat: support top tableview sort * feat: support left pannelView search * feat: update left panel bg color and apply application filter * feat: add top searchfield * feat: add host and sourceIp list datasource * misc: unify window controller * feat: support speed sort * misc: add connection translations * feat: add left pannel view section display. * misc: Rename ConnectionApplicationClientCellView to ApplicationClientCellView.swift * feat: Refactor SectionedTableView.swift * feat: update left panel view * feat: add right click to close conn * feat: support sort by host * feat: add top segment control * feat: add active connection support * misc: Rename `ConnectionsLeftPannelView` to `ConnectionLeftPannelView` * misc: add parent controller for ConnectionsViewController * misc: update localization * misc: recover failed request from log to connections * misc: filter application by path * misc: swiftlint enable empty_count rule * feat: add global shortcut for connections * fix: unknown filter * feat: add close button * misc: fix multi selection * fix: remove force unwarp * misc: fix beta ci [beta] --------- Co-authored-by: miniLV --- .bartycrouch.toml | 1 - .github/workflows/main.yml | 28 +- .swiftlint.yml | 4 + ClashX.xcodeproj/project.pbxproj | 150 +++- ClashX/AppDelegate.swift | 23 +- ClashX/Base.lproj/Main.storyboard | 7 + ClashX/Basic/Combine+Ext.swift | 22 + ClashX/Basic/SpeedUtils.swift | 33 + ClashX/Basic/String+Extension.swift | 2 +- ClashX/ClashWindowController.swift | 73 ++ ClashX/ClashX-Bridging-Header.h | 2 + ClashX/Extensions/DateFormatter+.swift | 6 + .../NSUserNotificationCenter+Extension.swift | 4 +- ClashX/General/ApiRequest.swift | 14 +- .../Managers/ClashResourceManager.swift | 6 +- .../General/Managers/ConnectionManager.swift | 2 +- ClashX/General/Managers/ICloudManager.swift | 4 +- ClashX/General/Managers/MenuItemFactory.swift | 4 +- .../Managers/RemoteControlManager.swift | 4 +- ClashX/General/Managers/Settings.swift | 2 +- ClashX/Images.xcassets/Contents.json | 6 + .../Contents.json | 22 + .../icon_connected.png | Bin 0 -> 498 bytes .../icon_connected@2x.png | Bin 0 -> 990 bytes .../Contents.json | 22 + .../icon_connection_fail.png | Bin 0 -> 471 bytes .../icon_connection_fail@2x.png | Bin 0 -> 896 bytes .../Contents.json | 22 + .../icon_connecting.png | Bin 0 -> 461 bytes .../icon_connecting@2x.png | Bin 0 -> 939 bytes ClashX/Info.plist | 2 +- ClashX/Models/ClashConnection.swift | 153 +++- ClashX/Models/ClashProxy.swift | 2 +- ClashX/Models/SavedProxyModel.swift | 3 - .../en.lproj/Localizable.strings | 69 ++ .../zh-Hans.lproj/Localizable.strings | 69 ++ .../zh-Hant.lproj/Localizable.strings | 69 ++ .../ClashWebViewContoller.swift | 55 -- .../ConnectionsViewController.swift | 220 ++++++ .../DashboardSubViewControllerProtocol.swift | 13 + .../Connections/DashboardViewController.swift | 129 ++++ .../Connections/Requests/ConnectionsReq.swift | 54 ++ .../Connections/Requests/StructedLogReq.swift | 146 ++++ .../ConnectionDetailViewModel.swift | 93 +++ .../ConnectionLeftPannelViewModel.swift | 113 +++ .../ConnectionTopListViewModel.swift | 123 +++ .../ViewModels/ConnectionsViewModel.swift | 190 +++++ .../ConnectionDetailInfoGeneralView.xib | 713 ++++++++++++++++++ .../Views/Cell/ConnectionCellProtocol.swift | 15 + .../Cell/ConnectionLeftTextCellView.swift | 113 +++ .../Cell/ConnectionProxyClientCellView.swift | 50 ++ .../Cell/ConnectionStatusIconCellView.swift | 49 ++ .../Views/Cell/ConnectionTextCellView.swift | 65 ++ .../Connections/Views/ConnectionColume.swift | 79 ++ .../ConnectionDetailInfoGeneralView.swift | 32 + .../Views/ConnectionDetailInfoView.swift | 136 ++++ .../Views/ConnectionLeftPannelView.swift | 194 +++++ .../Views/ConnectionTopListView.swift | 127 ++++ .../Views/SectionedTableView.swift | 149 ++++ .../ConnectionDetailInfoGeneralView.strings | 53 ++ .../ConnectionDetailInfoGeneralView.strings | 53 ++ .../ExternalControlViewController.swift | 2 +- .../RemoteConfigViewController.swift | 8 +- .../GeneralSettingViewController.swift | 2 +- .../GlobalShortCutViewController.swift | 14 +- ClashX/Views/ProxyDelayHistoryMenu.swift | 2 +- .../Views/ProxyGroupSpeedTestMenuItem.swift | 2 +- .../Views/StatusItem/NewStatusItemView.swift | 4 +- ClashX/Views/StatusItem/StatusItemTool.swift | 17 - ClashX/Views/StatusItem/StatusItemView.swift | 4 +- ClashX/goClash/go.mod | 2 +- ClashX/goClash/main.go | 15 + ClashX/goClash/proccess.go | 108 +++ ClashX/zh-Hans.lproj/Main.strings | 317 ++++---- ClashX/zh-Hant.lproj/Main.strings | 335 ++++---- fastlane/Fastfile | 19 - .../swiftFormate.sh => updateLocalization.sh | 0 77 files changed, 4164 insertions(+), 481 deletions(-) create mode 100644 ClashX/Basic/Combine+Ext.swift create mode 100644 ClashX/Basic/SpeedUtils.swift create mode 100644 ClashX/ClashWindowController.swift create mode 100644 ClashX/Images.xcassets/Contents.json create mode 100644 ClashX/Images.xcassets/icon_connection_done.imageset/Contents.json create mode 100644 ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected.png create mode 100644 ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected@2x.png create mode 100644 ClashX/Images.xcassets/icon_connection_fail.imageset/Contents.json create mode 100644 ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail.png create mode 100644 ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail@2x.png create mode 100644 ClashX/Images.xcassets/icon_connection_inprogress.imageset/Contents.json create mode 100644 ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting.png create mode 100644 ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting@2x.png create mode 100644 ClashX/ViewControllers/Connections/ConnectionsViewController.swift create mode 100644 ClashX/ViewControllers/Connections/DashboardSubViewControllerProtocol.swift create mode 100644 ClashX/ViewControllers/Connections/DashboardViewController.swift create mode 100644 ClashX/ViewControllers/Connections/Requests/ConnectionsReq.swift create mode 100644 ClashX/ViewControllers/Connections/Requests/StructedLogReq.swift create mode 100644 ClashX/ViewControllers/Connections/ViewModels/ConnectionDetailViewModel.swift create mode 100644 ClashX/ViewControllers/Connections/ViewModels/ConnectionLeftPannelViewModel.swift create mode 100644 ClashX/ViewControllers/Connections/ViewModels/ConnectionTopListViewModel.swift create mode 100644 ClashX/ViewControllers/Connections/ViewModels/ConnectionsViewModel.swift create mode 100644 ClashX/ViewControllers/Connections/Views/Base.lproj/ConnectionDetailInfoGeneralView.xib create mode 100644 ClashX/ViewControllers/Connections/Views/Cell/ConnectionCellProtocol.swift create mode 100644 ClashX/ViewControllers/Connections/Views/Cell/ConnectionLeftTextCellView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/Cell/ConnectionProxyClientCellView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/Cell/ConnectionStatusIconCellView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/ConnectionColume.swift create mode 100644 ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoGeneralView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/ConnectionLeftPannelView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/SectionedTableView.swift create mode 100644 ClashX/ViewControllers/Connections/Views/zh-Hans.lproj/ConnectionDetailInfoGeneralView.strings create mode 100644 ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings create mode 100644 ClashX/goClash/proccess.go rename ClashX/swiftFormate.sh => updateLocalization.sh (100%) diff --git a/.bartycrouch.toml b/.bartycrouch.toml index c4fc663ae..4d58d51c8 100644 --- a/.bartycrouch.toml +++ b/.bartycrouch.toml @@ -22,7 +22,6 @@ translateMethodName = "translate" [update.normalize] path = "./ClashX" -sourceLocale = "zh-Hans" harmonizeWithSource = true sortByKeys = true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 818f3e4e3..de27237e0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,6 @@ name: ClashX -on: [ push ] +on: [ push, workflow_dispatch ] env: FASTLANE_SKIP_UPDATE_CHECK: true @@ -9,7 +9,11 @@ jobs: build: runs-on: macos-13 steps: - - uses: actions/checkout@v3 + + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: import certs run: | echo `/usr/bin/xcodebuild -version` @@ -27,16 +31,19 @@ jobs: run: | bash install_dependency.sh - - name: update beta build version - if: contains(github.event.head_commit.message, '[beta]') && !startsWith(github.ref, 'refs/tags/') + - name: update dev build version + if: ${{!startsWith(github.ref, 'refs/tags/')}} run: | - bundle exec fastlane beta - bundle exec fastlane run set_info_plist_value path:ClashX/Info.plist key:BETA value:YES + tag=`git describe --abbrev=0`.`date '+%m%d%H%M%S'` + bundle exec fastlane run increment_build_number_in_plist build_number:"${tag}" scheme:"ClashX" + bundle exec fastlane run increment_version_number_in_plist version_number:"${tag}" scheme:"ClashX" + bundle exec fastlane run set_info_plist_value path:ClashX/Info.plist key:BETA value:true - name: update tag build version if: startsWith(github.ref, 'refs/tags/') run: | tag=${GITHUB_REF##*/} + bundle exec fastlane run set_info_plist_value path:ClashX/Info.plist key:BETA value:false bundle exec fastlane run increment_build_number_in_plist build_number:"${tag}" scheme:"ClashX" bundle exec fastlane run increment_version_number_in_plist version_number:"${tag}" scheme:"ClashX" @@ -53,20 +60,20 @@ jobs: echo "Check done" - name: setup node - if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[appcenter]') + if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[beta]') uses: actions/setup-node@v3 with: node-version: '18.x' - name: create dmg - if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[appcenter]') + if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[beta]') run: | npm install --global create-dmg create-dmg ClashX.app mv ClashX*.dmg ClashX.dmg - name: notarize - if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[notarize]') + if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[beta]') env: FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} FASTLANE_USER: ${{ secrets.FASTLANE_USER }} @@ -75,7 +82,7 @@ jobs: bundle exec fastlane run notarize package:"./ClashX.dmg" bundle_id:"com.west2online.ClashX" asc_provider:MEWHFZ92DY - name: upload to appcenter - if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[appcenter]') + if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[beta]') env: APPCENTER_DISTRIBUTE_UPLOAD_BUILD_ONLY: true APPCENTER_DISTRIBUTE_DESTINATIONS: Public @@ -84,6 +91,7 @@ jobs: APPCENTER_DISTRIBUTE_FILE: ClashX.dmg APPCENTER_OWNER_NAME: ${{ secrets.APPCENTER_OWNER_NAME }} APPCENTER_DISTRIBUTE_DSYM: "ClashX.app.dSYM.zip" + APPCENTER_DISTRIBUTE_RELEASE_NOTES: ${{ github.event.head_commit.message }} run: | appversion=`defaults read \`pwd\`/ClashX.app/Contents/Info.plist CFBundleShortVersionString` buildVersion=`defaults read \`pwd\`/ClashX.app/Contents/Info.plist CFBundleVersion` diff --git a/.swiftlint.yml b/.swiftlint.yml index 0d972988d..c9c456e03 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -9,6 +9,10 @@ disabled_rules: # rule identifiers turned on by default to exclude from running - type_body_length - cyclomatic_complexity - function_body_length + - nesting +opt_in_rules: # some rules are turned off by default, so you need to opt-in + - empty_count + - empty_string included: # paths to include during linting. `--path` is ignored if present. - ClashX excluded: # paths to ignore during linting. Takes precedence over `included`. diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index bb53cae6c..b63d24548 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 0AD7506B2A5B9A04001FFBBD /* ConnectionLeftPannelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD7506A2A5B9A04001FFBBD /* ConnectionLeftPannelViewModel.swift */; }; + 0AD7506D2A5B9B26001FFBBD /* ConnectionLeftTextCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD7506C2A5B9B26001FFBBD /* ConnectionLeftTextCellView.swift */; }; + 0AEF6EAF2A5F961B00EFEE23 /* SectionedTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEF6EAE2A5F961B00EFEE23 /* SectionedTableView.swift */; }; 4905A2C52A2058B000AEDA2E /* GlobalShortCutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4905A2C42A2058B000AEDA2E /* GlobalShortCutViewController.swift */; }; 4905A2C82A2058D400AEDA2E /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */; }; 4905A2CA2A20841B00AEDA2E /* NSView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */; }; @@ -34,6 +37,7 @@ 4960A6DB2136529200B940C9 /* JSBridgeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */; }; 4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */; }; 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E5211824F300A391FB /* NSImage+extension.swift */; }; + 4969E73D2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4969E73F2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib */; }; 496BDEE021196F1E00C5207F /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496BDEDF21196F1E00C5207F /* Logger.swift */; }; 49722FEF211F338B00650A41 /* FileEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49722FEA211F338B00650A41 /* FileEvent.swift */; }; 49722FF0211F338B00650A41 /* EventStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49722FEB211F338B00650A41 /* EventStream.swift */; }; @@ -46,6 +50,14 @@ 4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98D20D0AE990001E564 /* sampleConfig.yaml */; }; 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */; }; 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */; }; + 498D87D82A617D2200CD456F /* DashboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498D87D72A617D2200CD456F /* DashboardViewController.swift */; }; + 498D87DA2A617E9900CD456F /* DashboardSubViewControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498D87D92A617E9900CD456F /* DashboardSubViewControllerProtocol.swift */; }; + 4991D2282A5646D200978143 /* ConnectionProxyClientCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4991D2272A5646D200978143 /* ConnectionProxyClientCellView.swift */; }; + 4991D22A2A56472300978143 /* ConnectionCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4991D2292A56472300978143 /* ConnectionCellProtocol.swift */; }; + 4991D22C2A56478400978143 /* ConnectionColume.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4991D22B2A56478400978143 /* ConnectionColume.swift */; }; + 4991D22E2A564A4200978143 /* ConnectionTextCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4991D22D2A564A4200978143 /* ConnectionTextCellView.swift */; }; + 4991D2302A564DDC00978143 /* SpeedUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4991D22F2A564DDC00978143 /* SpeedUtils.swift */; }; + 4991D2322A565E6A00978143 /* Combine+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4991D2312A565E6A00978143 /* Combine+Ext.swift */; }; 4994B5542A47C4FF00E595B9 /* NormalMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4994B5532A47C4FF00E595B9 /* NormalMenuItemView.swift */; }; 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */; }; 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */; }; @@ -63,7 +75,16 @@ 49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */; }; 49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BB31E6246853EA008A4CB0 /* ICloudManager.swift */; }; 49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BC061B212931F4005A0FE7 /* AboutViewController.swift */; }; + 49C9AFEC2A592C5D00178BB4 /* ConnectionDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9AFEB2A592C5D00178BB4 /* ConnectionDetailViewModel.swift */; }; + 49C9AFF22A59366200178BB4 /* ConnectionDetailInfoGeneralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9AFF12A59366200178BB4 /* ConnectionDetailInfoGeneralView.swift */; }; + 49C9AFF42A593B8800178BB4 /* ConnectionTopListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9AFF32A593B8800178BB4 /* ConnectionTopListViewModel.swift */; }; 49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */; }; + 49CCDA282A54F8DA00FF1E13 /* ConnectionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CCDA272A54F8DA00FF1E13 /* ConnectionsViewController.swift */; }; + 49CCDA2A2A54F9AC00FF1E13 /* ClashWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CCDA292A54F9AC00FF1E13 /* ClashWindowController.swift */; }; + 49CCDA2D2A54FBA600FF1E13 /* ConnectionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CCDA2C2A54FBA600FF1E13 /* ConnectionsViewModel.swift */; }; + 49CCDA302A54FC3300FF1E13 /* ConnectionLeftPannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CCDA2F2A54FC3300FF1E13 /* ConnectionLeftPannelView.swift */; }; + 49CCDA322A5506D100FF1E13 /* ConnectionTopListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CCDA312A5506D100FF1E13 /* ConnectionTopListView.swift */; }; + 49CCDA362A55126A00FF1E13 /* ConnectionDetailInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CCDA352A55126A00FF1E13 /* ConnectionDetailInfoView.swift */; }; 49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */; }; 49CF3B2820CD7465001EBF94 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49CF3B2620CD7465001EBF94 /* Main.storyboard */; }; 49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B5B20CE8068001EBF94 /* ClashResourceManager.swift */; }; @@ -75,6 +96,9 @@ 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45129AEEC15006487EF /* StatusItemTool.swift */; }; 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */; }; 49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */; }; + 49D767742A6195C800830333 /* ConnectionsReq.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D767732A6195C800830333 /* ConnectionsReq.swift */; }; + 49D767762A6195E600830333 /* StructedLogReq.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D767752A6195E600830333 /* StructedLogReq.swift */; }; + 49D84AD32A56E9760074CCDB /* ConnectionStatusIconCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D84AD22A56E9760074CCDB /* ConnectionStatusIconCellView.swift */; }; 8A2BBEA727A03ACB0081EBEF /* ProxySetting.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */; }; 8ACD21BB27A04C7800BC4632 /* ProxySettingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */; }; 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BC27A04ED500BC4632 /* ProxyModeChangeCommand.swift */; }; @@ -138,6 +162,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0AD7506A2A5B9A04001FFBBD /* ConnectionLeftPannelViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionLeftPannelViewModel.swift; sourceTree = ""; }; + 0AD7506C2A5B9B26001FFBBD /* ConnectionLeftTextCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionLeftTextCellView.swift; sourceTree = ""; }; + 0AEF6EAE2A5F961B00EFEE23 /* SectionedTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedTableView.swift; sourceTree = ""; }; 4905A2C42A2058B000AEDA2E /* GlobalShortCutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalShortCutViewController.swift; sourceTree = ""; }; 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSView+Layout.swift"; sourceTree = ""; }; 4908087A29F8F3FF007A4944 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; }; @@ -165,6 +192,9 @@ 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSBridgeHandler.swift; sourceTree = ""; }; 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserNotificationCenter+Extension.swift"; sourceTree = ""; }; 4966E9E5211824F300A391FB /* NSImage+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+extension.swift"; sourceTree = ""; }; + 4969E73E2A5E3CB20012E005 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ConnectionDetailInfoGeneralView.xib; sourceTree = ""; }; + 4969E7412A5E3CB80012E005 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ConnectionDetailInfoGeneralView.strings"; sourceTree = ""; }; + 4969E7432A5E3CB90012E005 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings"; sourceTree = ""; }; 496BDEDF21196F1E00C5207F /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 496C16462A3418C80052064A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = ""; }; 496C16472A3418C80052064A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; @@ -182,6 +212,14 @@ 4989F98D20D0AE990001E564 /* sampleConfig.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = sampleConfig.yaml; sourceTree = ""; }; 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTabViewController.swift; sourceTree = ""; }; 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingViewController.swift; sourceTree = ""; }; + 498D87D72A617D2200CD456F /* DashboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardViewController.swift; sourceTree = ""; }; + 498D87D92A617E9900CD456F /* DashboardSubViewControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardSubViewControllerProtocol.swift; sourceTree = ""; }; + 4991D2272A5646D200978143 /* ConnectionProxyClientCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionProxyClientCellView.swift; sourceTree = ""; }; + 4991D2292A56472300978143 /* ConnectionCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionCellProtocol.swift; sourceTree = ""; }; + 4991D22B2A56478400978143 /* ConnectionColume.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionColume.swift; sourceTree = ""; }; + 4991D22D2A564A4200978143 /* ConnectionTextCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionTextCellView.swift; sourceTree = ""; }; + 4991D22F2A564DDC00978143 /* SpeedUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpeedUtils.swift; sourceTree = ""; }; + 4991D2312A565E6A00978143 /* Combine+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Combine+Ext.swift"; sourceTree = ""; }; 4994B5532A47C4FF00E595B9 /* NormalMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalMenuItemView.swift; sourceTree = ""; }; 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWebViewContoller.swift; sourceTree = ""; }; 499A485322ED707300F6C675 /* RemoteConfigViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigViewController.swift; sourceTree = ""; }; @@ -199,7 +237,16 @@ 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrivilegedHelperManager+Legacy.swift"; sourceTree = ""; }; 49BB31E6246853EA008A4CB0 /* ICloudManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICloudManager.swift; sourceTree = ""; }; 49BC061B212931F4005A0FE7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; + 49C9AFEB2A592C5D00178BB4 /* ConnectionDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailViewModel.swift; sourceTree = ""; }; + 49C9AFF12A59366200178BB4 /* ConnectionDetailInfoGeneralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailInfoGeneralView.swift; sourceTree = ""; }; + 49C9AFF32A593B8800178BB4 /* ConnectionTopListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionTopListViewModel.swift; sourceTree = ""; }; 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashProxy.swift; sourceTree = ""; }; + 49CCDA272A54F8DA00FF1E13 /* ConnectionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsViewController.swift; sourceTree = ""; }; + 49CCDA292A54F9AC00FF1E13 /* ClashWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWindowController.swift; sourceTree = ""; }; + 49CCDA2C2A54FBA600FF1E13 /* ConnectionsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsViewModel.swift; sourceTree = ""; }; + 49CCDA2F2A54FC3300FF1E13 /* ConnectionLeftPannelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionLeftPannelView.swift; sourceTree = ""; }; + 49CCDA312A5506D100FF1E13 /* ConnectionTopListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionTopListView.swift; sourceTree = ""; }; + 49CCDA352A55126A00FF1E13 /* ConnectionDetailInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailInfoView.swift; sourceTree = ""; }; 49CF3B1D20CD7463001EBF94 /* ClashX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClashX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 49CF3B2720CD7465001EBF94 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -215,8 +262,11 @@ 49D6A45129AEEC15006487EF /* StatusItemTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemTool.swift; sourceTree = ""; }; 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusItemView.swift; sourceTree = ""; }; 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemViewProtocol.swift; sourceTree = ""; }; + 49D767732A6195C800830333 /* ConnectionsReq.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsReq.swift; sourceTree = ""; }; + 49D767752A6195E600830333 /* StructedLogReq.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StructedLogReq.swift; sourceTree = ""; }; 49D8276627E9B01700159D93 /* LoginKitWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginKitWrapper.h; sourceTree = ""; }; 49D8276727E9B01700159D93 /* LoginKitWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginKitWrapper.m; sourceTree = ""; }; + 49D84AD22A56E9760074CCDB /* ConnectionStatusIconCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionStatusIconCellView.swift; sourceTree = ""; }; 5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.debug.xcconfig"; sourceTree = ""; }; 8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = ProxySetting.sdef; sourceTree = ""; }; 8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxySettingCommand.swift; sourceTree = ""; }; @@ -413,6 +463,7 @@ 4989F98520D0AA300001E564 /* ViewControllers */ = { isa = PBXGroup; children = ( + 49CCDA2B2A54FA8900FF1E13 /* Connections */, 498BC2512929CC0A00CA8084 /* Settings */, 49BC061B212931F4005A0FE7 /* AboutViewController.swift */, 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */, @@ -433,6 +484,18 @@ path = Settings; sourceTree = ""; }; + 4991D2262A56467600978143 /* Cell */ = { + isa = PBXGroup; + children = ( + 4991D2272A5646D200978143 /* ConnectionProxyClientCellView.swift */, + 4991D2292A56472300978143 /* ConnectionCellProtocol.swift */, + 4991D22D2A564A4200978143 /* ConnectionTextCellView.swift */, + 49D84AD22A56E9760074CCDB /* ConnectionStatusIconCellView.swift */, + 0AD7506C2A5B9B26001FFBBD /* ConnectionLeftTextCellView.swift */, + ); + path = Cell; + sourceTree = ""; + }; 4997732220D251A60009B136 /* Basic */ = { isa = PBXGroup; children = ( @@ -442,6 +505,8 @@ 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */, 49B10869216A356D0064FFCE /* String+Extension.swift */, 49ABB748236B0F9E00535CD7 /* UnsafePointer+bridge.swift */, + 4991D22F2A564DDC00978143 /* SpeedUtils.swift */, + 4991D2312A565E6A00978143 /* Combine+Ext.swift */, ); path = Basic; sourceTree = ""; @@ -458,6 +523,45 @@ path = StatusItem; sourceTree = ""; }; + 49C9AFF52A596ABA00178BB4 /* ViewModels */ = { + isa = PBXGroup; + children = ( + 49CCDA2C2A54FBA600FF1E13 /* ConnectionsViewModel.swift */, + 49C9AFEB2A592C5D00178BB4 /* ConnectionDetailViewModel.swift */, + 49C9AFF32A593B8800178BB4 /* ConnectionTopListViewModel.swift */, + 0AD7506A2A5B9A04001FFBBD /* ConnectionLeftPannelViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + 49CCDA2B2A54FA8900FF1E13 /* Connections */ = { + isa = PBXGroup; + children = ( + 49D767722A6195BE00830333 /* Requests */, + 49C9AFF52A596ABA00178BB4 /* ViewModels */, + 49CCDA2E2A54FC1900FF1E13 /* Views */, + 49CCDA272A54F8DA00FF1E13 /* ConnectionsViewController.swift */, + 498D87D72A617D2200CD456F /* DashboardViewController.swift */, + 498D87D92A617E9900CD456F /* DashboardSubViewControllerProtocol.swift */, + ); + path = Connections; + sourceTree = ""; + }; + 49CCDA2E2A54FC1900FF1E13 /* Views */ = { + isa = PBXGroup; + children = ( + 4991D2262A56467600978143 /* Cell */, + 4991D22B2A56478400978143 /* ConnectionColume.swift */, + 49C9AFF12A59366200178BB4 /* ConnectionDetailInfoGeneralView.swift */, + 4969E73F2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib */, + 49CCDA352A55126A00FF1E13 /* ConnectionDetailInfoView.swift */, + 49CCDA2F2A54FC3300FF1E13 /* ConnectionLeftPannelView.swift */, + 49CCDA312A5506D100FF1E13 /* ConnectionTopListView.swift */, + 0AEF6EAE2A5F961B00EFEE23 /* SectionedTableView.swift */, + ); + path = Views; + sourceTree = ""; + }; 49CF3B1420CD7463001EBF94 = { isa = PBXGroup; children = ( @@ -498,6 +602,7 @@ 49CF3B2A20CD7465001EBF94 /* ClashX.entitlements */, 49CF3B3520CD75DF001EBF94 /* ClashX-Bridging-Header.h */, F9FAB31D262BE04800DE02A6 /* Images.xcassets */, + 49CCDA292A54F9AC00FF1E13 /* ClashWindowController.swift */, ); path = ClashX; sourceTree = ""; @@ -512,6 +617,15 @@ path = "Support Files"; sourceTree = ""; }; + 49D767722A6195BE00830333 /* Requests */ = { + isa = PBXGroup; + children = ( + 49D767732A6195C800830333 /* ConnectionsReq.swift */, + 49D767752A6195E600830333 /* StructedLogReq.swift */, + ); + path = Requests; + sourceTree = ""; + }; 76229F122B00E935D126742A /* Pods */ = { isa = PBXGroup; children = ( @@ -669,6 +783,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4969E73D2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib in Resources */, 49761DA721C9497000AE13EF /* dashboard in Resources */, 4929F677258CD89B00A435F6 /* Country.mmdb.gz in Resources */, 4981C88B216BAE4A008CC14A /* Localizable.strings in Resources */, @@ -737,7 +852,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/SwiftLint/swiftlint --fix\n"; + shellScript = "${PODS_ROOT}/SwiftLint/swiftlint --fix && ${PODS_ROOT}/SwiftLint/swiftlint\n"; }; A741C26F5755233F0D7CEC6F /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -776,20 +891,30 @@ F977FAAE23669D6400C17F1F /* ConnectionManager.swift in Sources */, 495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */, F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */, + 0AD7506D2A5B9B26001FFBBD /* ConnectionLeftTextCellView.swift in Sources */, 495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */, + 4991D2322A565E6A00978143 /* Combine+Ext.swift in Sources */, 49281C802A1F01FA00F60935 /* DebugSettingViewController.swift in Sources */, + 49D767762A6195E600830333 /* StructedLogReq.swift in Sources */, + 49CCDA2A2A54F9AC00FF1E13 /* ClashWindowController.swift in Sources */, 4929F67F258CE04700A435F6 /* Settings.swift in Sources */, 49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */, 493AEAE3221AE3420016FE98 /* AppVersionUtil.swift in Sources */, 49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */, + 49CCDA2D2A54FBA600FF1E13 /* ConnectionsViewModel.swift in Sources */, + 49C9AFEC2A592C5D00178BB4 /* ConnectionDetailViewModel.swift in Sources */, + 4991D2302A564DDC00978143 /* SpeedUtils.swift in Sources */, 496BDEE021196F1E00C5207F /* Logger.swift in Sources */, 4905A2CA2A20841B00AEDA2E /* NSView+Layout.swift in Sources */, 49722FEF211F338B00650A41 /* FileEvent.swift in Sources */, 49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */, + 49D84AD32A56E9760074CCDB /* ConnectionStatusIconCellView.swift in Sources */, 4905A2C52A2058B000AEDA2E /* GlobalShortCutViewController.swift in Sources */, 4913C82321157D0200F6B87C /* Notification.swift in Sources */, 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */, 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */, + 498D87D82A617D2200CD456F /* DashboardViewController.swift in Sources */, + 4991D22E2A564A4200978143 /* ConnectionTextCellView.swift in Sources */, 49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */, F9203A26236342820020D57D /* AppDelegate+..swift in Sources */, 499A485C22ED793C00F6C675 /* NSView+Nib.swift in Sources */, @@ -806,22 +931,33 @@ 499A486522EEA3FD00F6C675 /* Array+Safe.swift in Sources */, F92D0B24236BC12000575E15 /* SavedProxyModel.swift in Sources */, F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */, + 49CCDA302A54FC3300FF1E13 /* ConnectionLeftPannelView.swift in Sources */, 49D223392A1DA5F10002FFCB /* SSIDSuspendTool.swift in Sources */, F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */, + 4991D22A2A56472300978143 /* ConnectionCellProtocol.swift in Sources */, 4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */, 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */, + 49D767742A6195C800830333 /* ConnectionsReq.swift in Sources */, + 49C9AFF22A59366200178BB4 /* ConnectionDetailInfoGeneralView.swift in Sources */, F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */, 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */, + 49C9AFF42A593B8800178BB4 /* ConnectionTopListViewModel.swift in Sources */, + 498D87DA2A617E9900CD456F /* DashboardSubViewControllerProtocol.swift in Sources */, 49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */, 49B4575D244F4A2A00463C39 /* PrivilegedHelperManager.swift in Sources */, + 4991D2282A5646D200978143 /* ConnectionProxyClientCellView.swift in Sources */, + 4991D22C2A56478400978143 /* ConnectionColume.swift in Sources */, 49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */, 4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */, F9E754D2239CC28D00CEE7CC /* NSAlert+Extension.swift in Sources */, 499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */, + 49CCDA282A54F8DA00FF1E13 /* ConnectionsViewController.swift in Sources */, + 49CCDA322A5506D100FF1E13 /* ConnectionTopListView.swift in Sources */, 49D176A9235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift in Sources */, F976275C23634DF8000EDEFE /* LoginServiceKit.swift in Sources */, 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */, 4994B5542A47C4FF00E595B9 /* NormalMenuItemView.swift in Sources */, + 0AD7506B2A5B9A04001FFBBD /* ConnectionLeftPannelViewModel.swift in Sources */, F92D0B2E236D35C000575E15 /* ProxyItemView.swift in Sources */, 49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */, 49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */, @@ -832,8 +968,10 @@ 493AEAE5221AE7230016FE98 /* ProxyMenuItem.swift in Sources */, 499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */, F939724C23A4B33500FE5A3F /* ClashProvider.swift in Sources */, + 0AEF6EAF2A5F961B00EFEE23 /* SectionedTableView.swift in Sources */, 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */, 49862FA0218418C600A1D5EC /* ClashRule.swift in Sources */, + 49CCDA362A55126A00FF1E13 /* ConnectionDetailInfoView.swift in Sources */, 49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */, 4929F684258CE07500A435F6 /* UserDefaultWrapper.swift in Sources */, F9E8F34623A12B89002DE5E8 /* String+Encode.swift in Sources */, @@ -863,6 +1001,16 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 4969E73F2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib */ = { + isa = PBXVariantGroup; + children = ( + 4969E73E2A5E3CB20012E005 /* Base */, + 4969E7412A5E3CB80012E005 /* zh-Hans */, + 4969E7432A5E3CB90012E005 /* zh-Hant */, + ); + name = ConnectionDetailInfoGeneralView.xib; + sourceTree = ""; + }; 4981C88D216BAE4A008CC14A /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index b040454b1..cc6b04d46 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -48,6 +48,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet var copyExportCommandMenuItem: NSMenuItem! @IBOutlet var copyExportCommandExternalMenuItem: NSMenuItem! @IBOutlet var externalControlSeparator: NSMenuItem! + @IBOutlet var connectionsMenuItem: NSMenuItem! var disposeBag = DisposeBag() var statusItemView: StatusItemViewProtocol! @@ -55,8 +56,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { var runAfterConfigReload: (() -> Void)? - var dashboardWindowController: ClashWebViewWindowController? - func applicationWillFinishLaunching(_ notification: Notification) { Logger.log("applicationWillFinishLaunching") signal(SIGPIPE, SIG_IGN) @@ -98,6 +97,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { if #unavailable(macOS 10.15) { // dashboard is not support in macOS 10.15 below self.dashboardMenuItem.isHidden = true + self.connectionsMenuItem.isHidden = true } setupStatusMenuItemData() AppVersionUtil.showUpgradeAlert() @@ -106,7 +106,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { if WebPortalManager.hasWebProtal { WebPortalManager.shared.addWebProtalMenuItem(&statusMenu) } - RemoteControlManager.setupMenuItem(separator: externalControlSeparator) AutoUpgardeManager.shared.setup() AutoUpgardeManager.shared.setupCheckForUpdatesMenuItem(checkForUpdateMenuItem) @@ -162,6 +161,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { setupNetworkNotifier() registCrashLogger() KeyboardShortCutManager.setup() + RemoteControlManager.setupMenuItem(separator: externalControlSeparator) } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { @@ -441,7 +441,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func updateProxyList(withMenus menus: [NSMenuItem]) { let startIndex = statusMenu.items.firstIndex(of: separatorLineTop)! + 1 let endIndex = statusMenu.items.firstIndex(of: sepatatorLineEndProxySelect)! - sepatatorLineEndProxySelect.isHidden = menus.count == 0 + sepatatorLineEndProxySelect.isHidden = menus.isEmpty for _ in 0...create().showWindow(sender) + } + + @IBAction func actionConnections(_ sender: NSMenuItem?) { + if #available(macOS 10.15, *) { + ClashWindowController.create().showWindow(sender) } - dashboardWindowController?.showWindow(sender) } @IBAction func actionAllowFromLan(_ sender: NSMenuItem) { diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 94f28cd9d..2475b0b88 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -682,6 +682,7 @@ + @@ -776,6 +777,12 @@ + + + + + + diff --git a/ClashX/Basic/Combine+Ext.swift b/ClashX/Basic/Combine+Ext.swift new file mode 100644 index 000000000..501c4e3f8 --- /dev/null +++ b/ClashX/Basic/Combine+Ext.swift @@ -0,0 +1,22 @@ +// +// Combine+Ext.swift +// ClashX +// +// Created by yicheng on 2023/7/6. +// Copyright © 2023 west2online. All rights reserved. +// + +import Combine +import Foundation + +@available(macOS 10.15, *) +public extension Publisher where Failure == Never { + func weakAssign( + to keyPath: ReferenceWritableKeyPath, + on object: T + ) -> AnyCancellable { + sink { [weak object] value in + object?[keyPath: keyPath] = value + } + } +} diff --git a/ClashX/Basic/SpeedUtils.swift b/ClashX/Basic/SpeedUtils.swift new file mode 100644 index 000000000..f77f677e3 --- /dev/null +++ b/ClashX/Basic/SpeedUtils.swift @@ -0,0 +1,33 @@ +// +// SpeedUtils.swift +// ClashX +// +// Created by yicheng on 2023/7/6. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation + +enum SpeedUtils { + static func getSpeedString(for byte: Int) -> String { + return getNetString(for: byte).appending("/s") + } + + static func getNetString(for byte: Int) -> String { + let kb = byte / 1024 + if kb < 1024 { + return "\(kb)KB" + } else { + let mb = Double(kb) / 1024.0 + if mb >= 100 { + if mb >= 1000 { + return String(format: "%.1fGB", mb/1024) + } + return String(format: "%.1fMB", mb) + } else { + return String(format: "%.2fMB", mb) + } + } + } + +} diff --git a/ClashX/Basic/String+Extension.swift b/ClashX/Basic/String+Extension.swift index 0d94f3416..90411e87b 100644 --- a/ClashX/Basic/String+Extension.swift +++ b/ClashX/Basic/String+Extension.swift @@ -9,7 +9,7 @@ import Foundation extension String { func isUrlVaild() -> Bool { - guard count > 0 else { return false } + guard !isEmpty else { return false } guard let url = URL(string: self) else { return false } guard url.host != nil, diff --git a/ClashX/ClashWindowController.swift b/ClashX/ClashWindowController.swift new file mode 100644 index 000000000..a5c5ab1db --- /dev/null +++ b/ClashX/ClashWindowController.swift @@ -0,0 +1,73 @@ +// +// ClashWindowController.swift +// ClashX +// +// Created by yicheng on 2023/7/5. +// Copyright © 2023 west2online. All rights reserved. +// +import AppKit + +private class ClashWindowsRecorder { + static let shared = ClashWindowsRecorder() + var windowControllers = [NSWindowController]() { + didSet { + if windowControllers.isEmpty { + NSApp.setActivationPolicy(.accessory) + } else { + if NSApp.activationPolicy() == .accessory { + NSApp.setActivationPolicy(.regular) + } + } + } + } +} + +class ClashWindowController: NSWindowController, NSWindowDelegate { + var onWindowClose: (() -> Void)? + var lastSize: CGSize? { + get { + if let str = UserDefaults.standard.value(forKey: "lastSize.\(T.className())") as? String { + return NSSizeFromString(str) as CGSize + } + return nil + } + set { + if let size = newValue { + UserDefaults.standard.set(NSStringFromSize(size), forKey: "lastSize.\(T.className())") + } + } + } + + static func create() -> NSWindowController { + if let wc = ClashWindowsRecorder.shared.windowControllers.first(where: {$0 is Self}) { + return wc + } + let win = NSWindow() + let wc = ClashWindowController(window: win) + wc.contentViewController = T() + win.titlebarAppearsTransparent = false + ClashWindowsRecorder.shared.windowControllers.append(wc) + return wc + } + + override func showWindow(_ sender: Any?) { + super.showWindow(sender) + NSApp.activate(ignoringOtherApps: true) + if let lastSize = lastSize, lastSize != .zero { + window?.setContentSize(lastSize) + } + window?.center() + window?.makeKeyAndOrderFront(self) + window?.delegate = self + } + + func windowWillClose(_ notification: Notification) { + ClashWindowsRecorder.shared.windowControllers.removeAll(where: {$0 == self}) + onWindowClose?() + if let win = window { + if !win.styleMask.contains(.fullScreen) { + lastSize = win.frame.size + } + } + } +} diff --git a/ClashX/ClashX-Bridging-Header.h b/ClashX/ClashX-Bridging-Header.h index 8a813a9fc..05b7dc49e 100644 --- a/ClashX/ClashX-Bridging-Header.h +++ b/ClashX/ClashX-Bridging-Header.h @@ -4,3 +4,5 @@ #import "goClash.h" #import "ProxyConfigRemoteProcessProtocol.h" #import "LoginKitWrapper.h" +#import +#import diff --git a/ClashX/Extensions/DateFormatter+.swift b/ClashX/Extensions/DateFormatter+.swift index 4a970c2fe..aa6f8f518 100644 --- a/ClashX/Extensions/DateFormatter+.swift +++ b/ClashX/Extensions/DateFormatter+.swift @@ -15,4 +15,10 @@ extension DateFormatter { dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SZ" return dateFormatter } + + static var simple: DateFormatter { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "MM-dd HH:mm:ss" + return dateFormatter + } } diff --git a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift index 4509b858a..44d823b74 100644 --- a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift +++ b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift @@ -110,8 +110,8 @@ extension NSUserNotificationCenter { } func postConfigErrorNotice(msg: String) { - let configName = ConfigManager.selectConfigName.count > 0 ? - Paths.configFileName(for: ConfigManager.selectConfigName) : "" + let configName = ConfigManager.selectConfigName.isEmpty ? "" : + Paths.configFileName(for: ConfigManager.selectConfigName) let message = "\(configName): \(msg)" postNotificationAlert(title: NSLocalizedString("Config loading Fail!", comment: ""), info: message) diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index 029e0ac52..acdf86307 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -36,9 +36,9 @@ class ApiRequest { alamoFireManager = Session(configuration: configuration) } - private static func authHeader() -> HTTPHeaders { + static func authHeader() -> HTTPHeaders { let secret = ConfigManager.shared.overrideSecret ?? ConfigManager.shared.apiSecret - return (secret.count > 0) ? ["Authorization": "Bearer \(secret)"] : [:] + return (!secret.isEmpty) ? ["Authorization": "Bearer \(secret)"] : [:] } @discardableResult @@ -192,7 +192,7 @@ class ApiRequest { case let .success(providerResp): completeHandler?(providerResp) case let .failure(err): - print(err) + Logger.log("\(err)") completeHandler?(ClashProviderResp()) assertionFailure() } @@ -298,8 +298,8 @@ class ApiRequest { // MARK: - Connections extension ApiRequest { - static func getConnections(completeHandler: @escaping ([ClashConnectionSnapShot.Connection]) -> Void) { - req("/connections").responseDecodable(of: ClashConnectionSnapShot.self) { resp in + static func getConnections(completeHandler: @escaping ([ClashConnectionBaseSnapShot.Connection]) -> Void) { + req("/connections").responseDecodable(of: ClashConnectionBaseSnapShot.self) { resp in switch resp.result { case let .success(snapshot): completeHandler(snapshot.connections) @@ -310,8 +310,8 @@ extension ApiRequest { } } - static func closeConnection(_ conn: ClashConnectionSnapShot.Connection) { - req("/connections/".appending(conn.id), method: .delete).response { _ in } + static func closeConnection(_ id: String) { + req("/connections/\(id)", method: .delete).response { _ in } } static func closeAllConnection() { diff --git a/ClashX/General/Managers/ClashResourceManager.swift b/ClashX/General/Managers/ClashResourceManager.swift index f04ce5b93..faa74b3c7 100644 --- a/ClashX/General/Managers/ClashResourceManager.swift +++ b/ClashX/General/Managers/ClashResourceManager.swift @@ -94,10 +94,10 @@ extension ClashResourceManager { alert.messageText = NSLocalizedString("Custom your GEOIP MMDB download address.", comment: "") let inputView = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24)) inputView.placeholderString = Settings.defaultMmdbDownloadUrl - if Settings.mmdbDownloadUrl.count > 0 { - inputView.stringValue = Settings.mmdbDownloadUrl - } else { + if Settings.mmdbDownloadUrl.isEmpty { inputView.stringValue = Settings.defaultMmdbDownloadUrl + } else { + inputView.stringValue = Settings.mmdbDownloadUrl } alert.accessoryView = inputView alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) diff --git a/ClashX/General/Managers/ConnectionManager.swift b/ClashX/General/Managers/ConnectionManager.swift index 4ff6f1e72..9eeb91e51 100644 --- a/ClashX/General/Managers/ConnectionManager.swift +++ b/ClashX/General/Managers/ConnectionManager.swift @@ -12,7 +12,7 @@ enum ConnectionManager { static func closeConnection(for group: String) { ApiRequest.getConnections { conns in for conn in conns where conn.chains.contains(group) { - ApiRequest.closeConnection(conn) + ApiRequest.closeConnection(conn.id) } } } diff --git a/ClashX/General/Managers/ICloudManager.swift b/ClashX/General/Managers/ICloudManager.swift index 0505ef53e..b2eab4083 100644 --- a/ClashX/General/Managers/ICloudManager.swift +++ b/ClashX/General/Managers/ICloudManager.swift @@ -61,7 +61,7 @@ class ICloudManager { return } let files = try? FileManager.default.contentsOfDirectory(atPath: url.path) - if let count = files?.count, count == 0 { + if files?.isEmpty == true { let path = Bundle.main.path(forResource: "sampleConfig", ofType: "yaml")! try? FileManager.default.copyItem(atPath: path, toPath: kDefaultConfigFilePath) try? FileManager.default.copyItem(atPath: Bundle.main.path(forResource: "sampleConfig", ofType: "yaml")!, toPath: url.appendingPathComponent("config.yaml").path) @@ -90,7 +90,7 @@ class ICloudManager { complete?(url) } } catch let err { - print(err) + Logger.log("\(err)") DispatchQueue.main.async { complete?(nil) } diff --git a/ClashX/General/Managers/MenuItemFactory.swift b/ClashX/General/Managers/MenuItemFactory.swift index 77a21e7fa..5f2432583 100644 --- a/ClashX/General/Managers/MenuItemFactory.swift +++ b/ClashX/General/Managers/MenuItemFactory.swift @@ -96,7 +96,7 @@ class MenuItemFactory { let app = AppDelegate.shared let startIndex = app.statusMenu.items.firstIndex(of: app.separatorLineTop)! + 1 let endIndex = app.statusMenu.items.firstIndex(of: app.sepatatorLineEndProxySelect)! - app.sepatatorLineEndProxySelect.isHidden = menus.count == 0 + app.sepatatorLineEndProxySelect.isHidden = menus.isEmpty for _ in 0.. 0 else { return } + guard !proxyGroup.speedtestAble.isEmpty else { return } let speedTestItem = ProxyGroupSpeedTestMenuItem(group: proxyGroup) let separator = NSMenuItem.separator() menu.insertItem(separator, at: 0) diff --git a/ClashX/General/Managers/RemoteControlManager.swift b/ClashX/General/Managers/RemoteControlManager.swift index 9ab145fed..e7a0fa7e4 100644 --- a/ClashX/General/Managers/RemoteControlManager.swift +++ b/ClashX/General/Managers/RemoteControlManager.swift @@ -62,13 +62,13 @@ class RemoteControlManager { menuSeparator = separator updateMenuItems() updateDropDownMenuItems() - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { + DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { RemoteControlManager.recoverSelection() } } static private func recoverSelection() { - if Recorder.selected != "" { + if !Recorder.selected.isEmpty { if let config = configs.first(where: { $0.uuid == Recorder.selected }) { selectConfig = config updateRemoteControl() diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index aab05fbbd..8a6c93dbd 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -75,7 +75,7 @@ enum Settings { @UserDefault("benchMarkUrl", defaultValue: defaultBenchmarkUrl) static var benchMarkUrl: String { didSet { - if benchMarkUrl.count == 0 { + if benchMarkUrl.isEmpty { benchMarkUrl = defaultBenchmarkUrl } } diff --git a/ClashX/Images.xcassets/Contents.json b/ClashX/Images.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/ClashX/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ClashX/Images.xcassets/icon_connection_done.imageset/Contents.json b/ClashX/Images.xcassets/icon_connection_done.imageset/Contents.json new file mode 100644 index 000000000..7b2aa6410 --- /dev/null +++ b/ClashX/Images.xcassets/icon_connection_done.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "icon_connected.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_connected@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected.png b/ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected.png new file mode 100644 index 0000000000000000000000000000000000000000..ea79ade03c4d63645c5bb30a6519c4194a963437 GIT binary patch literal 498 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1&S-^~7gA@w${E7uCi}7@E4ABs+ zooMgRs=iez6kQ99yB?&(XPI$ z|GoR1`)eOv*5p>=lA6>uPbmK?QyTksftjZlyu9`yZtwBj%>Uxq_NFJj775?asC)QJ zv8(O%0i(S;G-dxZMz`(A@Hls?Wy!%iM?w#_85;0}-@l~YJuJ{sVULqE}(;;NhWjhUd4%^K=iS6?*$v$KENpEhf R_$LmI8BbR~mvv4FO#u3TzC!>2 literal 0 HcmV?d00001 diff --git a/ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected@2x.png b/ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..51af80c40723a9f5b16a3dc4727eb6f5a9701e47 GIT binary patch literal 990 zcmV<410np0P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR917@z|H1ONa40RR917ytkO0OB=utpET6NJ&INR7ef2RafAyGc#d zn%4KWzAC7#2);n9Xe}xzse+&_D7J!6=%)yN2#SJVDkv(72v$MSKedX+M_Tbi5TO#S z6fD{p79V}YSK4H^JI-uO++>q#jq|a4=gysT?!9y84q=dS^~oef(~NbDn3sW-0;CYY zMW%wNgTR`GvHV*#RqsAzgW>k}aos<4LLZ_BLD{6_%q-rDlA;xO-+d2lq4P-#9z~2tQ7oHvj-i8( zDVUFCWoIzbHQ5C1(>^D`g}|e5>X52saf$fB4rxpo>&4Qt)5w#Yh-zU3f~U}~Hrg;4 z8D2SlLsQq3qU9J{umG<^7ZB6KDT~v`Z$!ncrF(~TJm&E%4T=Xla_*pmnUd&x0iH_D+s(Z~|BspD&c*0T(J_in-`z~3M zj|d2pzW&Kgl93!gDLQ*prMP0kCDcdS5&i1nGomxe0ytKSjBp$K*VDMmo6I zV~fBFbj7cX(LvRsD~iU&D~*>O@!*&$E)gTT?RHR8O+~23y3Z>A1gk4CiFjGp4gdfE M07*qoM6N<$f^DI%9{>OV literal 0 HcmV?d00001 diff --git a/ClashX/Images.xcassets/icon_connection_fail.imageset/Contents.json b/ClashX/Images.xcassets/icon_connection_fail.imageset/Contents.json new file mode 100644 index 000000000..b0a614ff2 --- /dev/null +++ b/ClashX/Images.xcassets/icon_connection_fail.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "icon_connection_fail.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_connection_fail@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail.png b/ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3663e6f9415f221bae83076e2ea138a028f52f GIT binary patch literal 471 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1&S-^~7gA@w${E7uCbM$m^4ABs+ z4RZ8jauirIZ$rVCj7{BK8B8qu7k2gsIOffIVQnHPY&tJsah+358z&RH!K~J1@x8Ae zZM(a;C1_D~+8m z@18T5%6KNQ*K+8__po4wC2{skLJe~ zPZYhWTCvhT+Nk8}K4Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR917@z|H1ONa40RR917ytkO0OB=utpET5>PbXFR7ef2RozQeQ4s&l-Rs(> zQF=*5BCxI8O&_xVAt)>#6@n5qE2yW4C?X{(goKKEhrFu^3At)j#qNJD1t^6Q@ zELZU*b#*^ZGe`Dv*n3y=)e})zDO3`mKp0n0 z!%$Y=1_Yk%82kJu9}Mez*JxF3`2r<7GN2Ps%Jym)9HJx2Nx7oLdZP-QWZUJV!9{LkqbBw zFcnrxGhkZ5I+#6)b|5*Qr{vewMeY{@O$Q2!c2|EJnA9nf3tc~Oai~f%$(hSSk(A=x zT>7V9wQPU<2#LWVOkC|2UC%1Jcpjm;z4+01#3KXILV*?9W^tW2_<@VX+=l@;t<9+D z>hu7VcV2?i-iq0e10I=XC>2a+W)B5UZ*k-(5kIZjckf|s4575;7;MME-&e1-nt+!! zAH~#zhlqDy_cLAO5vAAmv$}A(_Ki7#w zz8tt=!}FT`YJHf1UJ+2KTbGBBV0vgn>b4ieh$hmoP*QWqN~LOpXq<=R+m*=jF~t6e&!&H zdTg2J2~tV!g@RnI!e|7Ockd(Kb0b#-xp+0dfY81=k4!VT(X2pGubxFbMep#Va>ieC zbzCn0o6*tOSv`y7vEZWOdA>f2KvM|fnxJV1i!Tv1@o~~?cER0CR-iE&i%q!>gpm2o zbNtkroCr9fJu))*G8?GAgX~8B{37k=s(Q*_iOFA%mb6IZCb-z>6q&|y5_v4x;I@oh zQ|3!cwGeKh5Y@ThqVG9@3G#hs-Bua7-17A@xMa3S`*TT;bCnFUb)S@?yj-T8N&f@x W`W^GfKUpLI0000n;V;!C7c*}N#{N@N8Z+VbJHYxKEJap z4wM%^vS%V^{k7Lgp1HnjN~%wBC?_$h#XgL>Q_u7;g^KYNRVcds+*XkmU>nwWagTcrJTyYD(>)erA5oKE1f%WYjTt0 zt2A3RV|a`9hYCbbVl9T$8Uj$ z>E!B)y~XQVGM^N9N*#Dwa`(2+yMya4m^mza5pTR9HTiglXM4}2kF%b=sGHZecE literal 0 HcmV?d00001 diff --git a/ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting@2x.png b/ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..35fd939bbe9dbf46e8cfed7e80447788d23c9482 GIT binary patch literal 939 zcmV;c162HpP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR917@z|H1ONa40RR917ytkO0OB=utpET66-h)vR7ef2RY6ZvK@gtV_g)(c zlnMb&@nC|HkQ5>+G4bR@qoUq~vk?_;`UCJ_q9--P#Dg(<*2J3-6HQD!XhMl%EF?9l zF@^(CEFu&reeJF@TeiH~Rw%kjJNst8`F3V^b_U@uBNgr0wFc=ksGt`R+CZr=Ndy9T_Rg$T6g zOb*KO0;-);Bsq%Yq=`gtkUw+$v>ngOIl#wdTfTk4L+^g*BjOAFU*xrYS2lF(GbePE?C*g4 z>y5Stl?AF(lY*Q{XbdS*i7NznOTgRZFPVwUkOTV2=2j6ob5bUK#(eFN57r&w+XgGp z0lDQ^LA8~MRU6UA#-5uY5YOX!nV6wtsv<89MPKO^UBcettf@}3blxv z1sAC8QI%Se_epwsH+V%CJMKi>EV$H`!mHB|M^;#(FL*erA5)>>aQ&tpJNPe~FgbaD zF?8_bE8|w7;TXljMC2HCrYBr-jczNFkA*$=lCxbQ0z<-)aI*jucR3S`zLL}!#1UHs z+W#bHo015;L`(0xo4Mq2Rpi1&nGvRSg{Wp}o)8FQmhg>ows`Hl^WS`SJK-`+Li_*# N002ovPDHLkV1f_`l_CHD literal 0 HcmV?d00001 diff --git a/ClashX/Info.plist b/ClashX/Info.plist index 5dd6f3771..250d7cc46 100644 --- a/ClashX/Info.plist +++ b/ClashX/Info.plist @@ -3,7 +3,7 @@ BETA - + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDocumentTypes diff --git a/ClashX/Models/ClashConnection.swift b/ClashX/Models/ClashConnection.swift index 0cc323d3d..2d044c2b7 100644 --- a/ClashX/Models/ClashConnection.swift +++ b/ClashX/Models/ClashConnection.swift @@ -8,13 +8,162 @@ import Cocoa -struct ClashConnectionSnapShot: Codable { +struct ClashConnectionBaseSnapShot: Codable { let connections: [Connection] } -extension ClashConnectionSnapShot { +extension ClashConnectionBaseSnapShot { struct Connection: Codable { let id: String let chains: [String] } } + +@available(macOS 10.15, *) +class ClashConnectionSnapShot: Decodable { + var connections: [Connection] + let downloadTotal:Int + let uploadTotal:Int +} + +@available(macOS 10.15, *) +extension ClashConnectionSnapShot { + class Connection: NSObject, Decodable { + @objc enum ConnStatus: Int { + case connecting + case finished + case fail + + var image: NSImage? { + switch self { + case .connecting: return NSImage(named: "icon_connection_inprogress") + case .finished: return NSImage(named: "icon_connection_done") + case .fail: return NSImage(named: "icon_connection_fail") + } + } + + var title: String { + switch self { + case .connecting: return NSLocalizedString("Connecting", comment: "") + case .finished: return NSLocalizedString("Done", comment: "") + case .fail: return NSLocalizedString("Fail", comment: "") + } + } + } + + let id: String + let chains: [String] + @objc let metadata:MetaData + @objc @Published var upload:Int + @objc @Published var download:Int + @objc let start:Date + @objc let rule:String + let rulePayload:String + + @objc @Published var status = ConnStatus.connecting + @objc @Published var uploadSpeed = 0 { + didSet { + if uploadSpeed > maxUploadSpeed { + maxUploadSpeed = uploadSpeed + } + } + } + @objc @Published var downloadSpeed = 0 { + didSet { + if downloadSpeed > maxDownloadSpeed { + maxDownloadSpeed = downloadSpeed + } + } + } + @Published private(set) var maxUploadSpeed = 0 + @Published private(set) var maxDownloadSpeed = 0 + var error:String? + + enum CodingKeys: CodingKey { + case id + case chains + case metadata + case upload + case download + case start + case rule + case rulePayload + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + id = try container.decode(String.self, forKey: .id) + chains = try container.decode([String].self, forKey: .chains) + metadata = try container.decode(MetaData.self, forKey: .metadata) + upload = try container.decode(Int.self, forKey: .upload) + download = try container.decode(Int.self, forKey: .download) + start = try container.decode(Date.self, forKey: .start) + rule = try container.decode(String.self, forKey: .rule) + rulePayload = try container.decode(String.self, forKey: .rulePayload) + } + + init(id:String, chains:[String], meta:MetaData, upload:Int, download:Int, start:Date, rule:String, rulePayload:String) { + self.id = id + self.chains = chains + self.metadata = meta + self.upload = upload + self.download = download + self.start = start + self.rule = rule + self.rulePayload = rulePayload + super.init() + } + } + // {"network":"tcp","type":"HTTP Connect","sourceIP":"127.0.0.1","destinationIP":"124.72.132.104","sourcePort":"59217","destinationPort":"443","host":"slardar-bd.feishu.cn","dnsMode":"normal","processPath":"","specialProxy":""} + class MetaData:NSObject, Codable { + let network:String + @objc let type:String + let sourceIP: String + let destinationIP:String + let sourcePort:String + let destinationPort:String + @objc let host:String + let dnsMode:String + let specialProxy:String? + var processPath:String + + @objc var displayHost: String { + if !host.isEmpty { return host } + return destinationIP + } + var pid:String? + var processImage:NSImage? + + @objc var processName:String? + + enum CodingKeys: CodingKey { + case network + case type + case sourceIP + case destinationIP + case host + case dnsMode + case specialProxy + case processPath + case sourcePort + case destinationPort + } + + init(network: String, type: String, sourceIP: String, destinationIP: String, sourcePort: String, destinationPort: String, host: String, dnsMode: String, specialProxy: String?, processPath: String, pid: String? = nil, processImage: NSImage? = nil, processName: String? = nil) { + self.network = network + self.type = type + self.sourceIP = sourceIP + self.destinationIP = destinationIP + self.sourcePort = sourcePort + self.destinationPort = destinationPort + self.host = host + self.dnsMode = dnsMode + self.specialProxy = specialProxy + self.processPath = processPath + self.pid = pid + self.processImage = processImage + self.processName = processName + super.init() + } + } +} diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index 4b8705700..da51fc17c 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -127,7 +127,7 @@ class ClashProxy: Codable { }() lazy var isSpeedTestable: Bool = { - return speedtestAble.count > 0 + return !speedtestAble.isEmpty }() private enum CodingKeys: String, CodingKey { diff --git a/ClashX/Models/SavedProxyModel.swift b/ClashX/Models/SavedProxyModel.swift index 15f9715e9..563963857 100644 --- a/ClashX/Models/SavedProxyModel.swift +++ b/ClashX/Models/SavedProxyModel.swift @@ -26,9 +26,6 @@ struct SavedProxyModel: Codable { let filtered = models.filter({ model in let pass = !set.contains(model.key) set.insert(model.key) - if !pass { - print("pass", model) - } return pass }) return filtered diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 2d5c16af5..24903e835 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -1,9 +1,18 @@ +/* No comment provided by engineer. */ +"Active Connections" = "Active Connections"; + /* No comment provided by engineer. */ "Add a remote config" = "Add a remote config"; /* No comment provided by engineer. */ "Add a remote control config" = "Add a remote control config"; +/* No comment provided by engineer. */ +"All Clients" = "All Clients"; + +/* No comment provided by engineer. */ +"All Hosts" = "All Hosts"; + /* No comment provided by engineer. */ "Apply and Quit" = "Apply and Quit"; @@ -37,12 +46,21 @@ /* No comment provided by engineer. */ "Click OK to quit the app and apply change." = "Click OK to quit the app and apply change."; +/* No comment provided by engineer. */ +"Client" = "Client"; + +/* No comment provided by engineer. */ +"Close Connection" = "Close Connection"; + /* No comment provided by engineer. */ "Config file have been changed" = "Config file have been changed"; /* No comment provided by engineer. */ "Config loading Fail!" = "Config loading Fail!"; +/* No comment provided by engineer. */ +"Connecting" = "Connecting"; + /* No comment provided by engineer. */ "Copy Shell Command" = "Copy Shell Command"; @@ -52,6 +70,9 @@ /* No comment provided by engineer. */ "Custom your GEOIP MMDB download address." = "Custom your GEOIP MMDB download address."; +/* No comment provided by engineer. */ +"Date" = "Date"; + /* No comment provided by engineer. */ "Details" = "Details"; @@ -61,12 +82,24 @@ /* No comment provided by engineer. */ "Direct Mode" = "Direct Mode"; +/* No comment provided by engineer. */ +"Done" = "Done"; + +/* No comment provided by engineer. */ +"Download" = "Download"; + /* No comment provided by engineer. */ "Download fail" = "Download fail"; +/* No comment provided by engineer. */ +"Download speed" = "Download speed"; + /* No comment provided by engineer. */ "fail" = "fail"; +/* No comment provided by engineer. */ +"Fail" = "Fail"; + /* No comment provided by engineer. */ "Fail:" = "Fail:"; @@ -76,6 +109,12 @@ /* No comment provided by engineer. */ "Global Mode" = "Global Mode"; +/* No comment provided by engineer. */ +"Host" = "Host"; + +/* No comment provided by engineer. */ +"Hosts" = "Hosts"; + /* No comment provided by engineer. */ "hours" = "hours"; @@ -97,6 +136,9 @@ /* No comment provided by engineer. */ "Load Balance" = "Load Balance"; +/* No comment provided by engineer. */ +"Local Clients" = "Local Clients"; + /* No comment provided by engineer. */ "Need to Restart the ClashX to Take effect, Please start clashX manually" = "Need to Restart the ClashX to Take effect, Please start clashX manually"; @@ -109,6 +151,9 @@ /* No comment provided by engineer. */ "OK" = "OK"; +/* No comment provided by engineer. */ +"Open Connection Details" = "Open Connection Details"; + /* No comment provided by engineer. */ "Open Dashboard" = "Open Dashboard"; @@ -136,6 +181,9 @@ /* No comment provided by engineer. */ "Quit" = "Quit"; +/* No comment provided by engineer. */ +"Recent Connections" = "Recent Connections"; + /* No comment provided by engineer. */ "Reduce alerts if notification permission is disabled" = "Reduce alerts if notification permission is disabled"; @@ -154,6 +202,9 @@ /* No comment provided by engineer. */ "Remote Config Update Fail" = "Remote Config Update Fail"; +/* No comment provided by engineer. */ +"Requests" = "Requests"; + /* No comment provided by engineer. */ "Reset Daemon" = "Reset Daemon"; @@ -169,9 +220,15 @@ /* No comment provided by engineer. */ "Should be a least 1 hour" = "Should be a least 1 hour"; +/* No comment provided by engineer. */ +"Sources" = "Sources"; + /* No comment provided by engineer. */ "Stable" = "Stable"; +/* No comment provided by engineer. */ +"Status" = "Status"; + /* No comment provided by engineer. */ "Succeed!" = "Succeed!"; @@ -202,6 +259,12 @@ /* No comment provided by engineer. */ "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly." = "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly."; +/* No comment provided by engineer. */ +"Type" = "Type"; + +/* No comment provided by engineer. */ +"Unknown" = "Unknown"; + /* No comment provided by engineer. */ "Update GEOIP Database" = "Update GEOIP Database"; @@ -211,5 +274,11 @@ /* No comment provided by engineer. */ "Updating" = "Updating"; +/* No comment provided by engineer. */ +"Upload" = "Upload"; + +/* No comment provided by engineer. */ +"Upload speed" = "Upload speed"; + /* No comment provided by engineer. */ "Use reload config to try reconnect." = "Use reload config to try reconnect."; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index af34246b9..b4d375bba 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -1,9 +1,18 @@ +/* No comment provided by engineer. */ +"Active Connections" = "活动连接"; + /* No comment provided by engineer. */ "Add a remote config" = "添加托管配置文件"; /* No comment provided by engineer. */ "Add a remote control config" = "添加远程控制器配置"; +/* No comment provided by engineer. */ +"All Clients" = "所有客户端"; + +/* No comment provided by engineer. */ +"All Hosts" = "所有主机名"; + /* No comment provided by engineer. */ "Apply and Quit" = "应用并退出"; @@ -37,12 +46,21 @@ /* No comment provided by engineer. */ "Click OK to quit the app and apply change." = "确认清理并退出软件"; +/* No comment provided by engineer. */ +"Client" = "客户端"; + +/* No comment provided by engineer. */ +"Close Connection" = "关闭连接"; + /* No comment provided by engineer. */ "Config file have been changed" = "配置文件已被修改"; /* No comment provided by engineer. */ "Config loading Fail!" = "配置文件加载失败"; +/* No comment provided by engineer. */ +"Connecting" = "连接中"; + /* No comment provided by engineer. */ "Copy Shell Command" = "复制终端代理命令"; @@ -52,6 +70,9 @@ /* No comment provided by engineer. */ "Custom your GEOIP MMDB download address." = "自定义下载地址"; +/* No comment provided by engineer. */ +"Date" = "时间"; + /* No comment provided by engineer. */ "Details" = "查看详情"; @@ -61,12 +82,24 @@ /* No comment provided by engineer. */ "Direct Mode" = "直接连接"; +/* No comment provided by engineer. */ +"Done" = "完成"; + +/* No comment provided by engineer. */ +"Download" = "下载"; + /* No comment provided by engineer. */ "Download fail" = "下载失败"; +/* No comment provided by engineer. */ +"Download speed" = "下载速率"; + /* No comment provided by engineer. */ "fail" = "失败"; +/* No comment provided by engineer. */ +"Fail" = "失败"; + /* No comment provided by engineer. */ "Fail:" = "失败:"; @@ -76,6 +109,12 @@ /* No comment provided by engineer. */ "Global Mode" = "全局连接"; +/* No comment provided by engineer. */ +"Host" = "主机名"; + +/* No comment provided by engineer. */ +"Hosts" = "主机名"; + /* No comment provided by engineer. */ "hours" = "小时"; @@ -97,6 +136,9 @@ /* No comment provided by engineer. */ "Load Balance" = "负载均衡"; +/* No comment provided by engineer. */ +"Local Clients" = "本地程序"; + /* No comment provided by engineer. */ "Need to Restart the ClashX to Take effect, Please start clashX manually" = "需要重启ClashX生效,请手动启动ClashX"; @@ -109,6 +151,9 @@ /* No comment provided by engineer. */ "OK" = "确定"; +/* No comment provided by engineer. */ +"Open Connection Details" = "打开连接查看器"; + /* No comment provided by engineer. */ "Open Dashboard" = "打开控制台"; @@ -136,6 +181,9 @@ /* No comment provided by engineer. */ "Quit" = "退出"; +/* No comment provided by engineer. */ +"Recent Connections" = "最近连接"; + /* No comment provided by engineer. */ "Reduce alerts if notification permission is disabled" = "在通知权限关闭时减少提示弹窗"; @@ -154,6 +202,9 @@ /* No comment provided by engineer. */ "Remote Config Update Fail" = "托管配置文件更新失败"; +/* No comment provided by engineer. */ +"Requests" = "请求"; + /* No comment provided by engineer. */ "Reset Daemon" = "重置守护程序"; @@ -169,9 +220,15 @@ /* No comment provided by engineer. */ "Should be a least 1 hour" = "至少需要1小时间隔"; +/* No comment provided by engineer. */ +"Sources" = "来源"; + /* No comment provided by engineer. */ "Stable" = "Stable"; +/* No comment provided by engineer. */ +"Status" = "状态"; + /* No comment provided by engineer. */ "Succeed!" = "成功!"; @@ -202,6 +259,12 @@ /* No comment provided by engineer. */ "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly." = "由于Clash Core发布1.0版本,使用此版本的 ClashX 可能需要更新配置内容\n前往 https://github.com/Dreamacro/clash/wiki/breaking-changes-in-1.0.0 查看详情"; +/* No comment provided by engineer. */ +"Type" = "类型"; + +/* No comment provided by engineer. */ +"Unknown" = "未知"; + /* No comment provided by engineer. */ "Update GEOIP Database" = "更新IP数据库"; @@ -211,5 +274,11 @@ /* No comment provided by engineer. */ "Updating" = "更新中"; +/* No comment provided by engineer. */ +"Upload" = "上传"; + +/* No comment provided by engineer. */ +"Upload speed" = "上传速率"; + /* No comment provided by engineer. */ "Use reload config to try reconnect." = "使用重载配置文件按钮尝试重新连接"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 01fb198f8..2ac59cf83 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -1,9 +1,18 @@ +/* No comment provided by engineer. */ +"Active Connections" = "活動連線"; + /* No comment provided by engineer. */ "Add a remote config" = "添加託管配置文件"; /* No comment provided by engineer. */ "Add a remote control config" = "添加遠程控制器配置"; +/* No comment provided by engineer. */ +"All Clients" = "所有客戶端"; + +/* No comment provided by engineer. */ +"All Hosts" = "所有主機名"; + /* No comment provided by engineer. */ "Apply and Quit" = "應用並退出"; @@ -37,12 +46,21 @@ /* No comment provided by engineer. */ "Click OK to quit the app and apply change." = "確認清理併退出軟件"; +/* No comment provided by engineer. */ +"Client" = "客戶端"; + +/* No comment provided by engineer. */ +"Close Connection" = "關閉連線"; + /* No comment provided by engineer. */ "Config file have been changed" = "配置文件已被修改"; /* No comment provided by engineer. */ "Config loading Fail!" = "配置文件加載失敗"; +/* No comment provided by engineer. */ +"Connecting" = "連接中"; + /* No comment provided by engineer. */ "Copy Shell Command" = "複製終端代理命令"; @@ -52,6 +70,9 @@ /* No comment provided by engineer. */ "Custom your GEOIP MMDB download address." = "自定義下載地址"; +/* No comment provided by engineer. */ +"Date" = "時間"; + /* No comment provided by engineer. */ "Details" = "查看詳情"; @@ -61,12 +82,24 @@ /* No comment provided by engineer. */ "Direct Mode" = "直接連接"; +/* No comment provided by engineer. */ +"Done" = "完成"; + +/* No comment provided by engineer. */ +"Download" = "下載"; + /* No comment provided by engineer. */ "Download fail" = "下載失敗"; +/* No comment provided by engineer. */ +"Download speed" = "下載速率"; + /* No comment provided by engineer. */ "fail" = "失敗"; +/* No comment provided by engineer. */ +"Fail" = "失敗"; + /* No comment provided by engineer. */ "Fail:" = "失敗:"; @@ -76,6 +109,12 @@ /* No comment provided by engineer. */ "Global Mode" = "全局連接"; +/* No comment provided by engineer. */ +"Host" = "主機名"; + +/* No comment provided by engineer. */ +"Hosts" = "主機名"; + /* No comment provided by engineer. */ "hours" = "小時"; @@ -97,6 +136,9 @@ /* No comment provided by engineer. */ "Load Balance" = "負載均衡"; +/* No comment provided by engineer. */ +"Local Clients" = "本地程式"; + /* No comment provided by engineer. */ "Need to Restart the ClashX to Take effect, Please start clashX manually" = "需要重啟ClashX生效,請手動啟動ClashX"; @@ -109,6 +151,9 @@ /* No comment provided by engineer. */ "OK" = "確定"; +/* No comment provided by engineer. */ +"Open Connection Details" = "打開連接查看器"; + /* No comment provided by engineer. */ "Open Dashboard" = "打開控制台"; @@ -136,6 +181,9 @@ /* No comment provided by engineer. */ "Quit" = "退出"; +/* No comment provided by engineer. */ +"Recent Connections" = "最近連線"; + /* No comment provided by engineer. */ "Reduce alerts if notification permission is disabled" = "在通知權限關閉時減少提示彈窗"; @@ -154,6 +202,9 @@ /* No comment provided by engineer. */ "Remote Config Update Fail" = "遠程配置更新失敗"; +/* No comment provided by engineer. */ +"Requests" = "請求"; + /* No comment provided by engineer. */ "Reset Daemon" = "重置守護進程"; @@ -169,9 +220,15 @@ /* No comment provided by engineer. */ "Should be a least 1 hour" = "應該至少 1 小時"; +/* No comment provided by engineer. */ +"Sources" = "來源"; + /* No comment provided by engineer. */ "Stable" = "穩定"; +/* No comment provided by engineer. */ +"Status" = "狀態"; + /* No comment provided by engineer. */ "Succeed!" = "成功!"; @@ -202,6 +259,12 @@ /* No comment provided by engineer. */ "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly." = "此版本的 ClashX 包含因 clash core 1.0 發布而導致的中斷更改。請檢查您的配置是否無法正常工作"; +/* No comment provided by engineer. */ +"Type" = "類型"; + +/* No comment provided by engineer. */ +"Unknown" = "未知"; + /* No comment provided by engineer. */ "Update GEOIP Database" = "更新 GEOIP 數據庫"; @@ -211,5 +274,11 @@ /* No comment provided by engineer. */ "Updating" = "更新中"; +/* No comment provided by engineer. */ +"Upload" = "上傳"; + +/* No comment provided by engineer. */ +"Upload speed" = "上傳速率"; + /* No comment provided by engineer. */ "Use reload config to try reconnect." = "使用重新加載配置嘗試重新連接"; diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index 0d89f1f6d..1058d6020 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -25,55 +25,11 @@ enum WebCacheCleaner { } } -class ClashWebViewWindowController: NSWindowController { - var onWindowClose: (() -> Void)? - - static func create() -> ClashWebViewWindowController { - let win = NSWindow() - win.center() - let wc = ClashWebViewWindowController(window: win) - wc.contentViewController = ClashWebViewContoller() - return wc - } - - override func showWindow(_ sender: Any?) { - super.showWindow(sender) - NSApp.activate(ignoringOtherApps: true) - window?.makeKeyAndOrderFront(self) - window?.delegate = self - } -} - -extension ClashWebViewWindowController: NSWindowDelegate { - func windowWillClose(_ notification: Notification) { - NSApp.setActivationPolicy(.accessory) - onWindowClose?() - if let contentVC = contentViewController as? ClashWebViewContoller, let win = window { - if !win.styleMask.contains(.fullScreen) { - contentVC.lastSize = win.frame.size - } - } - } -} - class ClashWebViewContoller: NSViewController { let webview: CustomWKWebView = CustomWKWebView() var bridge: WebViewJavascriptBridge? let disposeBag = DisposeBag() let minSize = NSSize(width: 920, height: 580) - var lastSize: CGSize? { - get { - if let str = UserDefaults.standard.value(forKey: "ClashWebViewContoller.lastSize") as? String { - return NSSizeFromString(str) as CGSize - } - return nil - } - set { - if let size = newValue { - UserDefaults.standard.set(NSStringFromSize(size), forKey: "ClashWebViewContoller.lastSize") - } - } - } let effectView = NSVisualEffectView() @@ -139,13 +95,6 @@ class ClashWebViewContoller: NSViewController { view.layer?.cornerRadius = 10 view.window?.minSize = minSize - if let lastSize = lastSize, lastSize != .zero { - view.window?.setContentSize(lastSize) - } - view.window?.center() - if NSApp.activationPolicy() == .accessory { - NSApp.setActivationPolicy(.regular) - } } func setupView() { @@ -175,10 +124,6 @@ class ClashWebViewContoller: NSViewController { } Logger.log("load dashboard url fail", level: .error) } - - deinit { - NSApp.setActivationPolicy(.accessory) - } } extension ClashWebViewContoller { diff --git a/ClashX/ViewControllers/Connections/ConnectionsViewController.swift b/ClashX/ViewControllers/Connections/ConnectionsViewController.swift new file mode 100644 index 000000000..55517d1aa --- /dev/null +++ b/ClashX/ViewControllers/Connections/ConnectionsViewController.swift @@ -0,0 +1,220 @@ +// +// ConnectionsViewController.swift +// ClashX +// +// Created by yicheng on 2023/7/5. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import Combine + +@available(macOS 10.15, *) +class ConnectionsViewController: NSViewController { + let viewModel = ConnectionsViewModel() + let leftViewModel = ConnectionLeftPannelViewModel() + lazy var leftTableView = ConnectionLeftPannelView(viewModel: leftViewModel) + let topViewModel = ConnectionTopListViewModel() + lazy var topView = ConnectionTopListView(viewModel: topViewModel) + let detailView = ConnectionDetailInfoView() + + let connectionDetailViewModel = ConnectionDetailViewModel() + + var disposeBag = Set() + var modeCancellable = Set() + var leftWidthConstraint: NSLayoutConstraint? + var topViewBottomConstraint: NSLayoutConstraint? + override func viewDidLoad() { + super.viewDidLoad() + setup() + setupCommonViewModel() + setupAllConnViewModel() + } + + override func loadView() { + view = ConnectionsViewControllerBaseView(frame: NSRect(origin: .zero, size: CGSize(width: 900, height: 600))) + } + + private func setup() { + view.addSubview(leftTableView) + view.makeConstraints { + [$0.widthAnchor.constraint(greaterThanOrEqualToConstant: 900), + $0.heightAnchor.constraint(greaterThanOrEqualToConstant: 600)] + } + + leftWidthConstraint = leftTableView.widthAnchor.constraint(equalToConstant: 200) + leftTableView.makeConstraints { + [$0.leftAnchor.constraint(equalTo: view.leftAnchor), + $0.topAnchor.constraint(equalTo: view.topAnchor), + $0.bottomAnchor.constraint(equalTo: view.bottomAnchor), + leftWidthConstraint!] + } + + (view as! ConnectionsViewControllerBaseView).leftWidthConstraint = leftWidthConstraint + + view.addSubview(topView) + topView.makeConstraints { + [$0.leftAnchor.constraint(equalTo: leftTableView.rightAnchor), + $0.topAnchor.constraint(equalTo: view.topAnchor), + $0.rightAnchor.constraint(equalTo: view.rightAnchor)] + } + topViewBottomConstraint = topView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + detailView.setup(with: connectionDetailViewModel) + } + + private func setupCommonViewModel() { + topViewModel.onSelectedConnection = { [weak self] in + self?.viewModel.selectedConnection = $0 + } + + viewModel.$selectedConnection.sink { [weak self] conn in + self?.connectionDetailViewModel.accept(connection: conn) + }.store(in: &disposeBag) + + leftViewModel.onSelectedFilter = { [weak topViewModel] in + topViewModel?.applicationFilter = $0 + } + + viewModel.$showBottomView.removeDuplicates().sink { [weak self] show in + guard let self else { return } + if show { + view.addSubview(detailView) + topViewBottomConstraint?.isActive = false + detailView.makeConstraints { + [$0.leftAnchor.constraint(equalTo: self.leftTableView.rightAnchor), + $0.heightAnchor.constraint(equalToConstant: 236), + $0.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), + $0.rightAnchor.constraint(equalTo: self.view.rightAnchor), + $0.topAnchor.constraint(equalTo: self.topView.bottomAnchor)] + } + } else { + detailView.removeFromSuperview() + topViewBottomConstraint?.isActive = true + } + }.store(in: &disposeBag) + + } + + private func setupAllConnViewModel() { + viewModel.connectionDataDidRefresh.sink { [weak topViewModel] in + topViewModel?.connectionDidUpdate() + }.store(in: &modeCancellable) + + viewModel.$connections.map {Array($0.values)}.sink { [weak self] in + self?.topViewModel.accept(connections: $0) + }.store(in: &modeCancellable) + + viewModel.$applicationMap.map {Array($0.values)}.sink { [weak self] in + self?.leftViewModel.accept(connections: $0) + }.store(in: &modeCancellable) + + viewModel.$sourceIPs.map {Array($0)}.sink { [weak self] in + self?.leftViewModel.accept(sources: $0) + }.store(in: &modeCancellable) + + viewModel.$hosts.map {Array($0)}.sink { [weak self] in + self?.leftViewModel.accept(hosts: $0) + }.store(in: &modeCancellable) + } + + private func setupActiveConnViewModel() { + viewModel.connectionDataDidRefresh.sink { [weak self] in + guard let self else { return } + topViewModel.accept(connections: viewModel.currentConnections) + leftViewModel.accept(apps: viewModel.currentApplications, sources: viewModel.currentSourceIPs, hosts: viewModel.currentHosts) + }.store(in: &modeCancellable) + viewModel.connectionDataDidRefresh.send() + } + + func setActiveMode(enable:Bool) { + modeCancellable.removeAll() + viewModel.activeOnlyMode = enable + if viewModel.activeOnlyMode { + setupActiveConnViewModel() + } else { + setupAllConnViewModel() + } + } +} + +@available(macOS 10.15, *) +extension ConnectionsViewController:DashboardSubViewControllerProtocol { + func actionSearch(string: String) { + topViewModel.textFilter = string + } +} + +class ConnectionsViewControllerBaseView: NSView { + var leftWidthConstraint:NSLayoutConstraint? + enum DragType { + case none + case leftPannel + } + + var dragType = DragType.none + let dragSize:CGFloat = 5.0 + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func hitTest(_ point: NSPoint) -> NSView? { + if dragType == .none { + return super.hitTest(point) + } + return self + } + override func updateTrackingAreas() { + super.updateTrackingAreas() + trackingAreas.forEach({ removeTrackingArea($0) }) + addTrackingArea(NSTrackingArea(rect: bounds, + options: [.mouseMoved, + .mouseEnteredAndExited, + .activeAlways], + owner: self)) + } + + override func mouseExited(with event: NSEvent) { + NSCursor.arrow.set() + } + + override func mouseDown(with event: NSEvent) { + update(with: event) + } + + override func mouseUp(with event: NSEvent) { + dragType = .none + } + + override func mouseMoved(with event: NSEvent) { + update(with: event) + } + + override func mouseDragged(with event: NSEvent) { + switch dragType { + case .none: + break + case .leftPannel: + let deltaX = event.deltaX + let target = (leftWidthConstraint?.constant ?? 0) + deltaX + leftWidthConstraint?.constant = min(max(target, 200), 400) + } + } + + func update(with event: NSEvent) { + let locationInView = convert(event.locationInWindow, from: nil) + let currentLeftSize = leftWidthConstraint?.constant ?? 0 + if locationInView.x > currentLeftSize - dragSize && locationInView.x < currentLeftSize + dragSize { + dragType = .leftPannel + NSCursor.resizeLeftRight.set() + return + } + dragType = .none + NSCursor.arrow.set() + } + +} diff --git a/ClashX/ViewControllers/Connections/DashboardSubViewControllerProtocol.swift b/ClashX/ViewControllers/Connections/DashboardSubViewControllerProtocol.swift new file mode 100644 index 000000000..548703a5d --- /dev/null +++ b/ClashX/ViewControllers/Connections/DashboardSubViewControllerProtocol.swift @@ -0,0 +1,13 @@ +// +// DashboardSubViewControllerProtocol.swift +// ClashX +// +// Created by yicheng on 2023/7/14. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +protocol DashboardSubViewControllerProtocol: NSViewController { + func actionSearch(string:String) +} diff --git a/ClashX/ViewControllers/Connections/DashboardViewController.swift b/ClashX/ViewControllers/Connections/DashboardViewController.swift new file mode 100644 index 000000000..affdb9862 --- /dev/null +++ b/ClashX/ViewControllers/Connections/DashboardViewController.swift @@ -0,0 +1,129 @@ +// +// DashboardViewController.swift +// ClashX +// +// Created by yicheng on 2023/7/14. +// Copyright © 2023 west2online. All rights reserved. +// + +import Cocoa + +enum DashboardContentType: Int, CaseIterable { + case allConnection + case activeConnection + + var title: String { + switch self { + case .allConnection: + return NSLocalizedString("Recent Connections", comment:"") + case .activeConnection: + return NSLocalizedString("Active Connections", comment:"") + } + } +} + +@available(macOS 10.15, *) +class DashboardViewController: NSViewController { + private let toolbar = NSToolbar() + private var segmentControl: NSSegmentedControl! + private let searchField = NSSearchField() + + private let connectionVC = ConnectionsViewController() + + private var currentContentVC: DashboardSubViewControllerProtocol? + + override func viewDidLoad() { + super.viewDidLoad() + segmentControl = NSSegmentedControl(labels: DashboardContentType.allCases.map {$0.title}, + trackingMode: .selectOne, + target: self, + action: #selector(actionSwitchSegmentControl(sender: ))) + segmentControl.selectedSegment = 0 + searchField.delegate = self + setCurrentVC(connectionVC) + } + + override func loadView() { + view = NSView() + } + + override func viewWillAppear() { + super.viewWillAppear() + toolbar.delegate = self + view.window?.toolbar = toolbar + view.window?.styleMask.insert(.closable) + view.window?.styleMask.insert(.resizable) + view.window?.styleMask.insert(.miniaturizable) + view.window?.backgroundColor = NSColor.clear + if #available(macOS 11.0, *) { + view.window?.toolbarStyle = .unifiedCompact + } else { + view.window?.toolbar?.sizeMode = .small + } + } + + func setCurrentVC(_ vc: DashboardSubViewControllerProtocol) { + currentContentVC?.removeFromParent() + currentContentVC?.view.removeFromSuperview() + addChild(vc) + view.addSubview(vc.view) + vc.view.makeConstraintsToBindToSuperview() + currentContentVC = vc + } + + @objc func actionSwitchSegmentControl(sender: NSSegmentedControl) { + guard let contentType = DashboardContentType(rawValue: sender.selectedSegment) else { return } + switch contentType { + case .allConnection: + connectionVC.setActiveMode(enable: false) + case .activeConnection: + connectionVC.setActiveMode(enable: true) + } + } +} + +@available(macOS 10.15, *) +extension DashboardViewController:NSSearchFieldDelegate { + func controlTextDidChange(_ obj: Notification) { + if let textField = obj.object as? NSTextField { + currentContentVC?.actionSearch(string: textField.stringValue) + } + } + + func searchFieldDidEndSearching(_ sender: NSSearchField) { + currentContentVC?.actionSearch(string: sender.stringValue) + } +} + +extension NSToolbarItem.Identifier { + static let toolbarSearchItem = NSToolbarItem.Identifier("ToolbarSearchItem") + static let toolbarSegmentItem = NSToolbarItem.Identifier("toolbarSegmentItem") +} + +@available(macOS 10.15, *) +extension DashboardViewController: NSToolbarDelegate { + func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return [.toolbarSegmentItem, .flexibleSpace, .toolbarSearchItem] + } + + func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return [.toolbarSegmentItem, .flexibleSpace, .toolbarSearchItem] + } + + func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { + let item = NSToolbarItem(itemIdentifier: itemIdentifier) + if itemIdentifier == .toolbarSearchItem { + item.maxSize = NSSize(width: 200, height: 40) + searchField.sizeToFit() + item.view = searchField + } else if itemIdentifier == .toolbarSegmentItem { + if #available(macOS 11.0, *) { + item.isNavigational = true + } + item.minSize = CGSize(width: 300, height: 34) + item.view = segmentControl + } + + return item + } +} diff --git a/ClashX/ViewControllers/Connections/Requests/ConnectionsReq.swift b/ClashX/ViewControllers/Connections/Requests/ConnectionsReq.swift new file mode 100644 index 000000000..c742ae099 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Requests/ConnectionsReq.swift @@ -0,0 +1,54 @@ +// +// ConnectionsReq.swift +// ClashX +// +// Created by yicheng on 2023/7/14. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation +import Starscream + +@available(macOS 10.15, *) +class ConnectionsReq:WebSocketDelegate { + private var socket:WebSocket? + + let decoder = JSONDecoder() + var onSnapshotUpdate:((ClashConnectionSnapShot) -> Void)? + init() { + if let url = URL(string: ConfigManager.apiUrl.appending("/connections")) { + socket = WebSocket(url: url) + } + for header in ApiRequest.authHeader() { + socket?.request.setValue(header.value, forHTTPHeaderField: header.name) + } + socket?.delegate = self + decoder.dateDecodingStrategy = .formatted(DateFormatter.js) + } + + func connect() { + socket?.connect() + } + + func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { + if let data = text.data(using: .utf8) { + do { + let info = try decoder.decode(ClashConnectionSnapShot.self, from: data) + onSnapshotUpdate?(info) + } catch let err { + Logger.log("decode fail: \(err)", level: .warning) + } + } + } + + func websocketDidConnect(socket: Starscream.WebSocketClient) { + Logger.log("websocketDidConnect") + } + + func websocketDidDisconnect(socket: Starscream.WebSocketClient, error: Error?) { + Logger.log("websocketDidDisconnect: \(String(describing: error))", level: .warning) + + } + + func websocketDidReceiveData(socket: Starscream.WebSocketClient, data: Data) {} +} diff --git a/ClashX/ViewControllers/Connections/Requests/StructedLogReq.swift b/ClashX/ViewControllers/Connections/Requests/StructedLogReq.swift new file mode 100644 index 000000000..b43e2b891 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Requests/StructedLogReq.swift @@ -0,0 +1,146 @@ +// +// StructedLogReq.swift +// ClashX +// +// Created by yicheng on 2023/7/14. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation +import Starscream +import Combine + +class StructedLog:Codable { + class Pairs:Codable { + let key:String + let value:String + } + let time:String + let level:String + let message:String + let fields:[Pairs] + + func convertToConn() -> LogConn? { + let isTCP = message.starts(with: "[TCP]") + let isUDP = message.starts(with: "[UDP]") + if isTCP || isUDP { + let conn = LogConn() + conn.network = isTCP ? "tcp" : "udp" + for field in fields { + switch field.key { + case "lAddr": + conn.localAddr = field.value + case "rAddr": + conn.remoteAddr = field.value + case "mode": + conn.mode = field.value + case "rule": + conn.rule = field.value + case "proxy": + conn.proxy = field.value + case "rulePayload": + conn.rulePayload = field.value + case "error": + conn.error = field.value + default: + break + } + } + return conn + } + return nil + } +} + +class LogConn { + let time = Date() + var localAddr: String = "" + var remoteAddr: String = "" + var mode: String = "" + var rule: String = "" + var proxy: String = "" + var rulePayload: String = "" + var error: String = "" + var network: String = "" + + @available(macOS 10.15, *) + func toConn() -> ClashConnectionSnapShot.Connection { + let sourceInfos = localAddr.split(separator: ":") + let remoteInfos = remoteAddr.split(separator: ":") + let metaData = ClashConnectionSnapShot.MetaData(network: network, + type: "log", + sourceIP: String(sourceInfos.first ?? ""), + destinationIP: String(remoteInfos.first ?? ""), + sourcePort: String(sourceInfos.last ?? ""), + destinationPort: String(remoteInfos.last ?? ""), + host: String(remoteInfos.first ?? ""), + dnsMode: "", + specialProxy: nil, + processPath: "") + + let conn = ClashConnectionSnapShot.Connection(id: UUID().uuidString, chains: [proxy], meta: metaData, upload: 0, download: 0, start: time, rule: rule, rulePayload: rulePayload) + if !error.isEmpty { + conn.status = .fail + conn.error = error + } else { + conn.status = .finished + } + return conn + } +} + +@available(macOS 10.15, *) +class StructedLogReq: WebSocketDelegate { +/* + {"time":"22:44:32","level":"warn","message":"[TCP] dial failed", + "fields":[{"key":"error","value":"dial tcp4 1.2.4.6:80: i/o timeout"}, + {"key":"proxy","value":"Domestic"}, + {"key":"lAddr","value":"127.0.0.1:49790"}, + {"key":"rAddr","value":"1.2.4.6:80"}, + {"key":"rule","value":"GeoIP"}, + {"key":"rulePayload","value":"CN"}] + } + {"time":"22:42:09","level":"debug","message":"[TUN] hijack udp dns","fields":[{"key":"addr","value":"198.18.0.2:53"}]} + */ + let logLevel = ClashLogLevel.info + private var socket:WebSocket? + + let decoder = JSONDecoder() + + let onLogUpdate = PassthroughSubject() + init(level: ClashLogLevel = .warning) { + if let url = URL(string: ConfigManager.apiUrl.appending("/logs?format=structured&level=\(logLevel.rawValue)")) { + socket = WebSocket(url: url) + } + for header in ApiRequest.authHeader() { + socket?.request.setValue(header.value, forHTTPHeaderField: header.name) + } + socket?.delegate = self + decoder.dateDecodingStrategy = .formatted(DateFormatter.js) + } + + func connect() { + socket?.connect() + } + + func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { + if let data = text.data(using: .utf8) { + do { + let info = try decoder.decode(StructedLog.self, from: data) + onLogUpdate.send(info) + } catch let err { + Logger.log("decode fail: \(err)", level: .warning) + } + } + } + + func websocketDidConnect(socket: Starscream.WebSocketClient) { + Logger.log("websocketDidConnect") + } + + func websocketDidDisconnect(socket: Starscream.WebSocketClient, error: Error?) { + Logger.log("websocketDidDisconnect: \(String(describing: error))", level: .warning) + } + + func websocketDidReceiveData(socket: Starscream.WebSocketClient, data: Data) {} +} diff --git a/ClashX/ViewControllers/Connections/ViewModels/ConnectionDetailViewModel.swift b/ClashX/ViewControllers/Connections/ViewModels/ConnectionDetailViewModel.swift new file mode 100644 index 000000000..918eb91ed --- /dev/null +++ b/ClashX/ViewControllers/Connections/ViewModels/ConnectionDetailViewModel.swift @@ -0,0 +1,93 @@ +// +// ConnectionDetailViewModel.swift +// ClashX +// +// Created by yicheng on 2023/7/8. +// Copyright © 2023 west2online. All rights reserved. +// + +import Combine +import AppKit + +@available(macOS 10.15, *) +class ConnectionDetailViewModel { + @Published var processName = "" + @Published var processImage:NSImage? + @Published var remoteHost = "" + + @Published var entry = "" + @Published var networkType = "" + @Published var totalUpload = "" + @Published var totalDownload = "" + @Published var maxUpload = "" + @Published var maxDownload = "" + @Published var currentUpload = "" + @Published var currentDownload = "" + @Published var rule = "" + @Published var chain = "" + @Published var sourceIP = "" + @Published var destination = "" + @Published var applicationPath:String? = "" + @Published var otherText = "" + @Published var showCloseButton = false + + private var uuid = "" + var cancellable = Set() + + func accept(connection: ClashConnectionSnapShot.Connection?) { + cancellable.removeAll() + guard let connection else { return } + if let pid = connection.metadata.pid { + processName = "\(connection.metadata.processName ?? NSLocalizedString("Unknown", comment: "")) (\(pid))" + } else { + processName = connection.metadata.processName ?? NSLocalizedString("Unknown", comment: "") + } + uuid = connection.id + showCloseButton = connection.status == .connecting + processImage = connection.metadata.processImage + applicationPath = connection.metadata.processPath + let area = clash_getCountryForIp(connection.metadata.destinationIP.goStringBuffer()).toString() + let areaString = "\(flag(from: area))\(area)" + if connection.metadata.host.isEmpty { + remoteHost = "\(connection.metadata.destinationIP):\(connection.metadata.destinationPort) \(areaString)" + } else { + remoteHost = "\(connection.metadata.host):\(connection.metadata.destinationPort) \(areaString)" + } + + entry = connection.metadata.type + networkType = connection.metadata.network + + connection.$download.map {SpeedUtils.getNetString(for: $0)}.weakAssign(to: \.totalDownload, on: self).store(in: &cancellable) + connection.$upload.map {SpeedUtils.getNetString(for: $0)}.weakAssign(to: \.totalUpload, on: self).store(in: &cancellable) + + connection.$maxUploadSpeed.map {SpeedUtils.getSpeedString(for: $0)}.weakAssign(to: \.maxUpload, on: self).store(in: &cancellable) + connection.$maxDownloadSpeed.map {SpeedUtils.getSpeedString(for: $0)}.weakAssign(to: \.maxDownload, on: self).store(in: &cancellable) + + connection.$uploadSpeed.map {SpeedUtils.getSpeedString(for: $0)}.weakAssign(to: \.currentUpload, on: self).store(in: &cancellable) + connection.$downloadSpeed.map {SpeedUtils.getSpeedString(for: $0)}.weakAssign(to: \.currentDownload, on: self).store(in: &cancellable) + + rule = connection.rule + "\n" + connection.rulePayload + chain = connection.chains.joined(separator: "\n") + sourceIP = connection.metadata.sourceIP.appending(":").appending(connection.metadata.sourcePort) + destination = connection.metadata.destinationIP.appending(":").appending(connection.metadata.destinationPort) + if let error = connection.error { + otherText = error + } else { + otherText = "" + } + } + + func flag(from country:String) -> String { + if country.isEmpty { return "" } + let base : UInt32 = 127397 + var s = "" + for v in country.uppercased().unicodeScalars { + s.unicodeScalars.append(UnicodeScalar(base + v.value)!) + } + return s + } + + func closeConnection() { + ApiRequest.closeConnection(uuid) + } +} diff --git a/ClashX/ViewControllers/Connections/ViewModels/ConnectionLeftPannelViewModel.swift b/ClashX/ViewControllers/Connections/ViewModels/ConnectionLeftPannelViewModel.swift new file mode 100644 index 000000000..a45af17f1 --- /dev/null +++ b/ClashX/ViewControllers/Connections/ViewModels/ConnectionLeftPannelViewModel.swift @@ -0,0 +1,113 @@ +// +// ConnectionLeftPannelViewModel.swift +// ClashX +// +// Created by miniLV on 2023-07-10. +// Copyright © 2023 west2online. All rights reserved. +// + +import Combine + +@available(macOS 10.15, *) +class ConnectionLeftPannelViewModel { + + enum Section: Int, CaseIterable { + case all + case local + case remote + case hosts + } + + private(set) var currentSections = [Section.all, Section.local, Section.remote] + private(set) var localApplications = [ConnectionApplication]() + private(set) var sources = [String]() + private(set) var hosts = [String]() + private(set) var isHostMode = false + var onReloadTable:((IndexPath) -> Void)? + var onSelectedFilter:((ConnectionFilter?) -> Void)? + var selectedFilter:ConnectionFilter? + + func accept(connections new: [ConnectionApplication]) { + var dupSet = Set() + localApplications = new.filter {dupSet.insert($0.path ?? $0.pid).inserted} + .sorted(by: {$0.name ?? "" < $1.name ?? ""}) + if !isHostMode { + onReloadTable?(getSelectedIndexPath()) + } + } + + func accept(sources new: [String]) { + sources = new.sorted() + if !isHostMode { + onReloadTable?(getSelectedIndexPath()) + } + } + + func accept(hosts new: [String]) { + hosts = new.sorted() + if isHostMode { + onReloadTable?(getSelectedIndexPath()) + } + } + + func accept(apps:[ConnectionApplication], sources: [String], hosts: [String]) { + var dupSet = Set() + self.localApplications = apps.filter {dupSet.insert($0.path ?? $0.pid).inserted} + .sorted(by: {$0.name ?? "" < $1.name ?? ""}) + self.sources = sources.sorted() + self.hosts = hosts.sorted() + onReloadTable?(getSelectedIndexPath()) + } + + func setHostMode(enable:Bool) { + isHostMode = enable + selectedFilter = nil + onSelectedFilter?(nil) + currentSections = enable ? [.all, .hosts] : [.all, .local, .remote] + onReloadTable?(getSelectedIndexPath()) + } + + func getSelectedIndexPath() -> IndexPath { + switch selectedFilter { + case .none: + break + case .application(let path): + if let idx = localApplications.firstIndex(where: {($0.path ?? $0.pid) == path}) { + return IndexPath(item: idx, section: 1) + } + case .source(let ip): + if let idx = sources.firstIndex(where: {$0 == ip}) { + return IndexPath(item: idx, section: 2) + } + case .hosts(let name): + if let idx = hosts.firstIndex(where: {$0 == name}) { + return IndexPath(item: idx, section: 1) + } + } + return IndexPath(item: 0, section: 0) + + } + + func setSelect(indexPath:IndexPath) { + if indexPath.item < 0 || indexPath.section < 0 { + selectedFilter = nil + onSelectedFilter?(nil) + return + } + + let type = currentSections[indexPath.section] + switch type { + case Section.local: + let app = localApplications[indexPath.item] + selectedFilter = .application(path: app.path ?? app.pid) + case Section.remote: + selectedFilter = .source(ip: sources[indexPath.item]) + case .hosts: + selectedFilter = .hosts(name: hosts[indexPath.item]) + case .all: + selectedFilter = nil + } + onSelectedFilter?(selectedFilter) + } + +} diff --git a/ClashX/ViewControllers/Connections/ViewModels/ConnectionTopListViewModel.swift b/ClashX/ViewControllers/Connections/ViewModels/ConnectionTopListViewModel.swift new file mode 100644 index 000000000..dc6a9f74a --- /dev/null +++ b/ClashX/ViewControllers/Connections/ViewModels/ConnectionTopListViewModel.swift @@ -0,0 +1,123 @@ +// +// ConnectionTopListViewModel.swift +// ClashX +// +// Created by yicheng on 2023/7/8. +// Copyright © 2023 west2online. All rights reserved. +// + +import Combine + +@available(macOS 10.15, *) +class ConnectionTopListViewModel { + private var fullConnections = [ClashConnectionSnapShot.Connection]() + private(set) var connections = [ClashConnectionSnapShot.Connection]() + private var selectedUUIDs = [String]() + private var updateDebounceDate = Date() + + var onReloadTable:(() -> Void)? + var onSelectedConnection:((ClashConnectionSnapShot.Connection?) -> Void)? + var applicationFilter: ConnectionFilter? { + didSet { + updateData() + } + } + + var textFilter: String? { + didSet { + updateData() + } + } + + var currentSortDescriptor: NSSortDescriptor? { + didSet { + updateData() + } + } + + func accept(connections new: [ClashConnectionSnapShot.Connection]) { + fullConnections = new + updateData() + } + + func connectionDidUpdate() { + if let key = currentSortDescriptor?.key, ConnectionColume.isDynamicSort(for: key) { + updateData(applyDebounce: true) + } + + } + + func currentSelection() -> IndexSet { + let indexs = selectedUUIDs.compactMap { uuid in connections.firstIndex(where: { $0.id == uuid }) } + return IndexSet(indexs) + } + + func closeConnection(for indexs: IndexSet) { + for idx in indexs { + let conn = connections[idx] + ApiRequest.closeConnection(conn.id) + } + } + + private func updateData(applyDebounce:Bool=false) { + let current = Date() + if applyDebounce, current.timeIntervalSince(updateDebounceDate) < 0.2 { + return + } + updateDebounceDate = current + connections = fullConnections + + switch applicationFilter { + case .none: + break + case .application(let pathOrPid): + connections = connections.filter { conn in + conn.metadata.processPath == pathOrPid || conn.metadata.pid == pathOrPid + } + + case .source(let ip): + connections = connections.filter { conn in + conn.metadata.sourceIP == ip + } + case .hosts(let name): + connections = connections.filter { conn in + conn.metadata.displayHost == name + } + } + + if let textFilter = textFilter?.lowercased(), !textFilter.isEmpty { + connections = connections.filter { conn in + conn.metadata.displayHost.contains(textFilter) || + conn.metadata.network.contains(textFilter) || + conn.chains.joined().lowercased().contains(textFilter) || + conn.metadata.processName?.lowercased().contains(textFilter) ?? false || + conn.rule.lowercased().contains(textFilter) + } + } + + connections = (connections as NSArray).sortedArray(using: [currentSortDescriptor].compactMap {$0}) as! [ClashConnectionSnapShot.Connection] + onReloadTable?() + + } + + func setSelect(row: IndexSet) { + selectedUUIDs = row.map { connections[$0].id } + if selectedUUIDs.count == 1, let idx = row.first { + onSelectedConnection?(connections[idx]) + } else { + onSelectedConnection?(nil) + } + } + + func sortSortDescriptor(for columnType: ConnectionColume) -> NSSortDescriptor? { + if let keypath = columnType.compareKeyPath { + let sort = NSSortDescriptor(key: keypath, ascending: true) + if columnType == .date { + currentSortDescriptor = sort + } + return sort + } + return nil + } + +} diff --git a/ClashX/ViewControllers/Connections/ViewModels/ConnectionsViewModel.swift b/ClashX/ViewControllers/Connections/ViewModels/ConnectionsViewModel.swift new file mode 100644 index 000000000..d586a42b6 --- /dev/null +++ b/ClashX/ViewControllers/Connections/ViewModels/ConnectionsViewModel.swift @@ -0,0 +1,190 @@ +// +// ConnectionsViewModel.swift +// ClashX +// +// Created by yicheng on 2023/7/5. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation +import AppKit +import Combine + +struct ConnectionApplication { + let pid: String + let image:NSImage? + let name:String? + let path:String? +} + +@available(macOS 10.15, *) +class ConnectionsViewModel { + @Published private(set) var applicationMap = [String: ConnectionApplication]() + @Published private(set) var connections = [String:ClashConnectionSnapShot.Connection]() + @Published var selectedConnection: ClashConnectionSnapShot.Connection? { + didSet { + showBottomView = selectedConnection != nil + } + } + @Published private(set) var sourceIPs = Set() + @Published private(set) var hosts = Set() + @Published var showBottomView = false + let connectionDataDidRefresh = PassthroughSubject() + var activeOnlyMode = false { + didSet { + if activeOnlyMode, let currentSnapShot { + updateForActiveMode(snapShot: currentSnapShot) + } + } + } + private let unknownApplicationPlaceHolder = ConnectionApplication(pid: "-1", image: nil, name: NSLocalizedString("Unknown", comment: ""), path: "") + + private var cancellable = Set() + private(set) var currentApplications = [ConnectionApplication]() + private(set) var currentSourceIPs = [String]() + private(set) var currentHosts = [String]() + private(set) var currentConnections = [ClashConnectionSnapShot.Connection]() + private var currentSnapShot: ClashConnectionSnapShot? + private let req = ConnectionsReq() + private let logReq = StructedLogReq() + private var verifyConnList = [LogConn]() + init() { + req.connect() + logReq.connect() + req.onSnapshotUpdate = { + [weak self] snap in + self?.update(snapShot: snap) + } + logReq.onLogUpdate.compactMap({$0.convertToConn()}).sink { [weak self] conn in + self?.verifyConnList.append(conn) + }.store(in: &cancellable) + } + + func update(snapShot:ClashConnectionSnapShot) { + currentSnapShot = snapShot + defer { + connectionDataDidRefresh.send() + } + let keys = Set(snapShot.connections.map { $0.id }) + for key in connections.keys where !keys.contains(key) { + if let conn = connections[key] { + if conn.status == .connecting { + conn.status = .finished + } + conn.uploadSpeed = 0 + conn.downloadSpeed = 0 + } + + } + let lAddrs = Set(snapShot.connections.map { $0.metadata.sourceIP.appending(":").appending($0.metadata.sourcePort) }) + + for logCon in verifyConnList where !lAddrs.contains(logCon.localAddr) { + snapShot.connections.append(logCon.toConn()) + } + var processMap:[String:String]? + for conn in snapShot.connections { + if let oldConn = connections[conn.id] { + oldConn.uploadSpeed = conn.upload - oldConn.upload + oldConn.downloadSpeed = conn.download - oldConn.download + oldConn.upload = conn.upload + oldConn.download = conn.download + } else { + if processMap == nil { + processMap = getProcessList() + } + let key = conn.metadata.sourceIP.appending(conn.metadata.sourcePort) + if let pid = processMap![key], + let info = getProgressInfo(pid: pid) { + conn.metadata.pid = pid + conn.metadata.processPath = info.path ?? "" + conn.metadata.processName = info.name + conn.metadata.processImage = info.image + } else if !conn.metadata.processPath.isEmpty { + conn.metadata.processName = conn.metadata.processPath.components(separatedBy: "/").last + conn.metadata.processImage = NSWorkspace.shared.icon(forFile:conn.metadata.processPath) + } else { + if applicationMap["-1"] == nil { + applicationMap["-1"] = unknownApplicationPlaceHolder + } + conn.metadata.pid = unknownApplicationPlaceHolder.pid + conn.metadata.processName = unknownApplicationPlaceHolder.name + } + + connections[conn.id] = conn + if !sourceIPs.contains(conn.metadata.sourceIP) { + sourceIPs.insert(conn.metadata.sourceIP) + } + if !hosts.contains(conn.metadata.displayHost) { + hosts.insert(conn.metadata.displayHost) + } + } + } + + if activeOnlyMode { + updateForActiveMode(snapShot: snapShot) + } + + verifyConnList.removeAll() + + } + + private func updateForActiveMode(snapShot: ClashConnectionSnapShot) { + currentConnections = snapShot.connections.compactMap({ [weak self] in self?.connections[$0.id] }) + currentHosts = Array(Set(currentConnections.map {$0.metadata.displayHost})) + currentSourceIPs = Array(Set(currentConnections.map {$0.metadata.sourceIP})) + currentApplications = Array(Set(currentConnections.compactMap {$0.metadata.pid}).compactMap({[weak self] in self?.applicationMap[$0]})) + } + + private func getProcessList() -> [String:String] { + let tableString:String = clash_getProggressInfo().toString().trimmingCharacters(in: .whitespacesAndNewlines) + if tableString.isEmpty { return [:] } + let processList = tableString.components(separatedBy: "\n") + var map = [String:String]() + for process in processList { + let infos = process.components(separatedBy: " ") + // fmt.Sprintf("%s %d %d\n", srcIP, srcPort, pid) + let srcIp = infos[0] + let srcPort = infos[1] + let pid = infos[2] + map[srcIp.appending(srcPort)] = pid + } + return map + } + + private func getProcessPath(pid:Int32) -> String? { + let pathBuffer = UnsafeMutablePointer.allocate(capacity: Int(MAXPATHLEN)) + defer { + pathBuffer.deallocate() + } + let pathLength = proc_pidpath(pid, pathBuffer, UInt32(MAXPATHLEN)) + if pathLength > 0 { + let path = String(cString: pathBuffer) + return path + } + return nil + } + + private func getProgressInfo(pid:String) -> ConnectionApplication? { + if let info = applicationMap[pid] { + return info + } + guard let pidValue = Int32(pid) else { return nil } + + if let application = NSRunningApplication(processIdentifier: pidValue) { + let info = ConnectionApplication(pid:pid, + image: application.icon, + name: application.localizedName, + path: application.executableURL?.absoluteString) + applicationMap[pid] = info + return info + } + + guard let path = getProcessPath(pid: pidValue) else { return nil } + let info = ConnectionApplication(pid:pid, + image: NSWorkspace.shared.icon(forFile:path), + name: path.components(separatedBy: "/").last, + path: path) + applicationMap[pid] = info + return info + } +} diff --git a/ClashX/ViewControllers/Connections/Views/Base.lproj/ConnectionDetailInfoGeneralView.xib b/ClashX/ViewControllers/Connections/Views/Base.lproj/ConnectionDetailInfoGeneralView.xib new file mode 100644 index 000000000..975b16f2b --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/Base.lproj/ConnectionDetailInfoGeneralView.xib @@ -0,0 +1,713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ClashX/ViewControllers/Connections/Views/Cell/ConnectionCellProtocol.swift b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionCellProtocol.swift new file mode 100644 index 000000000..ba3e8b0a5 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionCellProtocol.swift @@ -0,0 +1,15 @@ +// +// ConnectionCellProtocol.swift +// ClashX +// +// Created by yicheng on 2023/7/6. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +@available(macOS 10.15, *) +protocol ConnectionCellProtocol: NSView { + func setup(with connection: ClashConnectionSnapShot.Connection, type: ConnectionColume) + +} diff --git a/ClashX/ViewControllers/Connections/Views/Cell/ConnectionLeftTextCellView.swift b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionLeftTextCellView.swift new file mode 100644 index 000000000..6352277cd --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionLeftTextCellView.swift @@ -0,0 +1,113 @@ +// +// ApplicationLocalClientCellView.swift +// ClashX +// +// Created by miniLV on 2023-07-10. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +@available(macOS 10.15, *) +class ConnectionApplicationCellView: NSView { + let imageView = NSImageView() + let nameLabel = NSTextField(labelWithString: "") + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + } + + func setupUI() { + addSubview(nameLabel) + addSubview(imageView) + nameLabel.font = NSFont.systemFont(ofSize: 13) + imageView.makeConstraints { + [$0.heightAnchor.constraint(equalToConstant: 23), + $0.widthAnchor.constraint(equalTo: $0.heightAnchor), + $0.centerYAnchor.constraint(equalTo: centerYAnchor), + $0.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 6)] + } + nameLabel.makeConstraints { + [ + $0.centerYAnchor.constraint(equalTo: centerYAnchor), + $0.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 5), + $0.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor) + ] + } + nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + nameLabel.cell?.truncatesLastVisibleLine = true + } + + func setup(with connection: ConnectionApplication) { + nameLabel.stringValue = connection.name ?? NSLocalizedString("Unknown", comment: "") + imageView.image = connection.image + } +} + +class ConnectionLeftTextCellView: NSView { + let nameLabel = NSTextField(labelWithString: "") + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + } + + func setupUI() { + addSubview(nameLabel) + nameLabel.font = NSFont.systemFont(ofSize: 13) + nameLabel.makeConstraints { + [ + $0.centerYAnchor.constraint(equalTo: centerYAnchor), + $0.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 6), + $0.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor) + ] + } + nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + nameLabel.cell?.truncatesLastVisibleLine = true + } + + func setup(with text: String) { + nameLabel.stringValue = text + } +} + +class ApplicationClientSectionCell: NSTableCellView { + let titleLabel = NSTextField(labelWithString: "") + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + } + + func setupUI() { + addSubview(titleLabel) + titleLabel.font = NSFont.systemFont(ofSize: 10) + titleLabel.textColor = NSColor.secondaryLabelColor + titleLabel.makeConstraints { + [ + $0.centerYAnchor.constraint(equalTo: centerYAnchor), + $0.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0), + $0.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor) + ] + } + titleLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + titleLabel.cell?.truncatesLastVisibleLine = true + } + + func setup(with title: String) { + titleLabel.stringValue = title + } +} diff --git a/ClashX/ViewControllers/Connections/Views/Cell/ConnectionProxyClientCellView.swift b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionProxyClientCellView.swift new file mode 100644 index 000000000..046ef4c4d --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionProxyClientCellView.swift @@ -0,0 +1,50 @@ +// +// ConnectionProxyClientCellView.swift +// ClashX +// +// Created by yicheng on 2023/7/6. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +@available(macOS 10.15, *) +class ConnectionProxyClientCellView: NSView, ConnectionCellProtocol { + let imageView = NSImageView() + let nameLabel = NSTextField(labelWithString: "") + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + } + + func setupUI() { + addSubview(nameLabel) + addSubview(imageView) + nameLabel.font = NSFont.systemFont(ofSize: 12) + imageView.makeConstraints { + [$0.heightAnchor.constraint(equalToConstant: 18), + $0.widthAnchor.constraint(equalTo: $0.heightAnchor), + $0.centerYAnchor.constraint(equalTo: centerYAnchor), + $0.leadingAnchor.constraint(equalTo: leadingAnchor)] + } + nameLabel.makeConstraints { + [ + $0.centerYAnchor.constraint(equalTo: centerYAnchor), + $0.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 4), + $0.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor) + ] + } + nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + nameLabel.cell?.truncatesLastVisibleLine = true + } + + func setup(with connection: ClashConnectionSnapShot.Connection, type: ConnectionColume) { + nameLabel.stringValue = connection.metadata.processName ?? NSLocalizedString("Unknown", comment: "") + imageView.image = connection.metadata.processImage + } +} diff --git a/ClashX/ViewControllers/Connections/Views/Cell/ConnectionStatusIconCellView.swift b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionStatusIconCellView.swift new file mode 100644 index 000000000..f1f27e917 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionStatusIconCellView.swift @@ -0,0 +1,49 @@ +// +// ConnectionStatusIconCellView.swift +// ClashX +// +// Created by yicheng on 2023/7/6. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import Combine + +@available(macOS 10.15, *) +class ConnectionStatusIconCellView: NSView, ConnectionCellProtocol { + let imageView = NSImageView() + var cancellable = Set() + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + } + + func setupUI() { + addSubview(imageView) + imageView.makeConstraints { + [$0.heightAnchor.constraint(equalToConstant: 18), + $0.widthAnchor.constraint(equalTo: $0.heightAnchor), + $0.centerYAnchor.constraint(equalTo: centerYAnchor), + $0.leadingAnchor.constraint(equalTo: leadingAnchor)] + } + } + + func setup(with connection: ClashConnectionSnapShot.Connection, type: ConnectionColume) { + cancellable.removeAll() + connection + .$status + .map {$0.image } + .weakAssign(to: \.image, on: imageView) + .store(in: &cancellable) + } + + override func prepareForReuse() { + super.prepareForReuse() + cancellable.removeAll() + } +} diff --git a/ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift new file mode 100644 index 000000000..fe4a89247 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift @@ -0,0 +1,65 @@ +// +// ConnectionTextCellView.swift +// ClashX +// +// Created by yicheng on 2023/7/6. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import Combine + +@available(macOS 10.15, *) +class ConnectionTextCellView: NSView, ConnectionCellProtocol { + let label = NSTextField(labelWithString: "") + var cancellable = Set() + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupUI() + } + + func setupUI() { + addSubview(label) + label.font = NSFont.systemFont(ofSize: 12) + label.makeConstraints { + [$0.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0), + $0.centerYAnchor.constraint(equalTo: centerYAnchor)] + } + } + + func setup(with connection: ClashConnectionSnapShot.Connection, type: ConnectionColume) { + cancellable.removeAll() + switch type { + case .upload: + connection.$upload.map { SpeedUtils.getNetString(for: $0) }.weakAssign(to: \.stringValue, on: label).store(in: &cancellable) + case .download: + connection.$download.map { SpeedUtils.getNetString(for: $0) }.weakAssign(to: \.stringValue, on: label).store(in: &cancellable) + case .currentUpload: + connection.$uploadSpeed.map { SpeedUtils.getSpeedString(for: $0) }.weakAssign(to: \.stringValue, on: label).store(in: &cancellable) + case .currentDownload: + connection.$downloadSpeed.map { SpeedUtils.getSpeedString(for: $0) }.weakAssign(to: \.stringValue, on: label).store(in: &cancellable) + case .status: + connection.$status.map { $0.title }.weakAssign(to: \.stringValue, on: label).store(in: &cancellable) + case .statusIcon, .process: + return + case .rule: + label.stringValue = connection.chains.joined(separator: "/") + case .date: + label.stringValue = DateFormatter.simple.string(from: connection.start) + case .url: + label.stringValue = connection.metadata.displayHost + case .type: + label.stringValue = connection.metadata.network + } + } + + override func prepareForReuse() { + super.prepareForReuse() + cancellable.removeAll() + } +} diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionColume.swift b/ClashX/ViewControllers/Connections/Views/ConnectionColume.swift new file mode 100644 index 000000000..0ddb2a0a3 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/ConnectionColume.swift @@ -0,0 +1,79 @@ +// +// ColumeType.swift +// ClashX +// +// Created by yicheng on 2023/7/6. +// Copyright © 2023 west2online. All rights reserved. +// + +enum ConnectionFilter { + case application(path:String) + case source(ip:String) + case hosts(name:String) +} + +@available(macOS 10.15, *) +enum ConnectionColume:String, CaseIterable { + case statusIcon + case process + case status + case url + case rule + case date + case upload + case download + case currentUpload + case currentDownload + case type + + var columeTitle:String { + switch self { + case .statusIcon: return "" + case .process: return NSLocalizedString("Client", comment: "") + case .status: return NSLocalizedString("Status", comment: "") + case .rule: return NSLocalizedString("Rule", comment: "") + case .url: return NSLocalizedString("Host", comment: "") + case .date: return NSLocalizedString("Date", comment: "") + case .upload: return NSLocalizedString("Upload", comment: "") + case .download: return NSLocalizedString("Download", comment: "") + case .currentUpload: return NSLocalizedString("Upload speed", comment: "") + case .currentDownload: return NSLocalizedString("Download speed", comment: "") + case .type: return NSLocalizedString("Type", comment: "") + } + } + + var compareKeyPath: String? { + switch self { + case .statusIcon, .status: return "status" + case .process: return "metadata.processName" + case .rule: return "rule" + case .url: return "metadata.displayHost" + case .date: return "start" + case .upload: return "upload" + case .download: return "download" + case .currentUpload: return "uploadSpeed" + case .currentDownload: return "downloadSpeed" + case .type:return "metadata.network" + } + } + + static func isDynamicSort(for keypath: String) -> Bool { + return keypath == "upload" || keypath == "download" || keypath == "uploadSpeed" || keypath == "downloadSpeed" || keypath == "done" + } + + var minWidth:CGFloat { + switch self { + case .statusIcon: return 16 + case .status: return 30 + default:return 60 + } + } + + var maxWidth:CGFloat { + switch self { + case .statusIcon: return 16 + default:return CGFloat.greatestFiniteMagnitude + } + } + +} diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoGeneralView.swift b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoGeneralView.swift new file mode 100644 index 000000000..218d19dac --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoGeneralView.swift @@ -0,0 +1,32 @@ +// +// ConnectionDetailInfoGeneralView.swift +// ClashX +// +// Created by yicheng on 2023/7/8. +// Copyright © 2023 west2online. All rights reserved. +// + +import Cocoa + +class ConnectionDetailInfoGeneralView: NSView, NibLoadable { + + @IBOutlet weak var entryLabel: NSTextField! + @IBOutlet weak var networkTypeLabel: NSTextField! + @IBOutlet weak var totalUploadLabel: NSTextField! + @IBOutlet weak var totalDownloadLabel: NSTextField! + @IBOutlet weak var maxUploadLabel: NSTextField! + @IBOutlet weak var maxDownloadLabel: NSTextField! + @IBOutlet weak var currentUploadLabel: NSTextField! + @IBOutlet weak var currentDownloadLabel: NSTextField! + + @IBOutlet weak var ruleLabel: NSTextField! + @IBOutlet weak var proxyChainLabel: NSTextField! + @IBOutlet weak var otherTextView: NSTextView! + @IBOutlet weak var sourceIpLabel: NSTextField! + @IBOutlet weak var destLabel: NSTextField! + override func awakeFromNib() { + super.awakeFromNib() + otherTextView.backgroundColor = NSColor.clear + otherTextView.font = NSFont.systemFont(ofSize: 10) + } +} diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift new file mode 100644 index 000000000..b67d6945f --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift @@ -0,0 +1,136 @@ +// +// ConnectionDetailInfoView.swift +// ClashX +// +// Created by yicheng on 2023/7/5. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import Combine + +@available(macOS 10.15, *) +class ConnectionDetailInfoView: NSView { + private let logoView = NSImageView() + private let processNameLabel = NSTextField(labelWithString: "") + private let hostLabel = NSTextField(labelWithString: "") + private let generalView = ConnectionDetailInfoGeneralView.createFromNib() + private let containerView = NSView() + private var cancelable = Set() + private let closeButton = NSButton() + private var viewModel: ConnectionDetailViewModel? + init() { + super.init(frame: .zero) + wantsLayer = true + layer?.backgroundColor = NSColor.white.cgColor + setupSubviews() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupSubviews() { + addSubview(logoView) + logoView.makeConstraints { + [$0.topAnchor.constraint(equalTo: topAnchor, constant: 16), + $0.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), + $0.widthAnchor.constraint(equalTo: $0.heightAnchor), + $0.widthAnchor.constraint(equalToConstant: 36)] + } + let nameStackView = NSStackView() + nameStackView.orientation = .vertical + nameStackView.addArrangedSubview(processNameLabel) + nameStackView.addArrangedSubview(hostLabel) + nameStackView.alignment = .left + nameStackView.spacing = 2 + addSubview(nameStackView) + nameStackView.makeConstraints { + [$0.centerYAnchor.constraint(equalTo: logoView.centerYAnchor), + $0.leadingAnchor.constraint(equalTo: logoView.trailingAnchor, constant: 10)] + } + + processNameLabel.font = NSFont.systemFont(ofSize: 17, weight: .bold) + hostLabel.font = NSFont.systemFont(ofSize: 12) + /* + let segmentControl = NSSegmentedControl(labels: ["General", "Event"], trackingMode: .selectOne, target: self, action: #selector(actionSelectSegment(sender: ))) + addSubview(segmentControl) + segmentControl.makeConstraints { + [$0.leftAnchor.constraint(equalTo: logoView.centerXAnchor), + $0.topAnchor.constraint(equalTo: nameStackView.bottomAnchor, constant: 12)] + } + segmentControl.selectedSegment = 0 + */ + closeButton.title = NSLocalizedString("Close Connection", comment: "") + closeButton.bezelStyle = .regularSquare + closeButton.target = self + closeButton.action = #selector(actionCloseConn) + addSubview(closeButton) + closeButton.makeConstraints { [ + $0.centerYAnchor.constraint(equalTo: nameStackView.centerYAnchor), + $0.rightAnchor.constraint(equalTo: rightAnchor, constant: -12) + ] } + + let separator = NSView() + separator.wantsLayer = true + separator.layer?.backgroundColor = NSColor.separatorColor.cgColor + addSubview(separator) + separator.makeConstraints { + [$0.heightAnchor.constraint(equalToConstant: 1), + $0.leftAnchor.constraint(equalTo: logoView.centerXAnchor), + $0.rightAnchor.constraint(equalTo: rightAnchor), + $0.centerYAnchor.constraint(equalTo: nameStackView.bottomAnchor, constant: 12)] + } + + addSubview(containerView) + containerView.makeConstraints { + [$0.leftAnchor.constraint(equalTo: separator.leftAnchor), + $0.rightAnchor.constraint(equalTo: rightAnchor), + $0.bottomAnchor.constraint(equalTo: bottomAnchor), + $0.topAnchor.constraint(equalTo: separator.bottomAnchor, constant: 16)] + } + + addGeneralView() + } + + func setup(with viewModel: ConnectionDetailViewModel) { + cancelable.removeAll() + self.viewModel = viewModel + viewModel.$processName.weakAssign(to: \.stringValue, on: processNameLabel).store(in: &cancelable) + viewModel.$applicationPath.weakAssign(to: \.toolTip, on: processNameLabel).store(in: &cancelable) + viewModel.$applicationPath.weakAssign(to: \.toolTip, on: logoView).store(in: &cancelable) + viewModel.$remoteHost.weakAssign(to: \.stringValue, on: hostLabel).store(in: &cancelable) + viewModel.$processImage.weakAssign(to: \.image, on: logoView).store(in: &cancelable) + viewModel.$entry.weakAssign(to: \.stringValue, on: generalView.entryLabel).store(in: &cancelable) + viewModel.$networkType.weakAssign(to: \.stringValue, on: generalView.networkTypeLabel).store(in: &cancelable) + + viewModel.$totalUpload.weakAssign(to: \.stringValue, on: generalView.totalUploadLabel).store(in: &cancelable) + viewModel.$totalDownload.weakAssign(to: \.stringValue, on: generalView.totalDownloadLabel).store(in: &cancelable) + + viewModel.$maxUpload.weakAssign(to: \.stringValue, on: generalView.maxUploadLabel).store(in: &cancelable) + viewModel.$maxDownload.weakAssign(to: \.stringValue, on: generalView.maxDownloadLabel).store(in: &cancelable) + + viewModel.$currentUpload.weakAssign(to: \.stringValue, on: generalView.currentUploadLabel).store(in: &cancelable) + viewModel.$currentDownload.weakAssign(to: \.stringValue, on: generalView.currentDownloadLabel).store(in: &cancelable) + + viewModel.$rule.weakAssign(to: \.stringValue, on: generalView.ruleLabel).store(in: &cancelable) + viewModel.$chain.weakAssign(to: \.stringValue, on: generalView.proxyChainLabel).store(in: &cancelable) + viewModel.$sourceIP.weakAssign(to: \.stringValue, on: generalView.sourceIpLabel).store(in: &cancelable) + + viewModel.$destination.weakAssign(to: \.stringValue, on: generalView.destLabel).store(in: &cancelable) + viewModel.$otherText.weakAssign(to: \.string, on: generalView.otherTextView).store(in: &cancelable) + + viewModel.$showCloseButton.map {!$0}.weakAssign(to: \.isHidden, on: closeButton).store(in: &cancelable) + } + + @objc func actionSelectSegment(sender: NSSegmentedControl?) { } + @objc func actionCloseConn() { + viewModel?.closeConnection() + } + func addGeneralView() { + containerView.subviews.forEach { $0.removeFromSuperview() } + containerView.addSubview(generalView) + generalView.makeConstraintsToBindToSuperview() + + } +} diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionLeftPannelView.swift b/ClashX/ViewControllers/Connections/Views/ConnectionLeftPannelView.swift new file mode 100644 index 000000000..3bccf3f93 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/ConnectionLeftPannelView.swift @@ -0,0 +1,194 @@ +// +// ConnectionLeftPannelView.swift +// ClashX +// +// Created by yicheng on 2023/7/5. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +fileprivate extension NSUserInterfaceItemIdentifier { + static let mainColumn = NSUserInterfaceItemIdentifier("mainColumn") + static let localApplication = NSUserInterfaceItemIdentifier("localApplication") + static let remoteApplication = NSUserInterfaceItemIdentifier("remoteApplication") + static let hosts = NSUserInterfaceItemIdentifier("hosts") + static let all = NSUserInterfaceItemIdentifier("all") +} + +@available(macOS 10.15, *) +class ConnectionLeftPannelView: NSView { + let viewModel:ConnectionLeftPannelViewModel + let columnIdentifier = NSUserInterfaceItemIdentifier(rawValue: "column") + let effectView = NSVisualEffectView() + + private let tableView: SectionedTableView = { + let table = SectionedTableView() + table.columnAutoresizingStyle = .uniformColumnAutoresizingStyle + table.backgroundColor = NSColor.clear + table.allowsColumnSelection = false + table.usesAutomaticRowHeights = true + table.translatesAutoresizingMaskIntoConstraints = false + return table + }() + + init(viewModel:ConnectionLeftPannelViewModel) { + self.viewModel = viewModel + super.init(frame: .zero) + setupSubviews() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + private func setupSubviews() { + addSubview(effectView) + effectView.makeConstraintsToBindToSuperview() + + let segmentControl = NSSegmentedControl(labels: [NSLocalizedString("Client", comment: ""), NSLocalizedString("Host", comment: "")], + trackingMode: .selectOne, + target: self, + action: #selector(actionSelectSegment(sender: ))) + addSubview(segmentControl) + segmentControl.makeConstraints {[ + $0.leftAnchor.constraint(equalTo: leftAnchor, constant: 12), + $0.rightAnchor.constraint(equalTo: rightAnchor, constant: -12), + $0.heightAnchor.constraint(equalToConstant: 20), + $0.topAnchor.constraint(equalTo: topAnchor, constant: 12) + ]} + segmentControl.selectedSegment = 0 + + let v = NSScrollView() + v.drawsBackground = false + v.backgroundColor = .clear + v.contentView.documentView = tableView + addSubview(v) + v.makeConstraints { [ + $0.leftAnchor.constraint(equalTo: leftAnchor), + $0.rightAnchor.constraint(equalTo: rightAnchor), + $0.bottomAnchor.constraint(equalTo: bottomAnchor), + $0.topAnchor.constraint(equalTo: segmentControl.bottomAnchor, constant: 2) + ] } + v.hasHorizontalScroller = false + + let column = NSTableColumn(identifier: .mainColumn) + column.minWidth = 60 + column.maxWidth = .greatestFiniteMagnitude + tableView.addTableColumn(column) + tableView.backgroundColor = .clear + tableView.headerView = nil + tableView.intercellSpacing = .zero + tableView.sectionDatasource = self + tableView.allowsEmptySelection = false + tableView.sizeLastColumnToFit() + tableView.reloadData() + + viewModel.onReloadTable = { [weak self] in + guard let self else { return } + tableView.reloadData() + tableView.selectRow(at: $0) + } + } + + @objc func actionSelectSegment(sender: NSSegmentedControl?) { + viewModel.setHostMode(enable: sender?.selectedSegment == 1) + } + +} + +// MARK: - section map to cell logic. +@available(macOS 10.15, *) +private extension ConnectionLeftPannelView { + + func getIdentifier(section: Int) -> NSUserInterfaceItemIdentifier { + var identifier:NSUserInterfaceItemIdentifier = NSUserInterfaceItemIdentifier("") + switch viewModel.currentSections[section] { + case .local: + identifier = .localApplication + case .remote: + identifier = .remoteApplication + case .hosts: + identifier = .hosts + case .all: + identifier = .all + } + return identifier + } + +} + +@available(macOS 10.15, *) +extension ConnectionLeftPannelView: TableViewSectionDataSource { + func numberOfSectionsInTableView(tableView: NSTableView) -> Int { + return viewModel.currentSections.count + } + + func tableView(tableView: NSTableView, numberOfRowsInSection section: Int) -> Int { + switch viewModel.currentSections[section] { + case .local: + return viewModel.localApplications.count + case .remote: + return viewModel.sources.count + case .hosts: + return viewModel.hosts.count + case .all: + return 1 + } + } + + func tableView(tableView: NSTableView, viewForHeaderInSection section: Int) -> NSView? { + switch viewModel.currentSections[section] { + case .local: + let sectionView = ApplicationClientSectionCell() + sectionView.setup(with: NSLocalizedString("Local Clients", comment: "")) + return sectionView + case .remote: + let sectionView = ApplicationClientSectionCell() + sectionView.setup(with: NSLocalizedString("Sources", comment: "")) + return sectionView + case .all: + let sectionView = ApplicationClientSectionCell() + sectionView.setup(with: NSLocalizedString("Requests", comment: "")) + return sectionView + case .hosts: + let sectionView = ApplicationClientSectionCell() + sectionView.setup(with: NSLocalizedString("Hosts", comment: "")) + return sectionView + } + } + + func tableView(tableView: NSTableView, viewForRowAt indexPath: IndexPath, column: NSTableColumn) -> NSView? { + let type = viewModel.currentSections[indexPath.section] + let identifier = getIdentifier(section: indexPath.section) + var view = tableView.makeView(withIdentifier: identifier, owner: self) + if view == nil { + switch type { + case .local: + view = ConnectionApplicationCellView() + case .remote, .all, .hosts: + view = ConnectionLeftTextCellView() + } + view?.identifier = identifier + } + + switch type { + case .local: + (view as! ConnectionApplicationCellView).setup(with: viewModel.localApplications[indexPath.item]) + case .remote: + (view as! ConnectionLeftTextCellView).setup(with: viewModel.sources[indexPath.item]) + case .all: + (view as! ConnectionLeftTextCellView).setup(with: viewModel.isHostMode ? NSLocalizedString("All Hosts", comment: ""):NSLocalizedString("All Clients", comment: "")) + case .hosts: + (view as! ConnectionLeftTextCellView).setup(with: viewModel.hosts[indexPath.item]) + } + return view + } + + func tableView(tableView: NSTableView, didSelectRowAtIndexPath indexPath: IndexPath) { + viewModel.setSelect(indexPath: indexPath) + } + + func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + return 36 + } +} diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift b/ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift new file mode 100644 index 000000000..1775d928c --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift @@ -0,0 +1,127 @@ +// +// ConnectionTopListView.swift +// ClashX +// +// Created by yicheng on 2023/7/5. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit + +@available(macOS 10.15, *) +class ConnectionTopListView: NSView { + private let viewModel:ConnectionTopListViewModel + + private let tableView: NSTableView = { + let table = NSTableView() + table.allowsColumnSelection = false + return table + }() + + let closeConnectionMenuItem = NSMenuItem() + + init(viewModel:ConnectionTopListViewModel) { + self.viewModel = viewModel + super.init(frame: .zero) + setupSubviews() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupSubviews() { + let v = NSScrollView() + v.contentView.documentView = tableView + addSubview(v) + v.makeConstraintsToBindToSuperview() + v.hasVerticalScroller = true + v.hasHorizontalScroller = true + + for columnType in ConnectionColume.allCases { + let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: columnType.rawValue)) + column.title = columnType.columeTitle + column.minWidth = columnType.minWidth + column.maxWidth = columnType.maxWidth + column.sortDescriptorPrototype = viewModel.sortSortDescriptor(for: columnType) + tableView.addTableColumn(column) + } + tableView.sortDescriptors = [viewModel.currentSortDescriptor].compactMap {$0} + tableView.usesAlternatingRowBackgroundColors = true + tableView.delegate = self + tableView.allowsMultipleSelection = true + tableView.dataSource = self + tableView.sizeLastColumnToFit() + tableView.reloadData() + closeConnectionMenuItem.title = NSLocalizedString("Close Connection", comment: "") + closeConnectionMenuItem.target = self + closeConnectionMenuItem.action = #selector(actionCloseConnection) + tableView.menu = NSMenu() + tableView.menu?.autoenablesItems = false + tableView.menu?.addItem(closeConnectionMenuItem) + tableView.menu?.delegate = self + + viewModel.onReloadTable = { [weak self] in + guard let self else { return } + tableView.reloadData() + tableView.selectRowIndexes(viewModel.currentSelection(), byExtendingSelection: false) + } + } + + @objc func actionCloseConnection() { + if tableView.selectedRowIndexes.contains(tableView.clickedRow) { + viewModel.closeConnection(for: tableView.selectedRowIndexes) + } else { + viewModel.closeConnection(for: [tableView.clickedRow]) + } + } +} + +@available(macOS 10.15, *) +extension ConnectionTopListView: NSMenuDelegate { + func menuNeedsUpdate(_ menu: NSMenu) { + closeConnectionMenuItem.isEnabled = !tableView.selectedRowIndexes.isEmpty + } +} + +@available(macOS 10.15, *) +extension ConnectionTopListView: NSTableViewDelegate { + func tableViewSelectionDidChange(_ notification: Notification) { + viewModel.setSelect(row: tableView.selectedRowIndexes) + } + + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + return 28 + } + + func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) { + viewModel.currentSortDescriptor = tableView.sortDescriptors.first + tableView.sortDescriptors = [viewModel.currentSortDescriptor].compactMap {$0} + } +} + +@available(macOS 10.15, *) +extension ConnectionTopListView: NSTableViewDataSource { + func numberOfRows(in tableView: NSTableView) -> Int { + return viewModel.connections.count + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + guard let tableColumn, let type = ConnectionColume(rawValue: tableColumn.identifier.rawValue) else { return nil } + var view = tableView.makeView(withIdentifier: tableColumn.identifier, owner: self) as? ConnectionCellProtocol + if view == nil { + switch type { + case .process: + view = ConnectionProxyClientCellView() + case .statusIcon: + view = ConnectionStatusIconCellView() + default: + view = ConnectionTextCellView() + } + view?.identifier = tableColumn.identifier + } + let c = viewModel.connections[row] + view?.setup(with: c, type: type) + return view + } +} diff --git a/ClashX/ViewControllers/Connections/Views/SectionedTableView.swift b/ClashX/ViewControllers/Connections/Views/SectionedTableView.swift new file mode 100644 index 000000000..976ccdb67 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/SectionedTableView.swift @@ -0,0 +1,149 @@ +// +// SectionedTableView.swift +// ClashX +// +// Created by yicheng on 2023/7/13. +// Copyright © 2023 Marcin Krzyzanowski. All rights reserved. +// + +import Cocoa + +protocol TableViewSectionDataSource: NSObject { + func numberOfSectionsInTableView(tableView: NSTableView) -> Int + func tableView(tableView: NSTableView, numberOfRowsInSection section: Int) -> Int + func tableView(tableView: NSTableView, viewForHeaderInSection section: Int) -> NSView? + func tableView(tableView: NSTableView, viewForRowAt indexPath: IndexPath, column:NSTableColumn) -> NSView? + func tableView(tableView: NSTableView, didSelectRowAtIndexPath indexPath: IndexPath) + func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat +} + +class SectionedTableView: NSTableView { + weak var sectionDatasource: TableViewSectionDataSource? + private var sectionHeaders = [Int:NSView]() + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + private func setup() { + dataSource = self + delegate = self + } + + override func reloadData() { + for section in 0 ..< (sectionDatasource?.numberOfSectionsInTableView(tableView: self) ?? 0) { + if let header = sectionDatasource?.tableView(tableView: self, viewForHeaderInSection: section) { + sectionHeaders[section] = header + } + } + super.reloadData() + } + + private func sectionForRow(row: Int, counts: [Int]) -> (section: Int?, row: Int?) { + var c = counts[0] + for section in 0.. 0 { + c += counts[section] + } + if (row >= c - counts[section]) && row < c { + return (section: section, row: row - (c - counts[section])) + } + } + return (section: nil, row: nil) + } + + private func sectionForRow(row: Int) -> (section: Int, row: Int) { + if let dataSource = sectionDatasource { + let numberOfSections = dataSource.numberOfSectionsInTableView(tableView: self) + var counts = [Int](repeating: 0, count: numberOfSections) + + for section in 0.. Int { + var total = 0 + + if let dataSource = sectionDatasource { + for section in 0.. NSView? { + guard let dataSource = sectionDatasource, let tableColumn else { return nil } + let (section, sectionRow) = sectionForRow(row: row) + + if let headerView = self.sectionHeaders[section] { + if sectionRow == 0 { + return headerView + } + return dataSource.tableView(tableView: tableView, viewForRowAt: IndexPath(item: sectionRow - 1, section: section), column: tableColumn) + } + return dataSource.tableView(tableView: tableView, viewForRowAt: IndexPath(item: sectionRow, section: section), column: tableColumn) + + } + + func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { + let (_, sectionRow) = sectionForRow(row: row) + if sectionRow == 0 { + return false + } + return true + } + + func tableViewSelectionDidChange(_ notification: Notification) { + guard let dataSource = sectionDatasource else { return } + let (section, sectionRow) = sectionForRow(row: selectedRow) + + if sectionHeaders[section] != nil { + if sectionRow == 0 { + return + } + dataSource.tableView(tableView: self, didSelectRowAtIndexPath: IndexPath(item: sectionRow - 1, section: section)) + return + } + dataSource.tableView(tableView: self, didSelectRowAtIndexPath: IndexPath(item: sectionRow, section: section)) + } + + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + return sectionDatasource?.tableView(tableView: tableView, heightOfRow: row) ?? 0 + } +} diff --git a/ClashX/ViewControllers/Connections/Views/zh-Hans.lproj/ConnectionDetailInfoGeneralView.strings b/ClashX/ViewControllers/Connections/Views/zh-Hans.lproj/ConnectionDetailInfoGeneralView.strings new file mode 100644 index 000000000..377b86fe6 --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/zh-Hans.lproj/ConnectionDetailInfoGeneralView.strings @@ -0,0 +1,53 @@ +/* Class = "NSTextFieldCell"; title = "NetworkType"; ObjectID = "6p6-n8-rBg"; */ +"6p6-n8-rBg.title" = "网络类型"; + +/* Class = "NSBox"; title = "Current Speed"; ObjectID = "7fZ-OX-aWF"; */ +"7fZ-OX-aWF.title" = "实时速率"; + +/* Class = "NSTextFieldCell"; title = "Source"; ObjectID = "9Id-dT-XYP"; */ +"9Id-dT-XYP.title" = "来源"; + +/* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "CGD-ut-PXk"; */ +"CGD-ut-PXk.title" = "上传"; + +/* Class = "NSBox"; title = "Rule"; ObjectID = "CiP-Ib-BPd"; */ +"CiP-Ib-BPd.title" = "规则"; + +/* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "GhE-tw-D8L"; */ +"GhE-tw-D8L.title" = "上传"; + +/* Class = "NSTextFieldCell"; title = "Entry"; ObjectID = "KQt-5l-lyc"; */ +"KQt-5l-lyc.title" = "入口"; + +/* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "KsS-nl-D2n"; */ +"KsS-nl-D2n.title" = "上传"; + +/* Class = "NSBox"; title = "Proxy Chain"; ObjectID = "MIz-sg-4Rx"; */ +"MIz-sg-4Rx.title" = "代理链"; + +/* Class = "NSBox"; title = "Max Speed"; ObjectID = "NBq-b1-RLL"; */ +"NBq-b1-RLL.title" = "最高速率"; + +/* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "ZEc-E9-AXS"; */ +"ZEc-E9-AXS.title" = "下载"; + +/* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "iem-RP-B0u"; */ +"iem-RP-B0u.title" = "下载"; + +/* Class = "NSBox"; title = "General"; ObjectID = "leq-MF-MFL"; */ +"leq-MF-MFL.title" = "通用"; + +/* Class = "NSTextFieldCell"; title = "Dest."; ObjectID = "m5m-4U-xAi"; */ +"m5m-4U-xAi.title" = "去向"; + +/* Class = "NSBox"; title = "Other"; ObjectID = "nG8-0D-7W1"; */ +"nG8-0D-7W1.title" = "其他"; + +/* Class = "NSBox"; title = "Address"; ObjectID = "obG-yt-Gi8"; */ +"obG-yt-Gi8.title" = "地址"; + +/* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "tes-yR-PKh"; */ +"tes-yR-PKh.title" = "下载"; + +/* Class = "NSBox"; title = "Total"; ObjectID = "xB0-fx-J0y"; */ +"xB0-fx-J0y.title" = "总计"; diff --git a/ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings b/ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings new file mode 100644 index 000000000..421d5ac0d --- /dev/null +++ b/ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings @@ -0,0 +1,53 @@ +/* Class = "NSTextFieldCell"; title = "NetworkType"; ObjectID = "6p6-n8-rBg"; */ +"6p6-n8-rBg.title" = "NetworkType"; + +/* Class = "NSBox"; title = "Current Speed"; ObjectID = "7fZ-OX-aWF"; */ +"7fZ-OX-aWF.title" = "Current Speed"; + +/* Class = "NSTextFieldCell"; title = "Source"; ObjectID = "9Id-dT-XYP"; */ +"9Id-dT-XYP.title" = "Source"; + +/* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "CGD-ut-PXk"; */ +"CGD-ut-PXk.title" = "Upload"; + +/* Class = "NSBox"; title = "Rule"; ObjectID = "CiP-Ib-BPd"; */ +"CiP-Ib-BPd.title" = "Rule"; + +/* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "GhE-tw-D8L"; */ +"GhE-tw-D8L.title" = "Upload"; + +/* Class = "NSTextFieldCell"; title = "Entry"; ObjectID = "KQt-5l-lyc"; */ +"KQt-5l-lyc.title" = "Entry"; + +/* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "KsS-nl-D2n"; */ +"KsS-nl-D2n.title" = "Upload"; + +/* Class = "NSBox"; title = "Proxy Chain"; ObjectID = "MIz-sg-4Rx"; */ +"MIz-sg-4Rx.title" = "Proxy Chain"; + +/* Class = "NSBox"; title = "Max Speed"; ObjectID = "NBq-b1-RLL"; */ +"NBq-b1-RLL.title" = "Max Speed"; + +/* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "ZEc-E9-AXS"; */ +"ZEc-E9-AXS.title" = "Download"; + +/* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "iem-RP-B0u"; */ +"iem-RP-B0u.title" = "Download"; + +/* Class = "NSBox"; title = "General"; ObjectID = "leq-MF-MFL"; */ +"leq-MF-MFL.title" = "General"; + +/* Class = "NSTextFieldCell"; title = "Dest."; ObjectID = "m5m-4U-xAi"; */ +"m5m-4U-xAi.title" = "Dest."; + +/* Class = "NSBox"; title = "Other"; ObjectID = "nG8-0D-7W1"; */ +"nG8-0D-7W1.title" = "Other"; + +/* Class = "NSBox"; title = "Address"; ObjectID = "obG-yt-Gi8"; */ +"obG-yt-Gi8.title" = "Address"; + +/* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "tes-yR-PKh"; */ +"tes-yR-PKh.title" = "Download"; + +/* Class = "NSBox"; title = "Total"; ObjectID = "xB0-fx-J0y"; */ +"xB0-fx-J0y.title" = "Total"; diff --git a/ClashX/ViewControllers/ExternalControlViewController.swift b/ClashX/ViewControllers/ExternalControlViewController.swift index 73573f901..73d341468 100644 --- a/ClashX/ViewControllers/ExternalControlViewController.swift +++ b/ClashX/ViewControllers/ExternalControlViewController.swift @@ -142,6 +142,6 @@ class ExternalControlAddView: NSView { } func isVaild() -> Bool { - return urlTextField.stringValue.isUrlVaild() && nameLabel.stringValue.count > 0 + return urlTextField.stringValue.isUrlVaild() && !nameLabel.stringValue.isEmpty } } diff --git a/ClashX/ViewControllers/RemoteConfigViewController.swift b/ClashX/ViewControllers/RemoteConfigViewController.swift index bd06df039..b8603df7a 100644 --- a/ClashX/ViewControllers/RemoteConfigViewController.swift +++ b/ClashX/ViewControllers/RemoteConfigViewController.swift @@ -219,24 +219,24 @@ class RemoteConfigAddView: NSView, NibLoadable { /// Get the config name /// - Returns: return (name, isUserInput) func getConfigName() -> (String, Bool) { - if configNameTextField.stringValue.count > 0 { + if !configNameTextField.stringValue.isEmpty { return (configNameTextField.stringValue, true) } return (configNameTextField.placeholderString ?? "", false) } func isVaild() -> Bool { - return urlTextField.stringValue.isUrlVaild() && getConfigName().0.count > 0 + return urlTextField.stringValue.isUrlVaild() && !getConfigName().0.isEmpty } func setUrl(string: String, name: String? = nil, defaultName: String?) { urlTextField.stringValue = string - if let name = name, name.count > 0 { + if let name = name, !name.isEmpty { configNameTextField.stringValue = name } - if let defaultName = defaultName, defaultName.count > 0 { + if let defaultName = defaultName, !defaultName.isEmpty { configNameTextField.placeholderString = defaultName } diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 63bf2b119..32b7efbe2 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -118,7 +118,7 @@ class GeneralSettingViewController: NSViewController { override func viewWillDisappear() { super.viewWillDisappear() let url = speedTestUrlField.stringValue - if url.isUrlVaild() || url.count == 0 { + if url.isUrlVaild() || url.isEmpty { Settings.benchMarkUrl = url } } diff --git a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift index d1e7a0bc8..cad0aaca7 100644 --- a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift +++ b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift @@ -21,6 +21,7 @@ extension KeyboardShortcuts.Name { static let log = Self("shortCut.log") static let dashboard = Self("shortCut.dashboard") static let openMenu = Self("shortCut.openMenu") + static let nativeDashboard = Self("shortCut.nativeDashboard") } @@ -61,6 +62,11 @@ enum KeyboardShortCutManager { KeyboardShortcuts.onKeyUp(for: .openMenu) { AppDelegate.shared.statusItem.button?.performClick(nil) } + if #available(macOS 10.15, *) { + KeyboardShortcuts.onKeyUp(for: .nativeDashboard) { + ClashWindowController.create().showWindow(self) + } + } } } @@ -88,11 +94,15 @@ class GlobalShortCutViewController: NSViewController { [NSTextField(labelWithString: NSLocalizedString("Global Mode", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .modeGlobal)] ]) - addGridView(in: otherBoxView, with: [ + var otherItems:[[NSView]] = [ [NSTextField(labelWithString: NSLocalizedString("Open Menu", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .openMenu)], [NSTextField(labelWithString: NSLocalizedString("Open Log", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .log)], [NSTextField(labelWithString: NSLocalizedString("Open Dashboard", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .dashboard)] - ]) + ] + if #available(macOS 10.15, *) { + otherItems.append([NSTextField(labelWithString: NSLocalizedString("Open Connection Details", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .nativeDashboard)]) + } + addGridView(in: otherBoxView, with: otherItems) } func addGridView(in superView: NSView, with views: [[NSView]]) { diff --git a/ClashX/Views/ProxyDelayHistoryMenu.swift b/ClashX/Views/ProxyDelayHistoryMenu.swift index c80e43754..93f8bd874 100644 --- a/ClashX/Views/ProxyDelayHistoryMenu.swift +++ b/ClashX/Views/ProxyDelayHistoryMenu.swift @@ -35,7 +35,7 @@ class ProxyDelayHistoryMenu: NSMenu { let historys = Array(proxy.history.reversed()) let change = Changeset(previous: currentHistory, current: historys, identifier: { $0.time }) currentHistory = historys - if change.moves.count == 0 && change.mutations.count == 0 { + if change.moves.isEmpty && change.mutations.isEmpty { change.removals.reversed().forEach { idx in removeItem(at: idx) } diff --git a/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift b/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift index dc7ed368b..af779785c 100644 --- a/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift +++ b/ClashX/Views/ProxyGroupSpeedTestMenuItem.swift @@ -141,7 +141,7 @@ private class ProxyGroupSpeedTestMenuItemView: MenuItemBaseView { self.label.stringValue = menu.title menu.isEnabled = true self.setNeedsDisplay() - if providers.count > 0 { + if !providers.isEmpty { MenuItemFactory.refreshExistingMenuItems() } } diff --git a/ClashX/Views/StatusItem/NewStatusItemView.swift b/ClashX/Views/StatusItem/NewStatusItemView.swift index 87a2347df..c5c0bb1be 100644 --- a/ClashX/Views/StatusItem/NewStatusItemView.swift +++ b/ClashX/Views/StatusItem/NewStatusItemView.swift @@ -31,8 +31,8 @@ class NewStatusMenuView: NSHostingView, StatusItemViewProtocol { } func updateSpeedLabel(up: Int, down: Int) { - let upSpeed = StatusItemTool.getSpeedString(for: up) - let downSpeed = StatusItemTool.getSpeedString(for: down) + let upSpeed = SpeedUtils.getSpeedString(for: up) + let downSpeed = SpeedUtils.getSpeedString(for: down) if upSpeed != viewModel.upSpeed {viewModel.upSpeed = upSpeed} if downSpeed != viewModel.downSpeed {viewModel.downSpeed = downSpeed} } diff --git a/ClashX/Views/StatusItem/StatusItemTool.swift b/ClashX/Views/StatusItem/StatusItemTool.swift index bb992589a..980523ca6 100644 --- a/ClashX/Views/StatusItem/StatusItemTool.swift +++ b/ClashX/Views/StatusItem/StatusItemTool.swift @@ -33,23 +33,6 @@ enum StatusItemTool { return font }() - static func getSpeedString(for byte: Int) -> String { - let kb = byte / 1024 - if kb < 1024 { - return "\(kb)KB/s" - } else { - let mb = Double(kb) / 1024.0 - if mb >= 100 { - if mb >= 1000 { - return String(format: "%.1fGB/s", mb/1024) - } - return String(format: "%.1fMB/s", mb) - } else { - return String(format: "%.2fMB/s", mb) - } - } - } - static func getMenuImage(enableProxy: Bool) -> NSImage { let selectedColor = NSColor.red let unselectedColor = selectedColor.withSystemEffect(.disabled) diff --git a/ClashX/Views/StatusItem/StatusItemView.swift b/ClashX/Views/StatusItem/StatusItemView.swift index c2ef27681..f09207a76 100644 --- a/ClashX/Views/StatusItem/StatusItemView.swift +++ b/ClashX/Views/StatusItem/StatusItemView.swift @@ -52,8 +52,8 @@ class StatusItemView: NSView, StatusItemViewProtocol { func updateSpeedLabel(up: Int, down: Int) { guard !speedContainerView.isHidden else { return } - let finalUpStr = StatusItemTool.getSpeedString(for: up) - let finalDownStr = StatusItemTool.getSpeedString(for: down) + let finalUpStr = SpeedUtils.getSpeedString(for: up) + let finalDownStr = SpeedUtils.getSpeedString(for: down) if downloadSpeedLabel.stringValue == finalDownStr && uploadSpeedLabel.stringValue == finalUpStr { return diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 67825fa3b..82db1909c 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -1,6 +1,6 @@ module github.com/yichengchen/clashX/ClashX -go 1.19 +go 1.20 require ( github.com/Dreamacro/clash v1.16.1-0.20230625011906-5212aaf445ec diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 1aba60cdd..4c68b815c 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -21,6 +21,7 @@ import ( "time" "unsafe" + "github.com/Dreamacro/clash/component/mmdb" "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/hub/executor" @@ -291,6 +292,15 @@ func verifyGEOIPDataBase() bool { return true } +//export clash_getCountryForIp +func clash_getCountryForIp(ip *C.char) *C.char { + record, _ := mmdb.Instance().Country(net.ParseIP(C.GoString(ip))) + if record != nil { + return C.CString(record.Country.IsoCode) + } + return C.CString("") +} + //export clash_closeAllConnections func clash_closeAllConnections() { snapshot := statistic.DefaultManager.Snapshot() @@ -299,5 +309,10 @@ func clash_closeAllConnections() { } } +//export clash_getProggressInfo +func clash_getProggressInfo() *C.char { + return C.CString(GetTcpNetList() + GetUDpList()) +} + func main() { } diff --git a/ClashX/goClash/proccess.go b/ClashX/goClash/proccess.go new file mode 100644 index 000000000..b3e6b29bd --- /dev/null +++ b/ClashX/goClash/proccess.go @@ -0,0 +1,108 @@ +package main + +import ( + "encoding/binary" + "fmt" + "net/netip" + "strconv" + "strings" + "syscall" + "unsafe" +) + +const ( + procpidpathinfo = 0xb + procpidpathinfosize = 1024 + proccallnumpidinfo = 0x2 +) + +var structSize = func() int { + value, _ := syscall.Sysctl("kern.osrelease") + major, _, _ := strings.Cut(value, ".") + n, _ := strconv.ParseInt(major, 10, 64) + switch true { + case n >= 22: + return 408 + default: + // from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n + // size/offset are round up (aligned) to 8 bytes in darwin + // rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) + + // 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n)) + return 384 + } +}() + +func GetTcpNetList() string { + value, err := syscall.Sysctl("net.inet.tcp.pcblist_n") + if err != nil { + return "" + } + + buf := []byte(value) + itemSize := structSize + // tcp + // rup8(sizeof(xtcpcb_n)) + itemSize += 208 + + result := "" + for i := 24; i+itemSize <= len(buf); i += itemSize { + // offset of xinpcb_n and xsocket_n + inp, so := i, i+104 + srcPort := binary.BigEndian.Uint16(buf[inp+18 : inp+20]) + // xinpcb_n.inp_vflag + flag := buf[inp+44] + + var srcIP netip.Addr + switch { + case flag&0x1 > 0: + // ipv4 + srcIP = netip.AddrFrom4([4]byte(buf[inp+76 : inp+80])) + case flag&0x2 > 0: + // ipv6 + srcIP = netip.AddrFrom16([16]byte(buf[inp+64 : inp+80])) + default: + continue + } + pid := readNativeUint32(buf[so+68 : so+72]) + result += fmt.Sprintf("%s %d %d\n", srcIP, srcPort, pid) + } + return result +} + +func GetUDpList() string { + value, err := syscall.Sysctl("net.inet.udp.pcblist_n") + if err != nil { + return "" + } + + buf := []byte(value) + itemSize := structSize + result := "" + + for i := 24; i+itemSize <= len(buf); i += itemSize { + // offset of xinpcb_n and xsocket_n + inp, so := i, i+104 + srcPort := binary.BigEndian.Uint16(buf[inp+18 : inp+20]) + // xinpcb_n.inp_vflag + flag := buf[inp+44] + var srcIP netip.Addr + switch { + case flag&0x1 > 0: + // ipv4 + srcIP = netip.AddrFrom4([4]byte(buf[inp+76 : inp+80])) + case flag&0x2 > 0: + // ipv6 + srcIP = netip.AddrFrom16([16]byte(buf[inp+64 : inp+80])) + default: + continue + } + + pid := readNativeUint32(buf[so+68 : so+72]) + result += fmt.Sprintf("%s %d %d\n", srcIP, srcPort, pid) + } + return result +} + +func readNativeUint32(b []byte) uint32 { + return *(*uint32)(unsafe.Pointer(&b[0])) +} diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 64b79169e..43bb2c6a3 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -1,63 +1,30 @@ /* Class = "NSMenuItem"; title = "ERROR"; ObjectID = "0iu-lB-eZN"; */ "0iu-lB-eZN.title" = "ERROR"; -/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ -"1d5-NL-UsJ.title" = "代理模式"; - -/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ -"1He-Eq-fSy.title" = "远程控制器"; - /* Class = "NSButtonCell"; title = "Update"; ObjectID = "2Rx-ih-aGW"; */ "2Rx-ih-aGW.title" = "更新"; /* Class = "NSMenuItem"; title = "Log level"; ObjectID = "3Da-fL-Mzr"; */ "3Da-fL-Mzr.title" = "日志等级"; -/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ -"3Om-yT-A83.label" = "调试"; - /* Class = "NSButtonCell"; title = "Add"; ObjectID = "51K-nB-xLS"; */ "51K-nB-xLS.title" = "添加"; -/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ -"5hn-k8-CWe.headerCell.title" = "Api Secret"; - -/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ -"5ws-55-f1g.title" = "重置"; - /* Class = "NSViewController"; title = "Remote Configs"; ObjectID = "6WI-Hi-v9j"; */ "6WI-Hi-v9j.title" = "托管的配置文件"; /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "7G6-oO-vNs"; */ "7G6-oO-vNs.title" = "Text Cell"; -/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ -"7wl-vK-5JO.title" = "复制终端代理命令(外部IP)"; - /* Class = "NSMenuItem"; title = "Rule"; ObjectID = "89n-bD-JHk"; */ "89n-bD-JHk.title" = "规则判断"; /* Class = "NSMenuItem"; title = "Set as system proxy"; ObjectID = "8se-yr-wmp"; */ "8se-yr-wmp.title" = "设置为系统代理"; -/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ -"9gE-NX-2wJ.title" = "远程控制器"; - /* Class = "NSMenuItem"; title = "Ports"; ObjectID = "9i0-LH-x04"; */ "9i0-LH-x04.title" = "端口"; -/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ -"afj-4G-usr.title" = "打开日志文件夹"; - -/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ -"AMT-oc-r8A.title" = "其他"; - -/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ -"aSG-9A-eeG.title" = "助手程序"; - -/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ -"AY0-nP-cGT.title" = "移除助手程序"; - /* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */ "AYu-sK-qS6.title" = "Main Menu"; @@ -67,27 +34,9 @@ /* Class = "NSMenuItem"; title = "Start at login"; ObjectID = "B1J-XB-BiZ"; */ "B1J-XB-BiZ.title" = "开机启动"; -/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ -"B2w-4r-5Kh.title" = "删除"; - -/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ -"bcR-rG-52F.title" = "端口设置(重启应用生效)"; - -/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ -"BFE-Qq-B2H.title" = "代理设置"; - -/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ -"BRR-WK-aeP.title" = "远程控制器"; - -/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ -"c01-0L-1SQ.title" = "日志"; - /* Class = "NSTableColumn"; headerCell.title = "Url"; ObjectID = "C79-J5-30z"; */ "C79-J5-30z.headerCell.title" = "链接"; -/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ -"ckH-Er-PfX.title" = "Api秘钥"; - /* Class = "NSMenuItem"; title = "Benchmark"; ObjectID = "COu-UX-bww"; */ "COu-UX-bww.title" = "延迟测速"; @@ -97,9 +46,6 @@ /* Class = "NSMenuItem"; title = "Help"; ObjectID = "Dd9-2F-FVY"; */ "Dd9-2F-FVY.title" = "帮助"; -/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ -"dV6-4Z-2SO.title" = "开机启动"; - /* Class = "NSMenuItem"; title = "SILENT"; ObjectID = "dVr-Xp-C0C"; */ "dVr-Xp-C0C.title" = "SILENT"; @@ -109,33 +55,9 @@ /* Class = "NSMenuItem"; title = "Manage"; ObjectID = "Dwg-Qb-2AU"; */ "Dwg-Qb-2AU.title" = "管理"; -/* Class = "NSTextFieldCell"; title = "Proxy Benchmark Url:"; ObjectID = "e0M-xf-ovR"; */ -"e0M-xf-ovR.title" = "代理延迟测速链接"; - -/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ -"E8B-e5-K0A.title" = "允许局域网控制(不推荐)"; - -/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ -"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染 (MacOS 13+)"; - -/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ -"F1s-SF-dqX.title" = "在特定 WiFi SSID 下自动暂停"; - -/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ -"GGx-F2-7kE.title" = "请务必注意处理可能存在的快捷键冲突,全局快捷键将优先于普通快捷键。"; - -/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ -"Gnh-m8-PAz.title" = "Box"; - /* Class = "NSMenuItem"; title = "Remote config"; ObjectID = "h1C-R6-Y9w"; */ "h1C-R6-Y9w.title" = "托管配置"; -/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ -"h1H-7k-9HS.title" = "设置更新间隔"; - -/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ -"hlb-KQ-Fdr.title" = "管理"; - /* Class = "NSMenuItem"; title = "About"; ObjectID = "hUb-k9-TEf"; */ "hUb-k9-TEf.title" = "关于"; @@ -145,9 +67,6 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "id7-f0-u56"; */ "id7-f0-u56.title" = "Text Cell"; -/* Class = "NSButtonCell"; title = "Use Built-in clash api"; ObjectID = "IET-Bf-hGj"; */ -"IET-Bf-hGj.title" = "使用内置Clash接口"; - /* Class = "NSMenuItem"; title = "API Connect Error"; ObjectID = "jGT-1M-xJu"; */ "jGT-1M-xJu.title" = "API Connect Error"; @@ -157,72 +76,24 @@ /* Class = "NSMenuItem"; title = "Config"; ObjectID = "JMV-Dy-CI0"; */ "JMV-Dy-CI0.title" = "配置"; -/* Class = "NSButtonCell"; title = "Revert to previous proxy on proxy disabled"; ObjectID = "JOF-yU-YHc"; */ -"JOF-yU-YHc.title" = "关闭代理时还原先前系统代理设置"; - -/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ -"jsL-HC-6ne.title" = "减少通知"; - -/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ -"kdV-Em-qBi.title" = "调试"; - -/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ -"kma-mp-ncL.title" = "通用"; - /* Class = "NSMenuItem"; title = "WARNING"; ObjectID = "ko2-Ir-DxA"; */ "ko2-Ir-DxA.title" = "WARNING"; -/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ -"krh-QF-pqZ.title" = "更多设置"; - -/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ -"LAj-p8-9gd.title" = "ClashX 设置"; - /* Class = "NSTableColumn"; headerCell.title = "Config Name"; ObjectID = "lRE-Xa-euB"; */ "lRE-Xa-euB.headerCell.title" = "配置文件名称"; -/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ -"Ltt-Vq-Hh1.label" = "通用"; - -/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ -"LW4-cA-3bB.title" = "覆盖配置文件设置"; - -/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ -"mbn-tK-UQa.title" = "Api 端口"; - -/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ -"NLT-FZ-48V.title" = "调试设置"; - -/* Class = "NSTextFieldCell"; title = "Update Channel"; ObjectID = "NLt-OM-k6i"; */ -"NLt-OM-k6i.title" = "更新通道"; - /* Class = "NSMenuItem"; title = "Direct"; ObjectID = "Np6-Pm-Lo3"; */ "Np6-Pm-Lo3.title" = "直接连接"; -/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ -"NPu-V9-f3r.title" = "忽略这些主机与域的代理设置"; - /* Class = "NSMenuItem"; title = "Quit"; ObjectID = "NXU-86-Eem"; */ "NXU-86-Eem.title" = "退出"; /* Class = "NSMenu"; title = "Help"; ObjectID = "ogW-pn-jeR"; */ "ogW-pn-jeR.title" = "帮助"; -/* Class = "NSTextFieldCell"; title = "Geo-IP Datebase"; ObjectID = "OMy-zu-iho"; */ -"OMy-zu-iho.title" = "Geo-IP数据库"; - /* Class = "NSMenuItem"; title = "Check Update"; ObjectID = "p0T-J8-Emx"; */ "p0T-J8-Emx.title" = "检查更新"; -/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ -"p7q-KN-kIv.title" = "将配置文件存储在iCloud中"; - -/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ -"PF0-Gd-XbR.title" = "应用配置"; - -/* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ -"pHl-C4-fNt.title" = "更新"; - /* Class = "NSMenuItem"; title = "Reload config"; ObjectID = "q3G-VH-eyy"; */ "q3G-VH-eyy.title" = "重载配置文件"; @@ -232,36 +103,18 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RCv-zz-HKW"; */ "RCv-zz-HKW.title" = "Text Cell"; -/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ -"s6y-wL-pnr.title" = "远程控制器"; - -/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ -"sfe-wu-UXp.title" = "使用英文逗号(,)分隔"; - /* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ "tck-zU-JKQ.title" = "配置"; /* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ "tL1-bl-LXd.title" = "托管的配置文件:"; -/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ -"URV-fZ-bJf.title" = "iCloud"; - /* Class = "NSMenu"; title = "API Connect Error"; ObjectID = "UU2-uE-YB4"; */ "UU2-uE-YB4.title" = "API Connect Error"; -/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ -"uUA-LS-Hu8.title" = "代理端口"; - /* Class = "NSMenuItem"; title = "Allow connect from Lan"; ObjectID = "Vz8-7n-vx6"; */ "Vz8-7n-vx6.title" = "允许局域网连接"; -/* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ -"WkL-aX-66E.title" = "远程控制器允许你控制其他设备上的 Clash 状态。"; - -/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ -"wKZ-TE-sf8.title" = "全局快捷键"; - /* Class = "NSMenu"; title = "Log level"; ObjectID = "wqo-3T-4qO"; */ "wqo-3T-4qO.title" = "日志等级"; @@ -271,18 +124,12 @@ /* Class = "NSMenuItem"; title = "DEBUG"; ObjectID = "XIR-Go-fWA"; */ "XIR-Go-fWA.title" = "DEBUG"; -/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ -"xnL-ma-vFo.title" = "使用英文逗号(,)分隔"; - /* Class = "NSTableColumn"; headerCell.title = "Update Time"; ObjectID = "xoc-hs-9qa"; */ "xoc-hs-9qa.headerCell.title" = "更新时间"; /* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "xxZ-9l-69m"; */ "xxZ-9l-69m.title" = "显示日志"; -/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ -"YF4-uZ-A0M.title" = "本地"; - /* Class = "NSButtonCell"; title = "Delete"; ObjectID = "yGD-AG-oYU"; */ "yGD-AG-oYU.title" = "删除"; @@ -292,17 +139,173 @@ /* Class = "NSMenuItem"; title = "Show network indicator"; ObjectID = "YIO-Vj-64f"; */ "YIO-Vj-64f.title" = "显示实时速率"; +/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ +"zwo-q5-k5N.title" = "内核版本"; + +/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ +"7wl-vK-5JO.title" = "复制终端代理命令(外部IP)"; + +/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ +"5hn-k8-CWe.headerCell.title" = "Api Secret"; + +/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ +"9gE-NX-2wJ.title" = "远程控制器"; + +/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ +"B2w-4r-5Kh.title" = "删除"; + +/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ +"ZcF-10-jsl.title" = "添加"; + +/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ +"s6y-wL-pnr.title" = "远程控制器"; + /* Class = "NSTableColumn"; headerCell.title = "Api Url"; ObjectID = "yO6-uZ-IRv"; */ "yO6-uZ-IRv.headerCell.title" = "Api Url"; +/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ +"1He-Eq-fSy.title" = "远程控制器"; + +/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ +"BRR-WK-aeP.title" = "远程控制器"; + +/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ +"hlb-KQ-Fdr.title" = "管理"; + +/* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ +"WkL-aX-66E.title" = "远程控制器允许你控制其他设备上的 Clash 状态。"; + +/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ +"h1H-7k-9HS.title" = "设置更新间隔"; + +/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ +"LAj-p8-9gd.title" = "ClashX 设置"; + +/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ +"Ltt-Vq-Hh1.label" = "通用"; + +/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ +"NPu-V9-f3r.title" = "忽略这些主机与域的代理设置"; + +/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ +"dV6-4Z-2SO.title" = "开机启动"; + +/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ +"kma-mp-ncL.title" = "通用"; + +/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ +"krh-QF-pqZ.title" = "更多设置"; + +/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ +"p7q-KN-kIv.title" = "将配置文件存储在iCloud中"; + +/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ +"jsL-HC-6ne.title" = "减少通知"; + +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ +"sfe-wu-UXp.title" = "使用英文逗号(,)分隔"; + +/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ +"E8B-e5-K0A.title" = "允许局域网控制(不推荐)"; + +/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "端口设置(重启应用生效)"; + +/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ +"mbn-tK-UQa.title" = "Api 端口"; + +/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ +"uUA-LS-Hu8.title" = "代理端口"; + /* Class = "NSButtonCell"; title = "Reset"; ObjectID = "yXh-2Y-aTS"; */ "yXh-2Y-aTS.title" = "重置"; +/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ +"Gnh-m8-PAz.title" = "Box"; + +/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ +"F1s-SF-dqX.title" = "在特定 WiFi SSID 下自动暂停"; + +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ +"xnL-ma-vFo.title" = "使用英文逗号(,)分隔"; + +/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ +"3Om-yT-A83.label" = "调试"; + +/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ +"NLT-FZ-48V.title" = "调试设置"; + +/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ +"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染 (MacOS 13+)"; + +/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ +"kdV-Em-qBi.title" = "调试"; + +/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ +"1d5-NL-UsJ.title" = "代理模式"; + +/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ +"AMT-oc-r8A.title" = "其他"; + +/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ +"BFE-Qq-B2H.title" = "代理设置"; + +/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ +"wKZ-TE-sf8.title" = "全局快捷键"; + +/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ +"GGx-F2-7kE.title" = "请务必注意处理可能存在的快捷键冲突,全局快捷键将优先于普通快捷键。"; + +/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ +"AY0-nP-cGT.title" = "移除助手程序"; + +/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ +"aSG-9A-eeG.title" = "助手程序"; + +/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ +"afj-4G-usr.title" = "打开日志文件夹"; + +/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ +"c01-0L-1SQ.title" = "日志"; + +/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ +"LW4-cA-3bB.title" = "覆盖配置文件设置"; + +/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ +"ckH-Er-PfX.title" = "Api秘钥"; + +/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ +"URV-fZ-bJf.title" = "iCloud"; + +/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ +"YF4-uZ-A0M.title" = "本地"; + /* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ "ZA9-qc-wi4.title" = "配置文件夹"; -/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ -"ZcF-10-jsl.title" = "添加"; +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ +"5ws-55-f1g.title" = "重置"; -/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ -"zwo-q5-k5N.title" = "内核版本"; +/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ +"PF0-Gd-XbR.title" = "应用配置"; + +/* Class = "NSMenuItem"; title = "Connection Details"; ObjectID = "v4s-jd-g1N"; */ +"v4s-jd-g1N.title" = "连接查看器"; + +/* Class = "NSButtonCell"; title = "Use Built-in clash api"; ObjectID = "IET-Bf-hGj"; */ +"IET-Bf-hGj.title" = "使用Clash内置Api"; + +/* Class = "NSButtonCell"; title = "Revert to previous proxy on proxy disabled"; ObjectID = "JOF-yU-YHc"; */ +"JOF-yU-YHc.title" = "取消代理时还原更早的系统代理设置"; + +/* Class = "NSTextFieldCell"; title = "Update Channel"; ObjectID = "NLt-OM-k6i"; */ +"NLt-OM-k6i.title" = "更新通道"; + +/* Class = "NSTextFieldCell"; title = "Geo-IP Datebase"; ObjectID = "OMy-zu-iho"; */ +"OMy-zu-iho.title" = "Geo-IP数据库"; + +/* Class = "NSTextFieldCell"; title = "Proxy Benchmark Url:"; ObjectID = "e0M-xf-ovR"; */ +"e0M-xf-ovR.title" = "代理延迟测试地址"; + +/* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ +"pHl-C4-fNt.title" = "更新"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index 4dcf56b68..6dd7da7f1 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -1,11 +1,23 @@ -/* Class = "NSMenuItem"; title = "ERROR"; ObjectID = "0iu-lB-eZN"; */ -"0iu-lB-eZN.title" = "錯誤"; +/* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ +"tck-zU-JKQ.title" = "配置"; -/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ -"1d5-NL-UsJ.title" = "模式"; +/* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ +"tL1-bl-LXd.title" = "託管的配置文件:"; -/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ -"1He-Eq-fSy.title" = "遙控"; +/* Class = "NSMenu"; title = "API Connect Error"; ObjectID = "UU2-uE-YB4"; */ +"UU2-uE-YB4.title" = "API Connect Error"; + +/* Class = "NSMenuItem"; title = "Allow connect from Lan"; ObjectID = "Vz8-7n-vx6"; */ +"Vz8-7n-vx6.title" = "允許局域網連接"; + +/* Class = "NSMenu"; title = "Log level"; ObjectID = "wqo-3T-4qO"; */ +"wqo-3T-4qO.title" = "日誌等級"; + +/* Class = "NSMenuItem"; title = "Dashboard"; ObjectID = "XG6-2M-PNi"; */ +"XG6-2M-PNi.title" = "控制台"; + +/* Class = "NSMenuItem"; title = "ERROR"; ObjectID = "0iu-lB-eZN"; */ +"0iu-lB-eZN.title" = "錯誤"; /* Class = "NSButtonCell"; title = "Update"; ObjectID = "2Rx-ih-aGW"; */ "2Rx-ih-aGW.title" = "更新"; @@ -13,51 +25,24 @@ /* Class = "NSMenuItem"; title = "Log level"; ObjectID = "3Da-fL-Mzr"; */ "3Da-fL-Mzr.title" = "日誌等級"; -/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ -"3Om-yT-A83.label" = "調試"; - /* Class = "NSButtonCell"; title = "Add"; ObjectID = "51K-nB-xLS"; */ "51K-nB-xLS.title" = "添加"; -/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ -"5hn-k8-CWe.headerCell.title" = "Api Secret"; - -/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ -"5ws-55-f1g.title" = "重置"; - /* Class = "NSViewController"; title = "Remote Configs"; ObjectID = "6WI-Hi-v9j"; */ "6WI-Hi-v9j.title" = "託管的配置文件"; /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "7G6-oO-vNs"; */ "7G6-oO-vNs.title" = "文本單元格"; -/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ -"7wl-vK-5JO.title" = "複製shell命令(外網IP)"; - /* Class = "NSMenuItem"; title = "Rule"; ObjectID = "89n-bD-JHk"; */ "89n-bD-JHk.title" = "規則判斷"; /* Class = "NSMenuItem"; title = "Set as system proxy"; ObjectID = "8se-yr-wmp"; */ "8se-yr-wmp.title" = "設定為系統代理"; -/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ -"9gE-NX-2wJ.title" = "外部控制"; - /* Class = "NSMenuItem"; title = "Ports"; ObjectID = "9i0-LH-x04"; */ "9i0-LH-x04.title" = "端口"; -/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ -"afj-4G-usr.title" = "打開日誌文件夾"; - -/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ -"AMT-oc-r8A.title" = "其他"; - -/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ -"aSG-9A-eeG.title" = "代理助手"; - -/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ -"AY0-nP-cGT.title" = "卸載代理程式"; - /* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */ "AYu-sK-qS6.title" = "主菜單"; @@ -67,27 +52,9 @@ /* Class = "NSMenuItem"; title = "Start at login"; ObjectID = "B1J-XB-BiZ"; */ "B1J-XB-BiZ.title" = "開機啟動"; -/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ -"B2w-4r-5Kh.title" = "刪除"; - -/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ -"bcR-rG-52F.title" = "端口設定"; - -/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ -"BFE-Qq-B2H.title" = "代理"; - -/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ -"BRR-WK-aeP.title" = "遙控"; - -/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ -"c01-0L-1SQ.title" = "日誌"; - /* Class = "NSTableColumn"; headerCell.title = "Url"; ObjectID = "C79-J5-30z"; */ "C79-J5-30z.headerCell.title" = "鏈接"; -/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ -"ckH-Er-PfX.title" = "API 秘鑰"; - /* Class = "NSMenuItem"; title = "Benchmark"; ObjectID = "COu-UX-bww"; */ "COu-UX-bww.title" = "延遲測速"; @@ -97,9 +64,6 @@ /* Class = "NSMenuItem"; title = "Help"; ObjectID = "Dd9-2F-FVY"; */ "Dd9-2F-FVY.title" = "幫助"; -/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ -"dV6-4Z-2SO.title" = "登錄時啟動"; - /* Class = "NSMenuItem"; title = "SILENT"; ObjectID = "dVr-Xp-C0C"; */ "dVr-Xp-C0C.title" = "SILENT"; @@ -109,33 +73,9 @@ /* Class = "NSMenuItem"; title = "Manage"; ObjectID = "Dwg-Qb-2AU"; */ "Dwg-Qb-2AU.title" = "管理"; -/* Class = "NSTextFieldCell"; title = "Proxy Benchmark Url:"; ObjectID = "e0M-xf-ovR"; */ -"e0M-xf-ovR.title" = "代理延遲測速連結"; - -/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ -"E8B-e5-K0A.title" = "允許局域網設備"; - -/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ -"eY9-1i-i7P.title" = "使用 SwiftUI 渲染狀態欄圖標"; - -/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ -"F1s-SF-dqX.title" = "SSID 掛起"; - -/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ -"GGx-F2-7kE.title" = "請確保解決任何潛在的快捷方式衝突,全局快捷方式優先於常規快捷方式"; - -/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ -"Gnh-m8-PAz.title" = "盒子"; - /* Class = "NSMenuItem"; title = "Remote config"; ObjectID = "h1C-R6-Y9w"; */ "h1C-R6-Y9w.title" = "託管配置"; -/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ -"h1H-7k-9HS.title" = "設定更新間隔"; - -/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ -"hlb-KQ-Fdr.title" = "管理"; - /* Class = "NSMenuItem"; title = "About"; ObjectID = "hUb-k9-TEf"; */ "hUb-k9-TEf.title" = "關於"; @@ -145,9 +85,6 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "id7-f0-u56"; */ "id7-f0-u56.title" = "Text Cell"; -/* Class = "NSButtonCell"; title = "Use Built-in clash api"; ObjectID = "IET-Bf-hGj"; */ -"IET-Bf-hGj.title" = "使用內建Clash接口"; - /* Class = "NSMenuItem"; title = "API Connect Error"; ObjectID = "jGT-1M-xJu"; */ "jGT-1M-xJu.title" = "API Connect Error"; @@ -157,72 +94,24 @@ /* Class = "NSMenuItem"; title = "Config"; ObjectID = "JMV-Dy-CI0"; */ "JMV-Dy-CI0.title" = "配置"; -/* Class = "NSButtonCell"; title = "Revert to previous proxy on proxy disabled"; ObjectID = "JOF-yU-YHc"; */ -"JOF-yU-YHc.title" = "關閉代理時還原先前系統代理設置"; - -/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ -"jsL-HC-6ne.title" = "減少通知"; - -/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ -"kdV-Em-qBi.title" = "調試"; - -/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ -"kma-mp-ncL.title" = "一般設定"; - /* Class = "NSMenuItem"; title = "WARNING"; ObjectID = "ko2-Ir-DxA"; */ "ko2-Ir-DxA.title" = "WARNING"; -/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ -"krh-QF-pqZ.title" = "設定"; - -/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ -"LAj-p8-9gd.title" = "設定"; - /* Class = "NSTableColumn"; headerCell.title = "Config Name"; ObjectID = "lRE-Xa-euB"; */ "lRE-Xa-euB.headerCell.title" = "配置文件名稱"; -/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ -"Ltt-Vq-Hh1.label" = "一般設定"; - -/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ -"LW4-cA-3bB.title" = "覆蓋配置文件設置"; - -/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ -"mbn-tK-UQa.title" = "API 端口"; - -/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ -"NLT-FZ-48V.title" = "調試設定"; - -/* Class = "NSTextFieldCell"; title = "Update Channel"; ObjectID = "NLt-OM-k6i"; */ -"NLt-OM-k6i.title" = "更新通道"; - /* Class = "NSMenuItem"; title = "Direct"; ObjectID = "Np6-Pm-Lo3"; */ "Np6-Pm-Lo3.title" = "直接連接"; -/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ -"NPu-V9-f3r.title" = "繞過這些主機和域的代理設定"; - /* Class = "NSMenuItem"; title = "Quit"; ObjectID = "NXU-86-Eem"; */ "NXU-86-Eem.title" = "退出"; /* Class = "NSMenu"; title = "Help"; ObjectID = "ogW-pn-jeR"; */ "ogW-pn-jeR.title" = "幫助"; -/* Class = "NSTextFieldCell"; title = "Geo-IP Datebase"; ObjectID = "OMy-zu-iho"; */ -"OMy-zu-iho.title" = "Geo-IP資料庫"; - /* Class = "NSMenuItem"; title = "Check Update"; ObjectID = "p0T-J8-Emx"; */ "p0T-J8-Emx.title" = "檢查更新"; -/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ -"p7q-KN-kIv.title" = "使用iCloud 存儲配置文件"; - -/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ -"PF0-Gd-XbR.title" = "應用配置"; - -/* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ -"pHl-C4-fNt.title" = "更新"; - /* Class = "NSMenuItem"; title = "Reload config"; ObjectID = "q3G-VH-eyy"; */ "q3G-VH-eyy.title" = "重載配置文件"; @@ -232,45 +121,129 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RCv-zz-HKW"; */ "RCv-zz-HKW.title" = "Text Cell"; -/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ -"s6y-wL-pnr.title" = "外部管理"; +/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ +"1He-Eq-fSy.title" = "遙控"; -/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ -"sfe-wu-UXp.title" = "以逗號(,)分隔"; +/* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ +"1d5-NL-UsJ.title" = "模式"; -/* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ -"tck-zU-JKQ.title" = "配置"; +/* Class = "NSTabViewItem"; label = "Debug"; ObjectID = "3Om-yT-A83"; */ +"3Om-yT-A83.label" = "調試"; -/* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ -"tL1-bl-LXd.title" = "託管的配置文件:"; +/* Class = "NSTableColumn"; headerCell.title = "Api Secret"; ObjectID = "5hn-k8-CWe"; */ +"5hn-k8-CWe.headerCell.title" = "Api Secret"; -/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ -"URV-fZ-bJf.title" = "iCloud"; +/* Class = "NSMenuItem"; title = "Copy shell command (External IP)"; ObjectID = "7wl-vK-5JO"; */ +"7wl-vK-5JO.title" = "複製shell命令(外網IP)"; -/* Class = "NSMenu"; title = "API Connect Error"; ObjectID = "UU2-uE-YB4"; */ -"UU2-uE-YB4.title" = "API Connect Error"; +/* Class = "NSTextFieldCell"; title = "External Controls"; ObjectID = "9gE-NX-2wJ"; */ +"9gE-NX-2wJ.title" = "外部控制"; -/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ -"uUA-LS-Hu8.title" = "代理端口"; +/* Class = "NSBox"; title = "Other"; ObjectID = "AMT-oc-r8A"; */ +"AMT-oc-r8A.title" = "其他"; -/* Class = "NSMenuItem"; title = "Allow connect from Lan"; ObjectID = "Vz8-7n-vx6"; */ -"Vz8-7n-vx6.title" = "允許局域網連接"; +/* Class = "NSButtonCell"; title = "Uninstall Proxy Helper"; ObjectID = "AY0-nP-cGT"; */ +"AY0-nP-cGT.title" = "卸載代理程式"; -/* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ -"WkL-aX-66E.title" = "這允許你控制在不同機器上運行的衝突核心"; +/* Class = "NSButtonCell"; title = "Delete"; ObjectID = "B2w-4r-5Kh"; */ +"B2w-4r-5Kh.title" = "刪除"; -/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ -"wKZ-TE-sf8.title" = "全局快捷方式"; +/* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ +"BFE-Qq-B2H.title" = "代理"; -/* Class = "NSMenu"; title = "Log level"; ObjectID = "wqo-3T-4qO"; */ -"wqo-3T-4qO.title" = "日誌等級"; +/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ +"BRR-WK-aeP.title" = "遙控"; -/* Class = "NSMenuItem"; title = "Dashboard"; ObjectID = "XG6-2M-PNi"; */ -"XG6-2M-PNi.title" = "控制台"; +/* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ +"E8B-e5-K0A.title" = "允許局域網設備"; + +/* Class = "NSTextFieldCell"; title = "SSID Suspend"; ObjectID = "F1s-SF-dqX"; */ +"F1s-SF-dqX.title" = "SSID 掛起"; + +/* Class = "NSTextFieldCell"; title = "Please ensure to address any potential shortcut conflicts. Global shortcuts take precedence over regular shortcuts."; ObjectID = "GGx-F2-7kE"; */ +"GGx-F2-7kE.title" = "請確保解決任何潛在的快捷方式衝突,全局快捷方式優先於常規快捷方式"; + +/* Class = "NSBox"; title = "Box"; ObjectID = "Gnh-m8-PAz"; */ +"Gnh-m8-PAz.title" = "盒子"; + +/* Class = "NSTabViewController"; title = "Settings"; ObjectID = "LAj-p8-9gd"; */ +"LAj-p8-9gd.title" = "設定"; + +/* Class = "NSTabViewItem"; label = "General"; ObjectID = "Ltt-Vq-Hh1"; */ +"Ltt-Vq-Hh1.label" = "一般設定"; + +/* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ +"NLT-FZ-48V.title" = "調試設定"; + +/* Class = "NSTextFieldCell"; title = "Bypass proxy settings for these Hosts & Domains"; ObjectID = "NPu-V9-f3r"; */ +"NPu-V9-f3r.title" = "繞過這些主機和域的代理設定"; + +/* Class = "NSTextFieldCell"; title = "This allows you to control the clash core running in the different machine"; ObjectID = "WkL-aX-66E"; */ +"WkL-aX-66E.title" = "這允許你控制在不同機器上運行的衝突核心"; /* Class = "NSMenuItem"; title = "DEBUG"; ObjectID = "XIR-Go-fWA"; */ "XIR-Go-fWA.title" = "調試"; +/* Class = "NSMenuItem"; title = "Show network indicator"; ObjectID = "YIO-Vj-64f"; */ +"YIO-Vj-64f.title" = "顯示網絡指示器"; + +/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ +"ZcF-10-jsl.title" = "添加"; + +/* Class = "NSTextFieldCell"; title = "Proxy Helper"; ObjectID = "aSG-9A-eeG"; */ +"aSG-9A-eeG.title" = "代理助手"; + +/* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ +"afj-4G-usr.title" = "打開日誌文件夾"; + +/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "端口設定"; + +/* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ +"c01-0L-1SQ.title" = "日誌"; + +/* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ +"dV6-4Z-2SO.title" = "登錄時啟動"; + +/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ +"eY9-1i-i7P.title" = "使用 SwiftUI 渲染狀態欄圖標"; + +/* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ +"h1H-7k-9HS.title" = "設定更新間隔"; + +/* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ +"hlb-KQ-Fdr.title" = "管理"; + +/* Class = "NSButtonCell"; title = "Reduce notifications"; ObjectID = "jsL-HC-6ne"; */ +"jsL-HC-6ne.title" = "減少通知"; + +/* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ +"kdV-Em-qBi.title" = "調試"; + +/* Class = "NSViewController"; title = "General"; ObjectID = "kma-mp-ncL"; */ +"kma-mp-ncL.title" = "一般設定"; + +/* Class = "NSMenuItem"; title = "Settings"; ObjectID = "krh-QF-pqZ"; */ +"krh-QF-pqZ.title" = "設定"; + +/* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ +"mbn-tK-UQa.title" = "API 端口"; + +/* Class = "NSButtonCell"; title = "Use iCloud to store config files"; ObjectID = "p7q-KN-kIv"; */ +"p7q-KN-kIv.title" = "使用iCloud 存儲配置文件"; + +/* Class = "NSViewController"; title = "External Manager"; ObjectID = "s6y-wL-pnr"; */ +"s6y-wL-pnr.title" = "外部管理"; + +/* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "sfe-wu-UXp"; */ +"sfe-wu-UXp.title" = "以逗號(,)分隔"; + +/* Class = "NSTextFieldCell"; title = "Proxy Port:"; ObjectID = "uUA-LS-Hu8"; */ +"uUA-LS-Hu8.title" = "代理端口"; + +/* Class = "NSViewController"; title = "Global ShortCut"; ObjectID = "wKZ-TE-sf8"; */ +"wKZ-TE-sf8.title" = "全局快捷方式"; + /* Class = "NSTextFieldCell"; title = "Separated by commas(,)"; ObjectID = "xnL-ma-vFo"; */ "xnL-ma-vFo.title" = "以逗號(,)分隔"; @@ -280,29 +253,59 @@ /* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "xxZ-9l-69m"; */ "xxZ-9l-69m.title" = "顯示日誌"; -/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ -"YF4-uZ-A0M.title" = "本地"; - /* Class = "NSButtonCell"; title = "Delete"; ObjectID = "yGD-AG-oYU"; */ "yGD-AG-oYU.title" = "刪除"; -/* Class = "NSMenuItem"; title = "Global"; ObjectID = "yiM-U4-MNg"; */ -"yiM-U4-MNg.title" = "全局"; - -/* Class = "NSMenuItem"; title = "Show network indicator"; ObjectID = "YIO-Vj-64f"; */ -"YIO-Vj-64f.title" = "顯示網絡指示器"; - /* Class = "NSTableColumn"; headerCell.title = "Api Url"; ObjectID = "yO6-uZ-IRv"; */ "yO6-uZ-IRv.headerCell.title" = "API 網址"; /* Class = "NSButtonCell"; title = "Reset"; ObjectID = "yXh-2Y-aTS"; */ "yXh-2Y-aTS.title" = "重置"; +/* Class = "NSMenuItem"; title = "Global"; ObjectID = "yiM-U4-MNg"; */ +"yiM-U4-MNg.title" = "全局"; + +/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ +"zwo-q5-k5N.title" = "核心版本"; + +/* Class = "NSButtonCell"; title = "Override Config Setting"; ObjectID = "LW4-cA-3bB"; */ +"LW4-cA-3bB.title" = "覆蓋配置文件設置"; + +/* Class = "NSTextFieldCell"; title = "Api Secret:"; ObjectID = "ckH-Er-PfX"; */ +"ckH-Er-PfX.title" = "API 秘鑰"; + +/* Class = "NSButtonCell"; title = "iCloud"; ObjectID = "URV-fZ-bJf"; */ +"URV-fZ-bJf.title" = "iCloud"; + +/* Class = "NSButtonCell"; title = "Local"; ObjectID = "YF4-uZ-A0M"; */ +"YF4-uZ-A0M.title" = "本地"; + /* Class = "NSTextFieldCell"; title = "Config Folder"; ObjectID = "ZA9-qc-wi4"; */ "ZA9-qc-wi4.title" = "配置文件夾"; -/* Class = "NSButtonCell"; title = "Add"; ObjectID = "ZcF-10-jsl"; */ -"ZcF-10-jsl.title" = "新增"; +/* Class = "NSButtonCell"; title = "Reset"; ObjectID = "5ws-55-f1g"; */ +"5ws-55-f1g.title" = "重置"; -/* Class = "NSTextFieldCell"; title = "Core Version"; ObjectID = "zwo-q5-k5N"; */ -"zwo-q5-k5N.title" = "核心版本"; +/* Class = "NSTextFieldCell"; title = "App Setting"; ObjectID = "PF0-Gd-XbR"; */ +"PF0-Gd-XbR.title" = "應用配置"; + +/* Class = "NSMenuItem"; title = "Connection Details"; ObjectID = "v4s-jd-g1N"; */ +"v4s-jd-g1N.title" = "連接詳情"; + +/* Class = "NSButtonCell"; title = "Use Built-in clash api"; ObjectID = "IET-Bf-hGj"; */ +"IET-Bf-hGj.title" = "使用內建 Clash API"; + +/* Class = "NSButtonCell"; title = "Revert to previous proxy on proxy disabled"; ObjectID = "JOF-yU-YHc"; */ +"JOF-yU-YHc.title" = "停用代理后恢复先前的代理设置"; + +/* Class = "NSTextFieldCell"; title = "Update Channel"; ObjectID = "NLt-OM-k6i"; */ +"NLt-OM-k6i.title" = "更新通道"; + +/* Class = "NSTextFieldCell"; title = "Geo-IP Datebase"; ObjectID = "OMy-zu-iho"; */ +"OMy-zu-iho.title" = "Geo-IP 数据库"; + +/* Class = "NSTextFieldCell"; title = "Proxy Benchmark Url:"; ObjectID = "e0M-xf-ovR"; */ +"e0M-xf-ovR.title" = "代理延遲測試地址"; + +/* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ +"pHl-C4-fNt.title" = "更新"; diff --git a/fastlane/Fastfile b/fastlane/Fastfile index ccef350e2..8d5c5544c 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -24,25 +24,6 @@ lane :check do ) end -lane :beta do -current_version = get_version_number( - target: "ClashX" -) -timestamp = Time.now.utc -new_build_identifier = "%d%02d%02d%02d%02d" % [ - timestamp.year, - timestamp.month, - timestamp.day, - timestamp.hour, - timestamp.min, -] -new_version = current_version + "." + new_build_identifier -increment_build_number_in_plist( - build_number: new_version, - target: 'ClashX' -) -end - lane :addKeyChain do if is_ci? diff --git a/ClashX/swiftFormate.sh b/updateLocalization.sh similarity index 100% rename from ClashX/swiftFormate.sh rename to updateLocalization.sh From 1359a23969ccd334f12339c67c4585fd4c7ba982 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 18 Jul 2023 13:40:37 +0800 Subject: [PATCH 087/122] misc: update connection panel localization --- .../ConnectionDetailInfoGeneralView.strings | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings b/ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings index 421d5ac0d..9509734db 100644 --- a/ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings +++ b/ClashX/ViewControllers/Connections/Views/zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings @@ -1,53 +1,53 @@ /* Class = "NSTextFieldCell"; title = "NetworkType"; ObjectID = "6p6-n8-rBg"; */ -"6p6-n8-rBg.title" = "NetworkType"; +"6p6-n8-rBg.title" = "網絡類型"; /* Class = "NSBox"; title = "Current Speed"; ObjectID = "7fZ-OX-aWF"; */ -"7fZ-OX-aWF.title" = "Current Speed"; +"7fZ-OX-aWF.title" = "實時速率"; /* Class = "NSTextFieldCell"; title = "Source"; ObjectID = "9Id-dT-XYP"; */ -"9Id-dT-XYP.title" = "Source"; +"9Id-dT-XYP.title" = "來源"; /* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "CGD-ut-PXk"; */ -"CGD-ut-PXk.title" = "Upload"; +"CGD-ut-PXk.title" = "上傳"; /* Class = "NSBox"; title = "Rule"; ObjectID = "CiP-Ib-BPd"; */ -"CiP-Ib-BPd.title" = "Rule"; +"CiP-Ib-BPd.title" = "規則"; /* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "GhE-tw-D8L"; */ -"GhE-tw-D8L.title" = "Upload"; +"GhE-tw-D8L.title" = "上傳"; /* Class = "NSTextFieldCell"; title = "Entry"; ObjectID = "KQt-5l-lyc"; */ -"KQt-5l-lyc.title" = "Entry"; +"KQt-5l-lyc.title" = "入口"; /* Class = "NSTextFieldCell"; title = "Upload"; ObjectID = "KsS-nl-D2n"; */ -"KsS-nl-D2n.title" = "Upload"; +"KsS-nl-D2n.title" = "上傳"; /* Class = "NSBox"; title = "Proxy Chain"; ObjectID = "MIz-sg-4Rx"; */ -"MIz-sg-4Rx.title" = "Proxy Chain"; +"MIz-sg-4Rx.title" = "代理鏈"; /* Class = "NSBox"; title = "Max Speed"; ObjectID = "NBq-b1-RLL"; */ -"NBq-b1-RLL.title" = "Max Speed"; +"NBq-b1-RLL.title" = "最高速率"; /* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "ZEc-E9-AXS"; */ -"ZEc-E9-AXS.title" = "Download"; +"ZEc-E9-AXS.title" = "下載"; /* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "iem-RP-B0u"; */ -"iem-RP-B0u.title" = "Download"; +"iem-RP-B0u.title" = "下載"; /* Class = "NSBox"; title = "General"; ObjectID = "leq-MF-MFL"; */ -"leq-MF-MFL.title" = "General"; +"leq-MF-MFL.title" = "通用"; /* Class = "NSTextFieldCell"; title = "Dest."; ObjectID = "m5m-4U-xAi"; */ -"m5m-4U-xAi.title" = "Dest."; +"m5m-4U-xAi.title" = "去向"; /* Class = "NSBox"; title = "Other"; ObjectID = "nG8-0D-7W1"; */ -"nG8-0D-7W1.title" = "Other"; +"nG8-0D-7W1.title" = "其他"; /* Class = "NSBox"; title = "Address"; ObjectID = "obG-yt-Gi8"; */ -"obG-yt-Gi8.title" = "Address"; +"obG-yt-Gi8.title" = "地址"; /* Class = "NSTextFieldCell"; title = "Download"; ObjectID = "tes-yR-PKh"; */ -"tes-yR-PKh.title" = "Download"; +"tes-yR-PKh.title" = "下載"; /* Class = "NSBox"; title = "Total"; ObjectID = "xB0-fx-J0y"; */ -"xB0-fx-J0y.title" = "Total"; +"xB0-fx-J0y.title" = "總計"; From 2024a12ecd73f2683a3957e67168ea19dfc07482 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 17 Jul 2023 14:47:41 +0800 Subject: [PATCH 088/122] misc: move setting to the first page of menu --- ClashX/Base.lproj/Main.storyboard | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 2475b0b88..e000a845f 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -784,9 +784,9 @@ - + - + @@ -842,16 +842,16 @@ - - - - - - - + + + + + + + From 740f84c99bdbffea160c8b91a7ede2ae24913ffb Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 18 Jul 2023 11:09:14 +0800 Subject: [PATCH 089/122] fix: should replace mmdb on app upgrade --- ClashX/General/Managers/ClashResourceManager.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ClashX/General/Managers/ClashResourceManager.swift b/ClashX/General/Managers/ClashResourceManager.swift index faa74b3c7..1ce79ceae 100644 --- a/ClashX/General/Managers/ClashResourceManager.swift +++ b/ClashX/General/Managers/ClashResourceManager.swift @@ -31,8 +31,7 @@ class ClashResourceManager { if fileManage.fileExists(atPath: destMMDBPath) { let vaild = verifyGEOIPDataBase().toBool() let versionChange = AppVersionUtil.hasVersionChanged || AppVersionUtil.isFirstLaunch - let customMMDBSet = !Settings.mmdbDownloadUrl.isEmpty - if !vaild || (versionChange && customMMDBSet) { + if !vaild || versionChange { Logger.log("removing new mmdb file") try? fileManage.removeItem(atPath: destMMDBPath) } From 2268b132a2a81ef1568dab4018db6071d54e8f5f Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 18 Jul 2023 13:34:01 +0800 Subject: [PATCH 090/122] fix: connection panel dark mode issue[beta] --- .../Views/ConnectionDetailInfoView.swift | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift index b67d6945f..07067f010 100644 --- a/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift +++ b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift @@ -22,10 +22,28 @@ class ConnectionDetailInfoView: NSView { init() { super.init(frame: .zero) wantsLayer = true - layer?.backgroundColor = NSColor.white.cgColor + updateColor() setupSubviews() } + override func viewDidChangeEffectiveAppearance() { + super.viewDidChangeEffectiveAppearance() + updateColor() + } + + func updateColor() { + if #available(macOS 11.0, *) { + effectiveAppearance.performAsCurrentDrawingAppearance { + layer?.backgroundColor = NSColor.controlColor.cgColor + } + } else { + let pervious = NSAppearance.current + NSAppearance.current = effectiveAppearance + layer?.backgroundColor = NSColor.controlColor.cgColor + NSAppearance.current = pervious + } + } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } From 7251a8c2a0179a1281ec62ec9238ec8b22d106cc Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 18 Jul 2023 15:11:47 +0800 Subject: [PATCH 091/122] misc: update core --- ClashX/goClash/go.mod | 24 ++++++++++----------- ClashX/goClash/go.sum | 50 +++++++++++++++++++++---------------------- Podfile.lock | 16 +++++++------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 82db1909c..95f680e86 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,13 +3,13 @@ module github.com/yichengchen/clashX/ClashX go 1.20 require ( - github.com/Dreamacro/clash v1.16.1-0.20230625011906-5212aaf445ec - github.com/oschwald/geoip2-golang v1.8.0 + github.com/Dreamacro/clash v1.17.1-0.20230714142015-c7e34bdc1171 + github.com/oschwald/geoip2-golang v1.9.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) require ( - github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd // indirect + github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 // indirect github.com/ajg/form v1.5.1 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-chi/chi/v5 v5.0.8 // indirect @@ -18,27 +18,27 @@ require ( github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb // indirect + github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df // indirect github.com/josharian/native v1.1.0 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect - github.com/miekg/dns v1.1.54 // indirect - github.com/oschwald/maxminddb-golang v1.10.0 // indirect + github.com/miekg/dns v1.1.55 // indirect + github.com/oschwald/maxminddb-golang v1.11.0 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/samber/lo v1.38.1 // indirect - github.com/sirupsen/logrus v1.9.2 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 // indirect github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/atomic v1.11.0 // indirect - golang.org/x/crypto v0.9.0 // indirect + golang.org/x/crypto v0.10.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/net v0.11.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect golang.org/x/tools v0.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 7df810d82..033942e12 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,7 +1,7 @@ -github.com/Dreamacro/clash v1.16.1-0.20230625011906-5212aaf445ec h1:WjddJYCq3BhYPCMAbTeOVIqYZORs5xkENpp/IYzRHF8= -github.com/Dreamacro/clash v1.16.1-0.20230625011906-5212aaf445ec/go.mod h1:JmNt+xgGcYPlS3+1wwn7BIbjRdThukFvhgerPDIQI2g= -github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8= -github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= +github.com/Dreamacro/clash v1.17.1-0.20230714142015-c7e34bdc1171 h1:dEm8h8NASkdLLz8WwI/zfkH5TjRChIQ/m82IOHi3e0E= +github.com/Dreamacro/clash v1.17.1-0.20230714142015-c7e34bdc1171/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE= +github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k= +github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -21,8 +21,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q= -github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= +github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df h1:pF1MMIzEJzJ/MyI4bXYXVYyN8CJgoQ2PPKT2z3O/Cl4= +github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -30,12 +30,12 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= -github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= -github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= -github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= -github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= -github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= +github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= +github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= +github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= +github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0= +github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= @@ -44,11 +44,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= -github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 h1:F9xjJm4IH8VjcqG4ujciOF+GIM4mjPkHhWLLzOghPtM= @@ -59,24 +59,24 @@ go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/Podfile.lock b/Podfile.lock index e08603e47..00b624f17 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Alamofire (5.7.1) - - AppCenter/Analytics (5.0.2): + - AppCenter/Analytics (5.0.3): - AppCenter/Core - - AppCenter/Core (5.0.2) - - AppCenter/Crashes (5.0.2): + - AppCenter/Core (5.0.3) + - AppCenter/Crashes (5.0.3): - AppCenter/Core - CocoaLumberjack/Core (3.8.0) - CocoaLumberjack/Swift (3.8.0): @@ -17,9 +17,9 @@ PODS: - RxRelay (6.5.0): - RxSwift (= 6.5.0) - RxSwift (6.5.0) - - Sparkle (2.4.1) + - Sparkle (2.4.2) - Starscream (3.1.1) - - SwiftLint (0.52.2) + - SwiftLint (0.52.4) - SwiftyJSON (5.0.1) - WebViewJavascriptBridge (6.0.3) @@ -58,7 +58,7 @@ SPEC REPOS: SPEC CHECKSUMS: Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88 - AppCenter: 355ba776b273d30147c3ac385c558bec60a7d4b1 + AppCenter: a4070ec3d4418b5539067a51f57155012e486ebd CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa @@ -66,9 +66,9 @@ SPEC CHECKSUMS: RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 - Sparkle: 02038653ca2cb9b64bc68952657c28c4d4c8c75d + Sparkle: 5ef7097e655c60f4aeb23fd1658fc3e8dd50f4ec Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0 - SwiftLint: 1ac76dac888ca05cb0cf24d0c85887ec1209961d + SwiftLint: 1cc5cd61ba9bacb2194e340aeb47a2a37fda00b3 SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 From bbe83be7794b36d947df3d812f6b00961a252b91 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 20 Jul 2023 08:52:28 +0800 Subject: [PATCH 092/122] fix: crash when sort by network --- ClashX/AppDelegate.swift | 2 +- ClashX/Models/ClashConnection.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index cc6b04d46..e791aea4f 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -842,7 +842,7 @@ extension AppDelegate { #if DEBUG return #else - UserDefaults.standard.register(defaults: ["NSApplicationCrashOnExceptions": true]) + UserDefaults.standard.register(defaults: ["NSApplicationCrashOnExceptions": false]) let x = UserDefaults.standard var launch_fail_times: Int = 0 if let xx = x.object(forKey: "launch_fail_times") as? Int { launch_fail_times = xx } diff --git a/ClashX/Models/ClashConnection.swift b/ClashX/Models/ClashConnection.swift index 2d044c2b7..270f5490b 100644 --- a/ClashX/Models/ClashConnection.swift +++ b/ClashX/Models/ClashConnection.swift @@ -116,7 +116,7 @@ extension ClashConnectionSnapShot { } // {"network":"tcp","type":"HTTP Connect","sourceIP":"127.0.0.1","destinationIP":"124.72.132.104","sourcePort":"59217","destinationPort":"443","host":"slardar-bd.feishu.cn","dnsMode":"normal","processPath":"","specialProxy":""} class MetaData:NSObject, Codable { - let network:String + @objc let network:String @objc let type:String let sourceIP: String let destinationIP:String From fea5404fe734374a221c7b4692a16ffa7abd0080 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 24 Jul 2023 09:17:01 +0800 Subject: [PATCH 093/122] fix: detail page bg color in dark mode --- .../Connections/Views/ConnectionDetailInfoView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift index 07067f010..95cdb8cdd 100644 --- a/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift +++ b/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift @@ -34,12 +34,12 @@ class ConnectionDetailInfoView: NSView { func updateColor() { if #available(macOS 11.0, *) { effectiveAppearance.performAsCurrentDrawingAppearance { - layer?.backgroundColor = NSColor.controlColor.cgColor + layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor } } else { let pervious = NSAppearance.current NSAppearance.current = effectiveAppearance - layer?.backgroundColor = NSColor.controlColor.cgColor + layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor NSAppearance.current = pervious } } From bc83166ee880d68a5ea1065b74613619dbff7248 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 24 Jul 2023 16:51:58 +0800 Subject: [PATCH 094/122] misc: update core --- ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 95f680e86..9c476f3b8 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -3,7 +3,7 @@ module github.com/yichengchen/clashX/ClashX go 1.20 require ( - github.com/Dreamacro/clash v1.17.1-0.20230714142015-c7e34bdc1171 + github.com/Dreamacro/clash v1.17.1-0.20230722093530-07ed6e8bef51 github.com/oschwald/geoip2-golang v1.9.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 033942e12..0dc9ffb89 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.17.1-0.20230714142015-c7e34bdc1171 h1:dEm8h8NASkdLLz8WwI/zfkH5TjRChIQ/m82IOHi3e0E= -github.com/Dreamacro/clash v1.17.1-0.20230714142015-c7e34bdc1171/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE= +github.com/Dreamacro/clash v1.17.1-0.20230722093530-07ed6e8bef51 h1:VzvedSu73t2Y/LVq8b7lSVrqfIK1fNgBqcpG06Pf7SI= +github.com/Dreamacro/clash v1.17.1-0.20230722093530-07ed6e8bef51/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE= github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k= github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= From 2448a80f111b2eabcf62d09776d319b94ffdc133 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 24 Jul 2023 17:06:18 +0800 Subject: [PATCH 095/122] misc: adjust connection panel order & enable auto save --- .../Connections/Views/ConnectionColume.swift | 14 +++++++++++--- .../Connections/Views/ConnectionTopListView.swift | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionColume.swift b/ClashX/ViewControllers/Connections/Views/ConnectionColume.swift index 0ddb2a0a3..5855a5ce7 100644 --- a/ClashX/ViewControllers/Connections/Views/ConnectionColume.swift +++ b/ClashX/ViewControllers/Connections/Views/ConnectionColume.swift @@ -17,13 +17,13 @@ enum ConnectionColume:String, CaseIterable { case statusIcon case process case status + case date case url case rule - case date - case upload - case download case currentUpload case currentDownload + case upload + case download case type var columeTitle:String { @@ -69,6 +69,14 @@ enum ConnectionColume:String, CaseIterable { } } + var width:CGFloat { + switch self { + case .upload, .download, .currentUpload, .currentDownload: return 80 + case .status: return 50 + default:return 100 + } + } + var maxWidth:CGFloat { switch self { case .statusIcon: return 16 diff --git a/ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift b/ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift index 1775d928c..4d3fb3d70 100644 --- a/ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift +++ b/ClashX/ViewControllers/Connections/Views/ConnectionTopListView.swift @@ -43,9 +43,12 @@ class ConnectionTopListView: NSView { column.title = columnType.columeTitle column.minWidth = columnType.minWidth column.maxWidth = columnType.maxWidth + column.width = columnType.width column.sortDescriptorPrototype = viewModel.sortSortDescriptor(for: columnType) tableView.addTableColumn(column) } + tableView.autosaveName = className.appending("tableAutoSave") + tableView.autosaveTableColumns = true tableView.sortDescriptors = [viewModel.currentSortDescriptor].compactMap {$0} tableView.usesAlternatingRowBackgroundColors = true tableView.delegate = self From c113a33faa453d6fc14d5d059c825cdcb68e70a2 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 24 Jul 2023 17:13:46 +0800 Subject: [PATCH 096/122] misc: update keyboardshortcuts --- ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3aa64d11b..a900cdd2d 100644 --- a/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sindresorhus/KeyboardShortcuts.git", "state" : { - "revision" : "018e445b340dac78dfc2a4c97c45d9571507c3b5", - "version" : "1.11.0" + "revision" : "8b1a9ce78c2f35c8a55dcc95897573abd2cc4f6e", + "version" : "1.13.0" } } ], From d1c69185d7ba6b5eb74c8b2be310b792731de3ca Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Thu, 24 Aug 2023 16:14:36 +0800 Subject: [PATCH 097/122] misc: add vless enum --- ClashX/Models/ClashProxy.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index da51fc17c..016a96e47 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -26,6 +26,7 @@ enum ClashProxyType: String, Codable { case relay = "Relay" case unknown = "Unknown" case wireguard = "Wireguard" + case vless = "Vless" static let proxyGroups: [ClashProxyType] = [.select, .urltest, .fallback, .loadBalance] From 8346f2aeba06bb3efea14d06df508bdd367141e9 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:45:49 +0800 Subject: [PATCH 098/122] misc: update core --- ClashX/goClash/go.mod | 20 +++++++++++--------- ClashX/goClash/go.sum | 33 +++++++++++++++++---------------- Podfile.lock | 10 +++++----- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 9c476f3b8..46fc2f8e2 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -1,9 +1,11 @@ module github.com/yichengchen/clashX/ClashX -go 1.20 +go 1.21 + +toolchain go1.21.0 require ( - github.com/Dreamacro/clash v1.17.1-0.20230722093530-07ed6e8bef51 + github.com/Dreamacro/clash v1.18.1-0.20230902125642-db2b5db2c6e2 github.com/oschwald/geoip2-golang v1.9.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) @@ -12,13 +14,13 @@ require ( github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 // indirect github.com/ajg/form v1.5.1 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect - github.com/go-chi/chi/v5 v5.0.8 // indirect + github.com/go-chi/chi/v5 v5.0.10 // indirect github.com/go-chi/cors v1.2.1 // indirect - github.com/go-chi/render v1.0.2 // indirect + github.com/go-chi/render v1.0.3 // indirect github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df // indirect + github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d // indirect github.com/josharian/native v1.1.0 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect @@ -32,13 +34,13 @@ require ( github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/atomic v1.11.0 // indirect - golang.org/x/crypto v0.10.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.11.0 // indirect + golang.org/x/net v0.14.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.9.0 // indirect - golang.org/x/text v0.10.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/tools v0.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index 0dc9ffb89..e3038263d 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.17.1-0.20230722093530-07ed6e8bef51 h1:VzvedSu73t2Y/LVq8b7lSVrqfIK1fNgBqcpG06Pf7SI= -github.com/Dreamacro/clash v1.17.1-0.20230722093530-07ed6e8bef51/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE= +github.com/Dreamacro/clash v1.18.1-0.20230902125642-db2b5db2c6e2 h1:M1xQyDbW0SA+uHrTxGbrf04GGbF/08vrWrW+Q5hLaf4= +github.com/Dreamacro/clash v1.18.1-0.20230902125642-db2b5db2c6e2/go.mod h1:r//xe/2pA3Zl+3fjIiI/o6RjIVd+z87drCD58dpRnFg= github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k= github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -9,20 +9,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= -github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= +github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= -github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= +github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df h1:pF1MMIzEJzJ/MyI4bXYXVYyN8CJgoQ2PPKT2z3O/Cl4= -github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= +github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d h1:Ka64cclWedOkGzm9M2/XYuwJUdmWRUozmsxW0PyKA3A= +github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -49,6 +49,7 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 h1:F9xjJm4IH8VjcqG4ujciOF+GIM4mjPkHhWLLzOghPtM= @@ -59,24 +60,24 @@ go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/Podfile.lock b/Podfile.lock index 00b624f17..28e42184d 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,12 +1,12 @@ PODS: - - Alamofire (5.7.1) + - Alamofire (5.8.0) - AppCenter/Analytics (5.0.3): - AppCenter/Core - AppCenter/Core (5.0.3) - AppCenter/Crashes (5.0.3): - AppCenter/Core - - CocoaLumberjack/Core (3.8.0) - - CocoaLumberjack/Swift (3.8.0): + - CocoaLumberjack/Core (3.8.1) + - CocoaLumberjack/Swift (3.8.1): - CocoaLumberjack/Core - FlexibleDiff (0.0.9) - GzipSwift (5.1.1) @@ -57,9 +57,9 @@ SPEC REPOS: - WebViewJavascriptBridge SPEC CHECKSUMS: - Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88 + Alamofire: 0e92e751b3e9e66d7982db43919d01f313b8eb91 AppCenter: a4070ec3d4418b5539067a51f57155012e486ebd - CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 + CocoaLumberjack: 5c7e64cdb877770859bddec4d3d5a0d7c9299df9 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa LetsMove: 7b9fe44737707d984fbd3f47af46609a9a07b461 From 6cf0b5e4249f02e3abc4659169c72399b6005bab Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:47:14 +0800 Subject: [PATCH 099/122] ci: update go version --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index de27237e0..a5380bc06 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: - name: setup Go uses: actions/setup-go@v3 with: - go-version: 1.20.x + go-version: 1.21.x - uses: maxim-lobanov/setup-xcode@v1 with: @@ -30,7 +30,7 @@ jobs: - name: install deps run: | bash install_dependency.sh - + - name: update dev build version if: ${{!startsWith(github.ref, 'refs/tags/')}} run: | @@ -38,7 +38,7 @@ jobs: bundle exec fastlane run increment_build_number_in_plist build_number:"${tag}" scheme:"ClashX" bundle exec fastlane run increment_version_number_in_plist version_number:"${tag}" scheme:"ClashX" bundle exec fastlane run set_info_plist_value path:ClashX/Info.plist key:BETA value:true - + - name: update tag build version if: startsWith(github.ref, 'refs/tags/') run: | @@ -72,7 +72,7 @@ jobs: create-dmg ClashX.app mv ClashX*.dmg ClashX.dmg - - name: notarize + - name: notarize if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[beta]') env: FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} @@ -81,7 +81,7 @@ jobs: run: | bundle exec fastlane run notarize package:"./ClashX.dmg" bundle_id:"com.west2online.ClashX" asc_provider:MEWHFZ92DY - - name: upload to appcenter + - name: upload to appcenter if: startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[beta]') env: APPCENTER_DISTRIBUTE_UPLOAD_BUILD_ONLY: true From 1d2abc3bb13decc68ee49ea0baab9b3dfcc373ff Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:34:55 +0800 Subject: [PATCH 100/122] feat: add update external resources action --- ClashX.xcodeproj/project.pbxproj | 16 +++++ ClashX/Actions/BaseAction.swift | 11 +++ .../UpdateExternalResourceAction.swift | 55 ++++++++++++++ ClashX/AppDelegate.swift | 4 ++ ClashX/Base.lproj/Main.storyboard | 10 ++- ClashX/General/ApiRequest.swift | 72 +++++++++++++++++++ .../en.lproj/Localizable.strings | 9 +++ .../zh-Hans.lproj/Localizable.strings | 9 +++ .../zh-Hant.lproj/Localizable.strings | 9 +++ ClashX/zh-Hans.lproj/Main.strings | 11 +-- ClashX/zh-Hant.lproj/Main.strings | 11 +-- 11 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 ClashX/Actions/BaseAction.swift create mode 100644 ClashX/Actions/UpdateExternalResourceAction.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index b63d24548..8335ba233 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ 495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495A44D220D267D000888A0A /* LaunchAtLogin.swift */; }; 495BFB8821919B9800C8779D /* RemoteConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495BFB8721919B9800C8779D /* RemoteConfigManager.swift */; }; 4960A6DB2136529200B940C9 /* JSBridgeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */; }; + 496322222AA5D89E00854231 /* UpdateExternalResourceAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */; }; + 496322242AA5D91200854231 /* BaseAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496322232AA5D91200854231 /* BaseAction.swift */; }; 4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */; }; 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E5211824F300A391FB /* NSImage+extension.swift */; }; 4969E73D2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4969E73F2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib */; }; @@ -190,6 +192,8 @@ 495A44D220D267D000888A0A /* LaunchAtLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLogin.swift; sourceTree = ""; }; 495BFB8721919B9800C8779D /* RemoteConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigManager.swift; sourceTree = ""; }; 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSBridgeHandler.swift; sourceTree = ""; }; + 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateExternalResourceAction.swift; sourceTree = ""; }; + 496322232AA5D91200854231 /* BaseAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAction.swift; sourceTree = ""; }; 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserNotificationCenter+Extension.swift"; sourceTree = ""; }; 4966E9E5211824F300A391FB /* NSImage+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+extension.swift"; sourceTree = ""; }; 4969E73E2A5E3CB20012E005 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ConnectionDetailInfoGeneralView.xib; sourceTree = ""; }; @@ -426,6 +430,15 @@ path = Views; sourceTree = ""; }; + 496322202AA5D88100854231 /* Actions */ = { + isa = PBXGroup; + children = ( + 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */, + 496322232AA5D91200854231 /* BaseAction.swift */, + ); + path = Actions; + sourceTree = ""; + }; 49722FDD211ED2A900650A41 /* Vendor */ = { isa = PBXGroup; children = ( @@ -593,6 +606,7 @@ 491C24FF21BD558B00AB5D44 /* Extensions */, 492C4866210EE69B004554A0 /* General */, 4931969C21631F2E00A8E6E7 /* Views */, + 496322202AA5D88100854231 /* Actions */, 4989F98520D0AA300001E564 /* ViewControllers */, 49761DA521C9490400AE13EF /* Resources */, 49CF3B3A20CD783A001EBF94 /* Support Files */, @@ -882,6 +896,7 @@ 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */, 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */, 49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */, + 496322242AA5D91200854231 /* BaseAction.swift in Sources */, 4952C3D02117027C004A4FA8 /* ConfigFileManager.swift in Sources */, 49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */, 4949D154213242F600EF85E6 /* Paths.swift in Sources */, @@ -966,6 +981,7 @@ 49769FB427E9B3E400E3D664 /* LoginKitWrapper.m in Sources */, 4960A6DB2136529200B940C9 /* JSBridgeHandler.swift in Sources */, 493AEAE5221AE7230016FE98 /* ProxyMenuItem.swift in Sources */, + 496322222AA5D89E00854231 /* UpdateExternalResourceAction.swift in Sources */, 499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */, F939724C23A4B33500FE5A3F /* ClashProvider.swift in Sources */, 0AEF6EAF2A5F961B00EFEE23 /* SectionedTableView.swift in Sources */, diff --git a/ClashX/Actions/BaseAction.swift b/ClashX/Actions/BaseAction.swift new file mode 100644 index 000000000..50274a1da --- /dev/null +++ b/ClashX/Actions/BaseAction.swift @@ -0,0 +1,11 @@ +// +// BaseAction.swift +// ClashX +// +// Created by yicheng on 2023/9/4. +// Copyright © 2023 west2online. All rights reserved. +// + +protocol BaseStaticAction { + static func run() +} diff --git a/ClashX/Actions/UpdateExternalResourceAction.swift b/ClashX/Actions/UpdateExternalResourceAction.swift new file mode 100644 index 000000000..3db87866c --- /dev/null +++ b/ClashX/Actions/UpdateExternalResourceAction.swift @@ -0,0 +1,55 @@ +// +// UpdateExternalResourceAction.swift +// ClashX +// +// Created by yicheng on 2023/9/4. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation +enum UpdateExternalResourceAction:BaseStaticAction { + static func run() { + ApiRequest.requestExternalProviderNames { provider in + let group = DispatchGroup() + var successCount = 0 + let totalCount = provider.proxies.count + provider.rules.count + if totalCount == 0 { + self.onFinished(success: 0, total: 0, fails: []) + return + } + var fails = [String]() + for name in provider.proxies { + group.enter() + ApiRequest.updateProvider(name: name, type: .proxy) { success in + if success { successCount += 1 } else { + fails.append(name) + } + group.leave() + } + } + + for name in provider.rules { + group.enter() + ApiRequest.updateProvider(name: name, type: .rule) { success in + if success { successCount += 1 } else { + fails.append(name) + } + group.leave() + } + } + + group.notify(queue: .main) { + self.onFinished(success: successCount, total: totalCount, fails: fails) + } + + } + } + + private static func onFinished(success:Int, total: Int, fails: [String]) { + var info = String(format: NSLocalizedString("total: %d, success: %d", comment: ""), total, success) + if !fails.isEmpty { + info.append(String(format: NSLocalizedString("fails: %@", comment: ""), fails.joined(separator: " "))) + } + NSUserNotificationCenter.default.post(title: NSLocalizedString("Update external resource complete", comment: ""), info: info) + } +} diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index e791aea4f..19a666c25 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -754,6 +754,10 @@ extension AppDelegate { } } + @IBAction func actionUpdateExternalResource(_ sender: Any) { + UpdateExternalResourceAction.run() + } + @IBAction func actionQuit(_ sender: Any) { NSApplication.shared.terminate(self) } diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index e000a845f..2c6bcb57b 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -799,6 +799,12 @@ + + + + + + @@ -828,9 +834,9 @@ - + - + diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index acdf86307..2f8ca1aa7 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -321,6 +321,78 @@ extension ApiRequest { req("/connections", method: .delete).response { _ in } } } + + // MARK: - Providers + + struct AllProviders { + var proxies = [String]() + var rules = [String]() + } + + static func requestExternalProviderNames(completeHandler: @escaping (AllProviders) -> Void) { + var providers = AllProviders() + let group = DispatchGroup() + group.enter() + ApiRequest.req("/providers/proxies").responseData { resp in + switch resp.result { + case let .success(res): + let json = JSON(res) + let provoders = json["providers"].dictionaryValue + .filter({ $0.value["vehicleType"] == "HTTP" }).map { $0.key } + providers.proxies = provoders + case let .failure(err): + Logger.log(err.localizedDescription, level: .warning) + } + group.leave() + } + + #if PRO_VERSION + group.enter() + ApiRequest.req("/providers/rules").responseData { resp in + switch resp.result { + case let .success(res): + let json = JSON(res) + let provoders = json["providers"].dictionaryValue + .filter({ $0.value["vehicleType"] == "HTTP" }).map { $0.key } + providers.rules = provoders + case let .failure(err): + Logger.log(err.localizedDescription, level: .warning) + } + group.leave() + } + #endif + group.notify(queue: .main) { + completeHandler(providers) + } + } + + enum ProviderType { + case proxy + case rule + } + + static func updateProvider(name: String, type: ProviderType, completeHandler: @escaping (Bool) -> Void) { + let url: String + switch type { + case .proxy: + url = "/providers/proxies/\(name.encoded)" + case .rule: + url = "/providers/rules/\(name.encoded)" + } + ApiRequest.req(url, method: .put).response { resp in + if resp.response?.statusCode == 204 { + completeHandler(true) + } else { + completeHandler(false) + } + } + } + + static func resetFakeIpCache() { + ApiRequest.req("/cache/fakeip/flush", method: .post).response { resp in + Logger.log("flush fake ip: \(resp.response?.statusCode ?? -1)") + } + } } // MARK: - Stream Apis diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 24903e835..78a7a426e 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -103,6 +103,9 @@ /* No comment provided by engineer. */ "Fail:" = "Fail:"; +/* No comment provided by engineer. */ +"fails: %@" = "fails: %@"; + /* No comment provided by engineer. */ "Global" = "Global"; @@ -259,12 +262,18 @@ /* No comment provided by engineer. */ "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly." = "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly."; +/* No comment provided by engineer. */ +"total: %d, success: %d" = "total: %d, success: %d"; + /* No comment provided by engineer. */ "Type" = "Type"; /* No comment provided by engineer. */ "Unknown" = "Unknown"; +/* No comment provided by engineer. */ +"Update external resource complete" = "Update external resource complete"; + /* No comment provided by engineer. */ "Update GEOIP Database" = "Update GEOIP Database"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index b4d375bba..576120ee5 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -103,6 +103,9 @@ /* No comment provided by engineer. */ "Fail:" = "失败:"; +/* No comment provided by engineer. */ +"fails: %@" = "失败: %@"; + /* No comment provided by engineer. */ "Global" = "全局"; @@ -259,12 +262,18 @@ /* No comment provided by engineer. */ "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly." = "由于Clash Core发布1.0版本,使用此版本的 ClashX 可能需要更新配置内容\n前往 https://github.com/Dreamacro/clash/wiki/breaking-changes-in-1.0.0 查看详情"; +/* No comment provided by engineer. */ +"total: %d, success: %d" = "共计: %d, 成功: %d"; + /* No comment provided by engineer. */ "Type" = "类型"; /* No comment provided by engineer. */ "Unknown" = "未知"; +/* No comment provided by engineer. */ +"Update external resource complete" = "更新外部资源完成"; + /* No comment provided by engineer. */ "Update GEOIP Database" = "更新IP数据库"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 2ac59cf83..53ccdb1d5 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -103,6 +103,9 @@ /* No comment provided by engineer. */ "Fail:" = "失敗:"; +/* No comment provided by engineer. */ +"fails: %@" = "失敗: %@"; + /* No comment provided by engineer. */ "Global" = "全局"; @@ -259,12 +262,18 @@ /* No comment provided by engineer. */ "This version of ClashX contains a break change due to clash core 1.0 released. Check if your config is not working properly." = "此版本的 ClashX 包含因 clash core 1.0 發布而導致的中斷更改。請檢查您的配置是否無法正常工作"; +/* No comment provided by engineer. */ +"total: %d, success: %d" = "共計: %d, 成功: %d"; + /* No comment provided by engineer. */ "Type" = "類型"; /* No comment provided by engineer. */ "Unknown" = "未知"; +/* No comment provided by engineer. */ +"Update external resource complete" = "更新外部資源完成"; + /* No comment provided by engineer. */ "Update GEOIP Database" = "更新 GEOIP 數據庫"; diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 43bb2c6a3..5022fb6f2 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -73,7 +73,7 @@ /* Class = "NSMenuItem"; title = "Copy shell command"; ObjectID = "Jmb-PK-rMW"; */ "Jmb-PK-rMW.title" = "复制终端代理命令"; -/* Class = "NSMenuItem"; title = "Config"; ObjectID = "JMV-Dy-CI0"; */ +/* Class = "NSMenuItem"; title = "Configs"; ObjectID = "JMV-Dy-CI0"; */ "JMV-Dy-CI0.title" = "配置"; /* Class = "NSMenuItem"; title = "WARNING"; ObjectID = "ko2-Ir-DxA"; */ @@ -103,7 +103,7 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RCv-zz-HKW"; */ "RCv-zz-HKW.title" = "Text Cell"; -/* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ +/* Class = "NSMenu"; title = "Configs"; ObjectID = "tck-zU-JKQ"; */ "tck-zU-JKQ.title" = "配置"; /* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ @@ -163,10 +163,10 @@ /* Class = "NSTableColumn"; headerCell.title = "Api Url"; ObjectID = "yO6-uZ-IRv"; */ "yO6-uZ-IRv.headerCell.title" = "Api Url"; -/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ +/* Class = "NSMenu"; title = "Remote controller"; ObjectID = "1He-Eq-fSy"; */ "1He-Eq-fSy.title" = "远程控制器"; -/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ +/* Class = "NSMenuItem"; title = "Remote controller"; ObjectID = "BRR-WK-aeP"; */ "BRR-WK-aeP.title" = "远程控制器"; /* Class = "NSMenuItem"; title = " Manage"; ObjectID = "hlb-KQ-Fdr"; */ @@ -309,3 +309,6 @@ /* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ "pHl-C4-fNt.title" = "更新"; + +/* Class = "NSMenuItem"; title = "Update external resources"; ObjectID = "9g1-lW-mA8"; */ +"9g1-lW-mA8.title" = "更新外部资源"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index 6dd7da7f1..964a4295a 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -1,4 +1,4 @@ -/* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ +/* Class = "NSMenu"; title = "Configs"; ObjectID = "tck-zU-JKQ"; */ "tck-zU-JKQ.title" = "配置"; /* Class = "NSTextFieldCell"; title = "Configs"; ObjectID = "tL1-bl-LXd"; */ @@ -91,7 +91,7 @@ /* Class = "NSMenuItem"; title = "Copy shell command"; ObjectID = "Jmb-PK-rMW"; */ "Jmb-PK-rMW.title" = "複製終端代理命令"; -/* Class = "NSMenuItem"; title = "Config"; ObjectID = "JMV-Dy-CI0"; */ +/* Class = "NSMenuItem"; title = "Configs"; ObjectID = "JMV-Dy-CI0"; */ "JMV-Dy-CI0.title" = "配置"; /* Class = "NSMenuItem"; title = "WARNING"; ObjectID = "ko2-Ir-DxA"; */ @@ -121,7 +121,7 @@ /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "RCv-zz-HKW"; */ "RCv-zz-HKW.title" = "Text Cell"; -/* Class = "NSMenu"; title = "Remote Controller"; ObjectID = "1He-Eq-fSy"; */ +/* Class = "NSMenu"; title = "Remote controller"; ObjectID = "1He-Eq-fSy"; */ "1He-Eq-fSy.title" = "遙控"; /* Class = "NSBox"; title = "Mode"; ObjectID = "1d5-NL-UsJ"; */ @@ -151,7 +151,7 @@ /* Class = "NSBox"; title = "Proxy"; ObjectID = "BFE-Qq-B2H"; */ "BFE-Qq-B2H.title" = "代理"; -/* Class = "NSMenuItem"; title = "Remote Controller"; ObjectID = "BRR-WK-aeP"; */ +/* Class = "NSMenuItem"; title = "Remote controller"; ObjectID = "BRR-WK-aeP"; */ "BRR-WK-aeP.title" = "遙控"; /* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ @@ -309,3 +309,6 @@ /* Class = "NSButtonCell"; title = "Update"; ObjectID = "pHl-C4-fNt"; */ "pHl-C4-fNt.title" = "更新"; + +/* Class = "NSMenuItem"; title = "Update external resources"; ObjectID = "9g1-lW-mA8"; */ +"9g1-lW-mA8.title" = "更新外部資源"; From a8e1df6cf4f8a6f69e4e93ecabc7f20c48e7cb7e Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:29:14 +0800 Subject: [PATCH 101/122] misc: update setting icon config on os below 10.11 --- .../ViewControllers/Settings/SettingTabViewController.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ClashX/ViewControllers/Settings/SettingTabViewController.swift b/ClashX/ViewControllers/Settings/SettingTabViewController.swift index 1128bdce3..21b428631 100644 --- a/ClashX/ViewControllers/Settings/SettingTabViewController.swift +++ b/ClashX/ViewControllers/Settings/SettingTabViewController.swift @@ -13,6 +13,12 @@ class SettingTabViewController: NSTabViewController { override func viewDidLoad() { super.viewDidLoad() tabStyle = .toolbar + if #unavailable(macOS 10.11) { + tabStyle = .segmentedControlOnTop + tabViewItems.forEach { item in + item.image = nil + } + } NSApp.activate(ignoringOtherApps: true) } From 2dc0f0d4b569c346d0edab9ca882f86d8f337f05 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:30:03 +0800 Subject: [PATCH 102/122] misc: remove swiftui --- ClashX.xcodeproj/project.pbxproj | 8 -- ClashX/AppDelegate.swift | 6 +- ClashX/Base.lproj/Main.storyboard | 76 ++++++++--------- ClashX/Basic/NSImage+extension.swift | 27 ------- .../Settings/DebugSettingViewController.swift | 5 -- .../Views/StatusItem/NewStatusItemView.swift | 81 ------------------- ClashX/Views/StatusItem/StatusItemTool.swift | 8 +- ClashX/Views/StatusItem/StatusItemView.swift | 57 ++++++------- ClashX/Views/StatusItem/StatusItemView.xib | 4 +- 9 files changed, 62 insertions(+), 210 deletions(-) delete mode 100644 ClashX/Basic/NSImage+extension.swift delete mode 100644 ClashX/Views/StatusItem/NewStatusItemView.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 8335ba233..5a6716d90 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -38,7 +38,6 @@ 496322222AA5D89E00854231 /* UpdateExternalResourceAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */; }; 496322242AA5D91200854231 /* BaseAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496322232AA5D91200854231 /* BaseAction.swift */; }; 4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */; }; - 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E5211824F300A391FB /* NSImage+extension.swift */; }; 4969E73D2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4969E73F2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib */; }; 496BDEE021196F1E00C5207F /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496BDEDF21196F1E00C5207F /* Logger.swift */; }; 49722FEF211F338B00650A41 /* FileEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49722FEA211F338B00650A41 /* FileEvent.swift */; }; @@ -96,7 +95,6 @@ 49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */; }; 49D223392A1DA5F10002FFCB /* SSIDSuspendTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */; }; 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45129AEEC15006487EF /* StatusItemTool.swift */; }; - 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */; }; 49D6A45629AEEC55006487EF /* StatusItemViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */; }; 49D767742A6195C800830333 /* ConnectionsReq.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D767732A6195C800830333 /* ConnectionsReq.swift */; }; 49D767762A6195E600830333 /* StructedLogReq.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D767752A6195E600830333 /* StructedLogReq.swift */; }; @@ -195,7 +193,6 @@ 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateExternalResourceAction.swift; sourceTree = ""; }; 496322232AA5D91200854231 /* BaseAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAction.swift; sourceTree = ""; }; 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserNotificationCenter+Extension.swift"; sourceTree = ""; }; - 4966E9E5211824F300A391FB /* NSImage+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+extension.swift"; sourceTree = ""; }; 4969E73E2A5E3CB20012E005 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ConnectionDetailInfoGeneralView.xib; sourceTree = ""; }; 4969E7412A5E3CB80012E005 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ConnectionDetailInfoGeneralView.strings"; sourceTree = ""; }; 4969E7432A5E3CB90012E005 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/ConnectionDetailInfoGeneralView.strings"; sourceTree = ""; }; @@ -264,7 +261,6 @@ 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupMenuItemView.swift; sourceTree = ""; }; 49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSIDSuspendTool.swift; sourceTree = ""; }; 49D6A45129AEEC15006487EF /* StatusItemTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemTool.swift; sourceTree = ""; }; - 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusItemView.swift; sourceTree = ""; }; 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemViewProtocol.swift; sourceTree = ""; }; 49D767732A6195C800830333 /* ConnectionsReq.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsReq.swift; sourceTree = ""; }; 49D767752A6195E600830333 /* StructedLogReq.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StructedLogReq.swift; sourceTree = ""; }; @@ -513,7 +509,6 @@ isa = PBXGroup; children = ( 495A44D220D267D000888A0A /* LaunchAtLogin.swift */, - 4966E9E5211824F300A391FB /* NSImage+extension.swift */, 496BDEDF21196F1E00C5207F /* Logger.swift */, 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */, 49B10869216A356D0064FFCE /* String+Extension.swift */, @@ -530,7 +525,6 @@ 495340AF20DE5F7200B0D3FF /* StatusItemView.xib */, 495340B220DE68C300B0D3FF /* StatusItemView.swift */, 49D6A45129AEEC15006487EF /* StatusItemTool.swift */, - 49D6A45329AEEC3C006487EF /* NewStatusItemView.swift */, 49D6A45529AEEC55006487EF /* StatusItemViewProtocol.swift */, ); path = StatusItem; @@ -951,7 +945,6 @@ F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */, 4991D22A2A56472300978143 /* ConnectionCellProtocol.swift in Sources */, 4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */, - 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */, 49D767742A6195C800830333 /* ConnectionsReq.swift in Sources */, 49C9AFF22A59366200178BB4 /* ConnectionDetailInfoGeneralView.swift in Sources */, F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */, @@ -970,7 +963,6 @@ 49CCDA322A5506D100FF1E13 /* ConnectionTopListView.swift in Sources */, 49D176A9235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift in Sources */, F976275C23634DF8000EDEFE /* LoginServiceKit.swift in Sources */, - 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */, 4994B5542A47C4FF00E595B9 /* NormalMenuItemView.swift in Sources */, 0AD7506B2A5B9A04001FFBBD /* ConnectionLeftPannelViewModel.swift in Sources */, F92D0B2E236D35C000575E15 /* ProxyItemView.swift in Sources */, diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 19a666c25..36d0b98eb 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -73,11 +73,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { ProcessInfo.processInfo.disableSuddenTermination() // setup menu item first statusItem = NSStatusBar.system.statusItem(withLength: statusItemLengthWithSpeed) - if #available(macOS 13, *), Settings.useSwiftUiMenuBar, let button = statusItem.button { - statusItemView = NewStatusMenuView.create(on: button) - } else { - statusItemView = StatusItemView.create(statusItem: statusItem) - } + statusItemView = StatusItemView.create(statusItem: statusItem) statusItemView.updateSize(width: statusItemLengthWithSpeed) statusMenu.delegate = self DispatchQueue.main.async { diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 2c6bcb57b..297e149b0 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1617,28 +1617,21 @@ - - + + - + - + - + - - - + + - - + + - @@ -237,11 +244,13 @@ + + @@ -278,15 +287,15 @@ - + - + - + @@ -300,7 +309,7 @@ - + @@ -407,15 +416,15 @@ - + - + - + @@ -429,7 +438,7 @@ - + @@ -498,6 +507,7 @@ + diff --git a/ClashX/General/Managers/Settings.swift b/ClashX/General/Managers/Settings.swift index 37874a2f0..a68a2e8df 100644 --- a/ClashX/General/Managers/Settings.swift +++ b/ClashX/General/Managers/Settings.swift @@ -48,8 +48,8 @@ enum Settings { @UserDefault("disableSSIDList", defaultValue: []) static var disableSSIDList: [String] - @UserDefault("useSwiftUiMenuBar", defaultValue: true) - static var useSwiftUiMenuBar: Bool + @UserDefault("enableIPV6", defaultValue: false) + static var enableIPV6: Bool static let apiSecretKey = "api-secret" diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 78a7a426e..0b0236981 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -220,6 +220,9 @@ /* No comment provided by engineer. */ "Rule Mode" = "Rule Mode"; +/* No comment provided by engineer. */ +"Script" = "Script"; + /* No comment provided by engineer. */ "Should be a least 1 hour" = "Should be a least 1 hour"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 576120ee5..297d17e35 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -220,6 +220,9 @@ /* No comment provided by engineer. */ "Rule Mode" = "规则判断"; +/* No comment provided by engineer. */ +"Script" = "脚本"; + /* No comment provided by engineer. */ "Should be a least 1 hour" = "至少需要1小时间隔"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 53ccdb1d5..deb180d39 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -220,6 +220,9 @@ /* No comment provided by engineer. */ "Rule Mode" = "規則模式"; +/* No comment provided by engineer. */ +"Script" = "腳本"; + /* No comment provided by engineer. */ "Should be a least 1 hour" = "應該至少 1 小時"; diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index 5434f38b8..ef44eef9a 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -25,6 +25,7 @@ class GeneralSettingViewController: NSViewController { @IBOutlet var apiSecretOverrideButton: NSButton! + @IBOutlet var ipv6Button: NSButton! @IBOutlet var speedTestUrlField: NSTextField! var disposeBag = DisposeBag() @@ -70,6 +71,11 @@ class GeneralSettingViewController: NSViewController { Settings.disableNoti = $0 }.disposed(by: disposeBag) + ipv6Button.state = Settings.enableIPV6 ? .on : .off + ipv6Button.rx.state.map { $0 == .on }.subscribe { + Settings.enableIPV6 = $0 + }.disposed(by: disposeBag) + if Settings.proxyPort > 0 { proxyPortTextField.stringValue = "\(Settings.proxyPort)" } else { diff --git a/ClashX/goClash/main.go b/ClashX/goClash/main.go index 4c68b815c..63473f7fb 100644 --- a/ClashX/goClash/main.go +++ b/ClashX/goClash/main.go @@ -33,6 +33,7 @@ import ( ) var secretOverride string = "" +var enableIPV6 bool = false func isAddrValid(addr string) bool { if addr != "" { @@ -99,7 +100,7 @@ func getRawCfg() (*config.RawConfig, error) { return config.UnmarshalRawConfig(buf) } -func parseDefaultConfigThenStart(checkPort, allowLan bool, proxyPort uint32, externalController string) (*config.Config, error) { +func parseDefaultConfigThenStart(checkPort, allowLan, ipv6 bool, proxyPort uint32, externalController string) (*config.Config, error) { rawCfg, err := getRawCfg() if err != nil { return nil, err @@ -139,6 +140,8 @@ func parseDefaultConfigThenStart(checkPort, allowLan bool, proxyPort uint32, ext } rawCfg.ExternalUI = "" rawCfg.Profile.StoreSelected = false + enableIPV6 = ipv6 + rawCfg.IPv6 = ipv6 if len(externalController) > 0 { rawCfg.ExternalController = externalController } @@ -232,8 +235,8 @@ func clash_setSecret(secret *C.char) { } //export run -func run(checkConfig, allowLan bool, portOverride uint32, externalController *C.char) *C.char { - cfg, err := parseDefaultConfigThenStart(checkConfig, allowLan, portOverride, C.GoString(externalController)) +func run(checkConfig, allowLan, ipv6 bool, portOverride uint32, externalController *C.char) *C.char { + cfg, err := parseDefaultConfigThenStart(checkConfig, allowLan, ipv6, portOverride, C.GoString(externalController)) if err != nil { return C.CString(err.Error()) } @@ -262,6 +265,7 @@ func clashUpdateConfig(path *C.char) *C.char { if err != nil { return C.CString(err.Error()) } + cfg.General.IPv6 = enableIPV6 executor.ApplyConfig(cfg, false) return C.CString("success") } diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 5022fb6f2..0e4595e1a 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -208,8 +208,8 @@ /* Class = "NSButtonCell"; title = "Allow control from lan"; ObjectID = "E8B-e5-K0A"; */ "E8B-e5-K0A.title" = "允许局域网控制(不推荐)"; -/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ -"bcR-rG-52F.title" = "端口设置(重启应用生效)"; +/* Class = "NSBox"; title = "Clash Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "Clash设置(重启应用生效)"; /* Class = "NSTextFieldCell"; title = "Api Port:"; ObjectID = "mbn-tK-UQa"; */ "mbn-tK-UQa.title" = "Api 端口"; @@ -235,9 +235,6 @@ /* Class = "NSBox"; title = "Debug Setting"; ObjectID = "NLT-FZ-48V"; */ "NLT-FZ-48V.title" = "调试设置"; -/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ -"eY9-1i-i7P.title" = "使用 SwiftUI 进行菜单栏图标渲染 (MacOS 13+)"; - /* Class = "NSViewController"; title = "Debug"; ObjectID = "kdV-Em-qBi"; */ "kdV-Em-qBi.title" = "调试"; @@ -312,3 +309,6 @@ /* Class = "NSMenuItem"; title = "Update external resources"; ObjectID = "9g1-lW-mA8"; */ "9g1-lW-mA8.title" = "更新外部资源"; + +/* Class = "NSButtonCell"; title = "Enable IPv6"; ObjectID = "KRm-2U-T4s"; */ +"KRm-2U-T4s.title" = "启用IPv6"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index 964a4295a..af6815ed6 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -196,8 +196,8 @@ /* Class = "NSButtonCell"; title = "Open Log Folder"; ObjectID = "afj-4G-usr"; */ "afj-4G-usr.title" = "打開日誌文件夾"; -/* Class = "NSBox"; title = "Port Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ -"bcR-rG-52F.title" = "端口設定"; +/* Class = "NSBox"; title = "Clash Settings (restart app to take effect)"; ObjectID = "bcR-rG-52F"; */ +"bcR-rG-52F.title" = "Clash設定"; /* Class = "NSTextFieldCell"; title = "Log"; ObjectID = "c01-0L-1SQ"; */ "c01-0L-1SQ.title" = "日誌"; @@ -205,9 +205,6 @@ /* Class = "NSButtonCell"; title = "Launch at login"; ObjectID = "dV6-4Z-2SO"; */ "dV6-4Z-2SO.title" = "登錄時啟動"; -/* Class = "NSButtonCell"; title = "Use SwiftUI to render status bar icon. (MacOS 13+)"; ObjectID = "eY9-1i-i7P"; */ -"eY9-1i-i7P.title" = "使用 SwiftUI 渲染狀態欄圖標"; - /* Class = "NSMenuItem"; title = "Set update interval"; ObjectID = "h1H-7k-9HS"; */ "h1H-7k-9HS.title" = "設定更新間隔"; @@ -312,3 +309,6 @@ /* Class = "NSMenuItem"; title = "Update external resources"; ObjectID = "9g1-lW-mA8"; */ "9g1-lW-mA8.title" = "更新外部資源"; + +/* Class = "NSButtonCell"; title = "Enable IPv6"; ObjectID = "KRm-2U-T4s"; */ +"KRm-2U-T4s.title" = "啟用IPv6"; From 73362acd75b60d0074cc9aac0c43067e6b09a749 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 5 Sep 2023 18:53:18 +0800 Subject: [PATCH 111/122] fix status menu display color issue --- ClashX/AppDelegate.swift | 22 ++++++++++---------- ClashX/Views/StatusItem/StatusItemView.swift | 9 ++++---- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 7e2ff9691..e51d8b99a 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -77,6 +77,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { statusItemView = StatusItemView.create(statusItem: statusItem) statusItemView.updateSize(width: statusItemLengthWithSpeed) statusMenu.delegate = self + setupStatusMenuItemData() DispatchQueue.main.async { self.postFinishLaunching() } @@ -95,7 +96,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.dashboardMenuItem.isHidden = true self.connectionsMenuItem.isHidden = true } - setupStatusMenuItemData() AppVersionUtil.showUpgradeAlert() ICloudManager.shared.setup() @@ -264,16 +264,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { }.disposed(by: disposeBag) statusItemView.updateViewStatus(enableProxy: ConfigManager.shared.proxyPortAutoSet) - - LaunchAtLogin.shared - .isEnableVirable - .asObservable() - .subscribe(onNext: { [weak self] enable in - guard let self = self else { return } - self.autoStartMenuItem.state = enable ? .on : .off - }).disposed(by: disposeBag) - - remoteConfigAutoupdateMenuItem.state = RemoteConfigManager.autoUpdateEnable ? .on : .off } func setupData() { @@ -355,6 +345,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { SystemProxyManager.shared.enableProxy() } + LaunchAtLogin.shared + .isEnableVirable + .asObservable() + .subscribe(onNext: { [weak self] enable in + guard let self = self else { return } + self.autoStartMenuItem.state = enable ? .on : .off + }).disposed(by: disposeBag) + + remoteConfigAutoupdateMenuItem.state = RemoteConfigManager.autoUpdateEnable ? .on : .off + if !PrivilegedHelperManager.shared.isHelperCheckFinished.value { proxySettingMenuItem.target = nil PrivilegedHelperManager.shared.isHelperCheckFinished diff --git a/ClashX/Views/StatusItem/StatusItemView.swift b/ClashX/Views/StatusItem/StatusItemView.swift index 9cd770e97..a3bef1452 100644 --- a/ClashX/Views/StatusItem/StatusItemView.swift +++ b/ClashX/Views/StatusItem/StatusItemView.swift @@ -45,8 +45,8 @@ class StatusItemView: NSView, StatusItemViewProtocol { uploadSpeedLabel.font = StatusItemTool.font downloadSpeedLabel.font = StatusItemTool.font - uploadSpeedLabel.textColor = NSColor.black - downloadSpeedLabel.textColor = NSColor.black + uploadSpeedLabel.textColor = NSColor.labelColor + downloadSpeedLabel.textColor = NSColor.labelColor } func updateSize(width: CGFloat) { @@ -55,10 +55,9 @@ class StatusItemView: NSView, StatusItemViewProtocol { func updateViewStatus(enableProxy: Bool) { if enableProxy { - imageView.contentTintColor = NSColor.black + imageView.contentTintColor = NSColor.labelColor } else { - // withSystemEffect(.disabled) is 0.5 - imageView.contentTintColor = NSColor.black.withAlphaComponent(0.25) + imageView.contentTintColor = NSColor.labelColor.withSystemEffect(.disabled) } } From 6e40d06df3bb0b68b9606e09d288537e5e3047a2 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:25:57 +0800 Subject: [PATCH 112/122] fix: setting shortcut page layout conflict --- ClashX/Base.lproj/Main.storyboard | 69 +++++++++++-------- .../GlobalShortCutViewController.swift | 30 ++++---- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 497593bb1..1e84c57bf 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -1632,16 +1632,16 @@ - + - + - + - - + + - - + + - + + + @@ -1691,10 +1694,10 @@ - + - + @@ -1702,7 +1705,7 @@ - + + + @@ -1722,10 +1728,10 @@ - + - + @@ -1733,7 +1739,7 @@ - + + + @@ -1753,7 +1762,7 @@ - + @@ -1785,6 +1794,9 @@ + + + @@ -1796,10 +1808,10 @@ - + - + @@ -1807,7 +1819,7 @@ - + + + @@ -1827,10 +1842,10 @@ - + - + @@ -1838,7 +1853,7 @@ - + @@ -1847,6 +1862,9 @@ + + + @@ -1857,9 +1875,6 @@ - - - diff --git a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift index 1285e4c8d..f630b148f 100644 --- a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift +++ b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift @@ -76,10 +76,9 @@ class GlobalShortCutViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() - - let systemProxy = KeyboardShortcuts.RecorderCocoa(for: .toggleSystemProxyMode) - let copyShellCommand = KeyboardShortcuts.RecorderCocoa(for: .copyShellCommand) - let copyShellCommandExternal = KeyboardShortcuts.RecorderCocoa(for: .copyExternalShellCommand) + let systemProxy = getRecoder(for: .toggleSystemProxyMode) + let copyShellCommand = getRecoder(for: .copyShellCommand) + let copyShellCommandExternal = getRecoder(for: .copyExternalShellCommand) addGridView(in: proxyBox.contentView!, with: [ [NSTextField(labelWithString: NSLocalizedString("System Proxy", comment: "")), systemProxy], [NSTextField(labelWithString: NSLocalizedString("Copy Shell Command", comment: "")), copyShellCommand], @@ -87,26 +86,31 @@ class GlobalShortCutViewController: NSViewController { ]) addGridView(in: modeBoxView, with: [ - [NSTextField(labelWithString: NSLocalizedString("Direct Mode", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .modeDirect)], - [NSTextField(labelWithString: NSLocalizedString("Rule Mode", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .modeRule)], - [NSTextField(labelWithString: NSLocalizedString("Global Mode", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .modeGlobal)] + [NSTextField(labelWithString: NSLocalizedString("Direct Mode", comment: "")), getRecoder(for: .modeDirect)], + [NSTextField(labelWithString: NSLocalizedString("Rule Mode", comment: "")), getRecoder(for: .modeRule)], + [NSTextField(labelWithString: NSLocalizedString("Global Mode", comment: "")), getRecoder(for: .modeGlobal)] ]) var otherItems: [[NSView]] = [ - [NSTextField(labelWithString: NSLocalizedString("Open Menu", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .openMenu)], - [NSTextField(labelWithString: NSLocalizedString("Open Log", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .log)], - [NSTextField(labelWithString: NSLocalizedString("Open Dashboard", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .dashboard)] + [NSTextField(labelWithString: NSLocalizedString("Open Menu", comment: "")), getRecoder(for: .openMenu)], + [NSTextField(labelWithString: NSLocalizedString("Open Log", comment: "")), getRecoder(for: .log)], + [NSTextField(labelWithString: NSLocalizedString("Open Dashboard", comment: "")), getRecoder(for: .dashboard)] ] if #available(macOS 10.15, *) { - otherItems.append([NSTextField(labelWithString: NSLocalizedString("Open Connection Details", comment: "")), KeyboardShortcuts.RecorderCocoa(for: .nativeDashboard)]) + otherItems.append([NSTextField(labelWithString: NSLocalizedString("Open Connection Details", comment: "")), getRecoder(for: .nativeDashboard)]) } addGridView(in: otherBoxView, with: otherItems) } - func addGridView(in superView: NSView, with views: [[NSView]]) { + private func getRecoder(for name: KeyboardShortcuts.Name) -> KeyboardShortcuts.RecorderCocoa { + let view = KeyboardShortcuts.RecorderCocoa(for: name) + view.setContentCompressionResistancePriority(.required, for: .vertical) + return view + } + + private func addGridView(in superView: NSView, with views: [[NSView]]) { let gridView = NSGridView(views: views) gridView.rowSpacing = 10 - gridView.rowAlignment = .firstBaseline superView.addSubview(gridView) gridView.makeConstraintsToBindToSuperview(NSEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)) gridView.setContentHuggingPriority(.required, for: .vertical) From a8d6f53ca7dc98941fdc76519aa0f7b4d7ecc9ec Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:19:08 +0800 Subject: [PATCH 113/122] feat: show alert when user try to quit clashx with active window displaying --- ClashX.xcodeproj/project.pbxproj | 8 +- ClashX/Actions/BaseAction.swift | 11 --- ClashX/Actions/TerminalCleanUpAction.swift | 80 +++++++++++++++++++ .../UpdateExternalResourceAction.swift | 2 +- ClashX/AppDelegate.swift | 55 ++----------- ClashX/Base.lproj/Main.storyboard | 12 ++- ClashX/ClashWindowController.swift | 20 ++++- ClashX/Extensions/NSView+Nib.swift | 12 +++ .../en.lproj/Localizable.strings | 6 ++ .../zh-Hans.lproj/Localizable.strings | 6 ++ .../zh-Hant.lproj/Localizable.strings | 6 ++ .../ClashWebViewContoller.swift | 11 --- .../Connections/DashboardViewController.swift | 3 - .../Settings/SettingTabViewController.swift | 2 +- ClashX/zh-Hans.lproj/Main.strings | 3 + ClashX/zh-Hant.lproj/Main.strings | 3 + 16 files changed, 153 insertions(+), 87 deletions(-) delete mode 100644 ClashX/Actions/BaseAction.swift create mode 100644 ClashX/Actions/TerminalCleanUpAction.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 40e147c2c..d33b288cf 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -36,7 +36,6 @@ 495BFB8821919B9800C8779D /* RemoteConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495BFB8721919B9800C8779D /* RemoteConfigManager.swift */; }; 4960A6DB2136529200B940C9 /* JSBridgeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */; }; 496322222AA5D89E00854231 /* UpdateExternalResourceAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */; }; - 496322242AA5D91200854231 /* BaseAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496322232AA5D91200854231 /* BaseAction.swift */; }; 4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */; }; 4969E73D2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4969E73F2A5E3CB20012E005 /* ConnectionDetailInfoGeneralView.xib */; }; 496BDEE021196F1E00C5207F /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496BDEDF21196F1E00C5207F /* Logger.swift */; }; @@ -48,6 +47,7 @@ 4981C88B216BAE4A008CC14A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4981C88D216BAE4A008CC14A /* Localizable.strings */; }; 4982F51F2344A216008804B0 /* Cgo+Convert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4982F51E2344A216008804B0 /* Cgo+Convert.swift */; }; 49862FA0218418C600A1D5EC /* ClashRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49862F9F218418C600A1D5EC /* ClashRule.swift */; }; + 49870ADB2AA75DC7002B106B /* TerminalCleanUpAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49870ADA2AA75DC7002B106B /* TerminalCleanUpAction.swift */; }; 4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98D20D0AE990001E564 /* sampleConfig.yaml */; }; 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */; }; 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */; }; @@ -191,7 +191,6 @@ 495BFB8721919B9800C8779D /* RemoteConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigManager.swift; sourceTree = ""; }; 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSBridgeHandler.swift; sourceTree = ""; }; 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateExternalResourceAction.swift; sourceTree = ""; }; - 496322232AA5D91200854231 /* BaseAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAction.swift; sourceTree = ""; }; 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserNotificationCenter+Extension.swift"; sourceTree = ""; }; 4969E73E2A5E3CB20012E005 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ConnectionDetailInfoGeneralView.xib; sourceTree = ""; }; 4969E7412A5E3CB80012E005 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ConnectionDetailInfoGeneralView.strings"; sourceTree = ""; }; @@ -209,6 +208,7 @@ 4981C88E216BAE4D008CC14A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 4982F51E2344A216008804B0 /* Cgo+Convert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Cgo+Convert.swift"; sourceTree = ""; }; 49862F9F218418C600A1D5EC /* ClashRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashRule.swift; sourceTree = ""; }; + 49870ADA2AA75DC7002B106B /* TerminalCleanUpAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalCleanUpAction.swift; sourceTree = ""; }; 498960722340F21C00AFB7EC /* com.west2online.ClashX.ProxyConfigHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = com.west2online.ClashX.ProxyConfigHelper.entitlements; sourceTree = ""; }; 4989F98D20D0AE990001E564 /* sampleConfig.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = sampleConfig.yaml; sourceTree = ""; }; 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTabViewController.swift; sourceTree = ""; }; @@ -430,7 +430,7 @@ isa = PBXGroup; children = ( 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */, - 496322232AA5D91200854231 /* BaseAction.swift */, + 49870ADA2AA75DC7002B106B /* TerminalCleanUpAction.swift */, ); path = Actions; sourceTree = ""; @@ -910,7 +910,6 @@ 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */, 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */, 49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */, - 496322242AA5D91200854231 /* BaseAction.swift in Sources */, 4952C3D02117027C004A4FA8 /* ConfigFileManager.swift in Sources */, 49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */, 4949D154213242F600EF85E6 /* Paths.swift in Sources */, @@ -951,6 +950,7 @@ 492C4871210EF62E004554A0 /* ClashConfig.swift in Sources */, 492C4869210EE6B9004554A0 /* ApiRequest.swift in Sources */, 49CF3B6520CEE06C001EBF94 /* ConfigManager.swift in Sources */, + 49870ADB2AA75DC7002B106B /* TerminalCleanUpAction.swift in Sources */, F9E754D0239CC21F00CEE7CC /* WebPortalManager.swift in Sources */, 495BFB8821919B9800C8779D /* RemoteConfigManager.swift in Sources */, 4982F51F2344A216008804B0 /* Cgo+Convert.swift in Sources */, diff --git a/ClashX/Actions/BaseAction.swift b/ClashX/Actions/BaseAction.swift deleted file mode 100644 index 50274a1da..000000000 --- a/ClashX/Actions/BaseAction.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// BaseAction.swift -// ClashX -// -// Created by yicheng on 2023/9/4. -// Copyright © 2023 west2online. All rights reserved. -// - -protocol BaseStaticAction { - static func run() -} diff --git a/ClashX/Actions/TerminalCleanUpAction.swift b/ClashX/Actions/TerminalCleanUpAction.swift new file mode 100644 index 000000000..0380645e1 --- /dev/null +++ b/ClashX/Actions/TerminalCleanUpAction.swift @@ -0,0 +1,80 @@ +// +// TerminalCleanUpAction.swift +// ClashX +// +// Created by yicheng on 2023/9/5. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import Foundation +import RxSwift + +enum TerminalConfirmAction { + static func run() -> NSApplication.TerminateReply { + guard confirmAction() else { + return .terminateCancel + } + let group = DispatchGroup() + var shouldWait = false + + if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) || + NetworkChangeNotifier.hasInterfaceProxySetToClash() { + Logger.log("ClashX quit need clean proxy setting") + shouldWait = true + group.enter() + + SystemProxyManager.shared.disableProxy(forceDisable: ConfigManager.shared.isProxySetByOtherVariable.value) { + group.leave() + } + } + + if !shouldWait { + Logger.log("ClashX quit without clean waiting") + return .terminateNow + } + + if let statusItem = AppDelegate.shared.statusItem, statusItem.menu != nil { + statusItem.menu = nil + } + AppDelegate.shared.disposeBag = DisposeBag() + + DispatchQueue.global(qos: .default).async { + let res = group.wait(timeout: .now() + 5) + switch res { + case .success: + Logger.log("ClashX quit after clean up finish") + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + NSApp.reply(toApplicationShouldTerminate: true) + } + DispatchQueue.global().asyncAfter(deadline: .now() + 1) { + NSApp.reply(toApplicationShouldTerminate: true) + } + case .timedOut: + Logger.log("ClashX quit after clean up timeout") + DispatchQueue.main.async { + NSApp.reply(toApplicationShouldTerminate: true) + } + DispatchQueue.global().asyncAfter(deadline: .now() + 1) { + NSApp.reply(toApplicationShouldTerminate: true) + } + } + } + + Logger.log("ClashX quit wait for clean up") + return .terminateLater + } + + static func confirmAction() -> Bool { + if NSApp.activationPolicy() == .regular { + let alert = NSAlert() + alert.messageText = NSLocalizedString("Quit ClashX?", comment: "") + alert.informativeText = NSLocalizedString("The active connections will be interrupted.", comment: "") + alert.alertStyle = .informational + alert.addButton(withTitle: NSLocalizedString("Quit", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + return alert.runModal() == .alertFirstButtonReturn + } + return true + } +} diff --git a/ClashX/Actions/UpdateExternalResourceAction.swift b/ClashX/Actions/UpdateExternalResourceAction.swift index 09e642377..11eff0539 100644 --- a/ClashX/Actions/UpdateExternalResourceAction.swift +++ b/ClashX/Actions/UpdateExternalResourceAction.swift @@ -7,7 +7,7 @@ // import Foundation -enum UpdateExternalResourceAction: BaseStaticAction { +enum UpdateExternalResourceAction { static func run() { ApiRequest.requestExternalProviderNames { provider in let group = DispatchGroup() diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index e51d8b99a..7576f8369 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -21,7 +21,7 @@ let statusItemLengthWithSpeed: CGFloat = 72 @main class AppDelegate: NSObject, NSApplicationDelegate { - var statusItem: NSStatusItem! + private(set) var statusItem: NSStatusItem! @IBOutlet var checkForUpdateMenuItem: NSMenuItem! @IBOutlet var statusMenu: NSMenu! @@ -160,54 +160,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { - let group = DispatchGroup() - var shouldWait = false - - if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) || - NetworkChangeNotifier.hasInterfaceProxySetToClash() { - Logger.log("ClashX quit need clean proxy setting") - shouldWait = true - group.enter() - - SystemProxyManager.shared.disableProxy(forceDisable: ConfigManager.shared.isProxySetByOtherVariable.value) { - group.leave() - } - } - - if !shouldWait { - Logger.log("ClashX quit without clean waiting") - return .terminateNow - } - - if statusItem != nil, statusItem.menu != nil { - statusItem.menu = nil - } - disposeBag = DisposeBag() - - DispatchQueue.global(qos: .default).async { - let res = group.wait(timeout: .now() + 5) - switch res { - case .success: - Logger.log("ClashX quit after clean up finish") - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - NSApp.reply(toApplicationShouldTerminate: true) - } - DispatchQueue.global().asyncAfter(deadline: .now() + 1) { - NSApp.reply(toApplicationShouldTerminate: true) - } - case .timedOut: - Logger.log("ClashX quit after clean up timeout") - DispatchQueue.main.async { - NSApp.reply(toApplicationShouldTerminate: true) - } - DispatchQueue.global().asyncAfter(deadline: .now() + 1) { - NSApp.reply(toApplicationShouldTerminate: true) - } - } - } - - Logger.log("ClashX quit wait for clean up") - return .terminateLater + return TerminalConfirmAction.run() } func applicationWillTerminate(_ aNotification: Notification) { @@ -758,6 +711,10 @@ extension AppDelegate { @IBAction func actionQuit(_ sender: Any) { NSApplication.shared.terminate(self) } + + @IBAction func actionMoreSetting(_ sender: Any) { + ClashWindowController.create().showWindow(sender) + } } // MARK: Streaming Info diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 1e84c57bf..9985b2b03 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -9,7 +9,7 @@ - + @@ -529,6 +529,13 @@ + + + + + + + @@ -864,8 +871,7 @@ - - + diff --git a/ClashX/ClashWindowController.swift b/ClashX/ClashWindowController.swift index 1baec81c8..a983af73f 100644 --- a/ClashX/ClashWindowController.swift +++ b/ClashX/ClashWindowController.swift @@ -24,7 +24,8 @@ private class ClashWindowsRecorder { class ClashWindowController: NSWindowController, NSWindowDelegate { var onWindowClose: (() -> Void)? - var lastSize: CGSize? { + private var fromCache = false + private var lastSize: CGSize? { get { if let str = UserDefaults.standard.value(forKey: "lastSize.\(T.className())") as? String { return NSSizeFromString(str) as CGSize @@ -40,12 +41,23 @@ class ClashWindowController: NSWindowController, NSWindowDe static func create() -> NSWindowController { if let wc = ClashWindowsRecorder.shared.windowControllers.first(where: { $0 is Self }) { + (wc as? ClashWindowController)?.fromCache = true return wc } let win = NSWindow() let wc = ClashWindowController(window: win) - wc.contentViewController = T() + if let X = T.self as? NibLoadable.Type { + wc.contentViewController = (X.createFromNib(in: .main) as! NSViewController) + } else { + wc.contentViewController = T() + } win.titlebarAppearsTransparent = false + win.styleMask.insert(.closable) + win.styleMask.insert(.resizable) + win.styleMask.insert(.miniaturizable) + if let title = wc.contentViewController?.title { + win.title = title + } ClashWindowsRecorder.shared.windowControllers.append(wc) return wc } @@ -53,10 +65,10 @@ class ClashWindowController: NSWindowController, NSWindowDe override func showWindow(_ sender: Any?) { super.showWindow(sender) NSApp.activate(ignoringOtherApps: true) - if let lastSize = lastSize, lastSize != .zero { + if !fromCache, let lastSize = lastSize, lastSize != .zero { window?.setContentSize(lastSize) + window?.center() } - window?.center() window?.makeKeyAndOrderFront(self) window?.delegate = self } diff --git a/ClashX/Extensions/NSView+Nib.swift b/ClashX/Extensions/NSView+Nib.swift index 09bef9396..492a51d0f 100644 --- a/ClashX/Extensions/NSView+Nib.swift +++ b/ClashX/Extensions/NSView+Nib.swift @@ -27,3 +27,15 @@ extension NibLoadable where Self: NSView { return views.last as! Self } } + +extension NibLoadable where Self: NSViewController { + static var nibName: String? { + return String(describing: Self.self) + } + + static func createFromNib(in bundle: Bundle = Bundle.main) -> Self { + guard let nibName = nibName else { fatalError() } + let sb = NSStoryboard(name: "Main", bundle: Bundle.main) + return sb.instantiateController(withIdentifier: nibName) as! Self + } +} diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index 0b0236981..b3bc57215 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -184,6 +184,9 @@ /* No comment provided by engineer. */ "Quit" = "Quit"; +/* No comment provided by engineer. */ +"Quit ClashX?" = "Quit ClashX?"; + /* No comment provided by engineer. */ "Recent Connections" = "Recent Connections"; @@ -256,6 +259,9 @@ /* No comment provided by engineer. */ "Testing" = "Testing"; +/* No comment provided by engineer. */ +"The active connections will be interrupted." = "The active connections will be interrupted."; + /* No comment provided by engineer. */ "The remote config name is duplicated" = "The remote config name is duplicated"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 297d17e35..77b8c6cf1 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -184,6 +184,9 @@ /* No comment provided by engineer. */ "Quit" = "退出"; +/* No comment provided by engineer. */ +"Quit ClashX?" = "退出ClashX?"; + /* No comment provided by engineer. */ "Recent Connections" = "最近连接"; @@ -256,6 +259,9 @@ /* No comment provided by engineer. */ "Testing" = "测速中"; +/* No comment provided by engineer. */ +"The active connections will be interrupted." = "活动的连接将被强制打断。"; + /* No comment provided by engineer. */ "The remote config name is duplicated" = "托管配置文件名称重复"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index deb180d39..3ef2a7d71 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -184,6 +184,9 @@ /* No comment provided by engineer. */ "Quit" = "退出"; +/* No comment provided by engineer. */ +"Quit ClashX?" = "退出 ClashX?"; + /* No comment provided by engineer. */ "Recent Connections" = "最近連線"; @@ -256,6 +259,9 @@ /* No comment provided by engineer. */ "Testing" = "測試中"; +/* No comment provided by engineer. */ +"The active connections will be interrupted." = "活躍的連接將被中斷。"; + /* No comment provided by engineer. */ "The remote config name is duplicated" = "遠端配置名稱重複"; diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index 0e3ab8935..63ab61024 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -33,14 +33,6 @@ class ClashWebViewContoller: NSViewController { let effectView = NSVisualEffectView() - static func createWindowController() -> NSWindowController { - let sb = NSStoryboard(name: "Main", bundle: Bundle.main) - let vc = sb.instantiateController(withIdentifier: "ClashWebViewContoller") as! ClashWebViewContoller - let wc = NSWindowController(window: NSWindow()) - wc.contentViewController = vc - return wc - } - override func loadView() { view = NSView(frame: NSRect(origin: .zero, size: minSize)) } @@ -86,9 +78,6 @@ class ClashWebViewContoller: NSViewController { view.window?.isOpaque = false view.window?.backgroundColor = NSColor.clear - view.window?.styleMask.insert(.closable) - view.window?.styleMask.insert(.resizable) - view.window?.styleMask.insert(.miniaturizable) view.window?.toolbar = NSToolbar() view.window?.toolbar?.showsBaselineSeparator = false view.wantsLayer = true diff --git a/ClashX/ViewControllers/Connections/DashboardViewController.swift b/ClashX/ViewControllers/Connections/DashboardViewController.swift index 6e0ea1ce9..eb25e9f26 100644 --- a/ClashX/ViewControllers/Connections/DashboardViewController.swift +++ b/ClashX/ViewControllers/Connections/DashboardViewController.swift @@ -51,9 +51,6 @@ class DashboardViewController: NSViewController { super.viewWillAppear() toolbar.delegate = self view.window?.toolbar = toolbar - view.window?.styleMask.insert(.closable) - view.window?.styleMask.insert(.resizable) - view.window?.styleMask.insert(.miniaturizable) view.window?.backgroundColor = NSColor.clear if #available(macOS 11.0, *) { view.window?.toolbarStyle = .unifiedCompact diff --git a/ClashX/ViewControllers/Settings/SettingTabViewController.swift b/ClashX/ViewControllers/Settings/SettingTabViewController.swift index 961395214..a93155573 100644 --- a/ClashX/ViewControllers/Settings/SettingTabViewController.swift +++ b/ClashX/ViewControllers/Settings/SettingTabViewController.swift @@ -8,7 +8,7 @@ import Cocoa -class SettingTabViewController: NSTabViewController { +class SettingTabViewController: NSTabViewController, NibLoadable { override func viewDidLoad() { super.viewDidLoad() tabStyle = .toolbar diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index 0e4595e1a..b90866e6b 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -312,3 +312,6 @@ /* Class = "NSButtonCell"; title = "Enable IPv6"; ObjectID = "KRm-2U-T4s"; */ "KRm-2U-T4s.title" = "启用IPv6"; + +/* Class = "NSMenuItem"; title = "Quit ClashX"; ObjectID = "TsN-g8-OjG"; */ +"TsN-g8-OjG.title" = "退出 ClashX"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index af6815ed6..b5100a509 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -312,3 +312,6 @@ /* Class = "NSButtonCell"; title = "Enable IPv6"; ObjectID = "KRm-2U-T4s"; */ "KRm-2U-T4s.title" = "啟用IPv6"; + +/* Class = "NSMenuItem"; title = "Quit ClashX"; ObjectID = "TsN-g8-OjG"; */ +"TsN-g8-OjG.title" = "退出 ClashX"; From 61582e3f80bcfca594cbc609be4e675df2d4cb83 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 5 Sep 2023 22:01:05 +0800 Subject: [PATCH 114/122] feat: allow edit config file directly when reload with an error --- ClashX.xcodeproj/project.pbxproj | 4 +++ ClashX/Actions/UpdateConfigAction.swift | 26 +++++++++++++++++++ ClashX/AppDelegate.swift | 6 ++--- ClashX/General/ApiRequest.swift | 14 ++-------- ClashX/General/Managers/ConfigManager.swift | 15 +++++++++++ .../en.lproj/Localizable.strings | 3 +++ .../zh-Hans.lproj/Localizable.strings | 3 +++ .../zh-Hant.lproj/Localizable.strings | 3 +++ 8 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 ClashX/Actions/UpdateConfigAction.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index d33b288cf..1212e630e 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -48,6 +48,7 @@ 4982F51F2344A216008804B0 /* Cgo+Convert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4982F51E2344A216008804B0 /* Cgo+Convert.swift */; }; 49862FA0218418C600A1D5EC /* ClashRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49862F9F218418C600A1D5EC /* ClashRule.swift */; }; 49870ADB2AA75DC7002B106B /* TerminalCleanUpAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49870ADA2AA75DC7002B106B /* TerminalCleanUpAction.swift */; }; + 49870ADD2AA76602002B106B /* UpdateConfigAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49870ADC2AA76602002B106B /* UpdateConfigAction.swift */; }; 4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98D20D0AE990001E564 /* sampleConfig.yaml */; }; 498BC2532929CC2A00CA8084 /* SettingTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */; }; 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498BC2542929CCAE00CA8084 /* GeneralSettingViewController.swift */; }; @@ -209,6 +210,7 @@ 4982F51E2344A216008804B0 /* Cgo+Convert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Cgo+Convert.swift"; sourceTree = ""; }; 49862F9F218418C600A1D5EC /* ClashRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashRule.swift; sourceTree = ""; }; 49870ADA2AA75DC7002B106B /* TerminalCleanUpAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalCleanUpAction.swift; sourceTree = ""; }; + 49870ADC2AA76602002B106B /* UpdateConfigAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateConfigAction.swift; sourceTree = ""; }; 498960722340F21C00AFB7EC /* com.west2online.ClashX.ProxyConfigHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = com.west2online.ClashX.ProxyConfigHelper.entitlements; sourceTree = ""; }; 4989F98D20D0AE990001E564 /* sampleConfig.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = sampleConfig.yaml; sourceTree = ""; }; 498BC2522929CC2A00CA8084 /* SettingTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTabViewController.swift; sourceTree = ""; }; @@ -431,6 +433,7 @@ children = ( 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */, 49870ADA2AA75DC7002B106B /* TerminalCleanUpAction.swift */, + 49870ADC2AA76602002B106B /* UpdateConfigAction.swift */, ); path = Actions; sourceTree = ""; @@ -994,6 +997,7 @@ 4960A6DB2136529200B940C9 /* JSBridgeHandler.swift in Sources */, 493AEAE5221AE7230016FE98 /* ProxyMenuItem.swift in Sources */, 496322222AA5D89E00854231 /* UpdateExternalResourceAction.swift in Sources */, + 49870ADD2AA76602002B106B /* UpdateConfigAction.swift in Sources */, 499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */, F939724C23A4B33500FE5A3F /* ClashProvider.swift in Sources */, 0AEF6EAF2A5F961B00EFEE23 /* SectionedTableView.swift in Sources */, diff --git a/ClashX/Actions/UpdateConfigAction.swift b/ClashX/Actions/UpdateConfigAction.swift new file mode 100644 index 000000000..dba801269 --- /dev/null +++ b/ClashX/Actions/UpdateConfigAction.swift @@ -0,0 +1,26 @@ +// +// UpdateConfigAction.swift +// ClashX +// +// Created by yicheng on 2023/9/5. +// Copyright © 2023 west2online. All rights reserved. +// + +import AppKit +import Foundation + +enum UpdateConfigAction { + static func showError(text: String, configName: String) { + let alert = NSAlert() + alert.alertStyle = .critical + alert.messageText = NSLocalizedString("Reload Config Fail", comment: "") + alert.informativeText = text + alert.addButton(withTitle: NSLocalizedString("Edit in Text Mode", comment: "")) + alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) + if alert.runModal() == .alertFirstButtonReturn { + ConfigManager.getConfigPath(configName: configName) { + NSWorkspace.shared.open(URL(fileURLWithPath: $0)) + } + } + } +} diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 7576f8369..cf2861e7b 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -509,10 +509,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { completeHandler?(err) } - if let error = err { - NSUserNotificationCenter.default - .postNotificationAlert(title: NSLocalizedString("Reload Config Fail", comment: ""), - info: error) + if let err { + UpdateConfigAction.showError(text: err, configName: config) } else { self.syncConfig() self.resetStreamApi() diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index dc88f24b8..d661666e8 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -105,18 +105,8 @@ class ApiRequest { } static func requestConfigUpdate(configName: String, callback: @escaping ((ErrorString?) -> Void)) { - if ICloudManager.shared.useiCloud.value { - ICloudManager.shared.getUrl { url in - guard let url = url else { - callback("icloud error") - return - } - let configPath = url.appendingPathComponent(Paths.configFileName(for: configName)).path - requestConfigUpdate(configPath: configPath, callback: callback) - } - } else { - let filePath = Paths.localConfigPath(for: configName) - requestConfigUpdate(configPath: filePath, callback: callback) + ConfigManager.getConfigPath(configName: configName) { + requestConfigUpdate(configPath: $0, callback: callback) } } diff --git a/ClashX/General/Managers/ConfigManager.swift b/ClashX/General/Managers/ConfigManager.swift index 63250001f..c6d09e854 100644 --- a/ClashX/General/Managers/ConfigManager.swift +++ b/ClashX/General/Managers/ConfigManager.swift @@ -145,6 +145,21 @@ class ConfigManager { UserDefaults.standard.set(newValue.rawValue, forKey: "selectLoggingApiLevel") } } + + static func getConfigPath(configName: String, complete: ((String) -> Void)? = nil) { + if ICloudManager.shared.useiCloud.value { + ICloudManager.shared.getUrl { url in + guard let url = url else { + return + } + let configPath = url.appendingPathComponent(Paths.configFileName(for: configName)).path + complete?(configPath) + } + } else { + let filePath = Paths.localConfigPath(for: configName) + complete?(filePath) + } + } } extension ConfigManager { diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index b3bc57215..d45893548 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -94,6 +94,9 @@ /* No comment provided by engineer. */ "Download speed" = "Download speed"; +/* No comment provided by engineer. */ +"Edit in Text Mode" = "Edit in Text Mode"; + /* No comment provided by engineer. */ "fail" = "fail"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index 77b8c6cf1..b3c874a76 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -94,6 +94,9 @@ /* No comment provided by engineer. */ "Download speed" = "下载速率"; +/* No comment provided by engineer. */ +"Edit in Text Mode" = "编辑配置"; + /* No comment provided by engineer. */ "fail" = "失败"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 3ef2a7d71..5e3089c1f 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -94,6 +94,9 @@ /* No comment provided by engineer. */ "Download speed" = "下載速率"; +/* No comment provided by engineer. */ +"Edit in Text Mode" = "編輯配置"; + /* No comment provided by engineer. */ "fail" = "失敗"; From e98a6c31040060ebc708222bf0d44ccdfa16852e Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 6 Sep 2023 10:53:05 +0800 Subject: [PATCH 115/122] feat: add DiagnosticReports folder open button in debug view controller --- ClashX/Actions/UpdateConfigAction.swift | 1 + ClashX/Base.lproj/Main.storyboard | 72 ++++++++++++++----- .../Settings/DebugSettingViewController.swift | 4 ++ ClashX/zh-Hans.lproj/Main.strings | 6 ++ ClashX/zh-Hant.lproj/Main.strings | 6 ++ 5 files changed, 71 insertions(+), 18 deletions(-) diff --git a/ClashX/Actions/UpdateConfigAction.swift b/ClashX/Actions/UpdateConfigAction.swift index dba801269..e4ddcf147 100644 --- a/ClashX/Actions/UpdateConfigAction.swift +++ b/ClashX/Actions/UpdateConfigAction.swift @@ -17,6 +17,7 @@ enum UpdateConfigAction { alert.informativeText = text alert.addButton(withTitle: NSLocalizedString("Edit in Text Mode", comment: "")) alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) + NSApp.activate(ignoringOtherApps: true) if alert.runModal() == .alertFirstButtonReturn { ConfigManager.getConfigPath(configName: configName) { NSWorkspace.shared.open(URL(fileURLWithPath: $0)) diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 9985b2b03..6c960ccf7 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -287,15 +287,15 @@ - + - + - + @@ -309,7 +309,7 @@ - + @@ -416,15 +416,15 @@ - + - + - + @@ -438,7 +438,7 @@ - + @@ -1638,16 +1638,16 @@ - + - + - + - + @@ -1701,7 +1701,7 @@ - + @@ -1735,7 +1735,7 @@ - + @@ -1769,7 +1769,7 @@ - + @@ -1815,7 +1815,7 @@ - + @@ -1848,6 +1848,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1890,6 +1924,7 @@ + @@ -1900,6 +1935,7 @@ + diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index 916d35ab7..ba02e56d4 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -75,4 +75,8 @@ class DebugSettingViewController: NSViewController { Settings.disableRestoreProxy.toggle() revertProxyButton.state = Settings.disableRestoreProxy ? .off : .on } + + @IBAction func actionOpenCrashLogFolder(_ sender: Any) { + NSWorkspace.shared.open(URL(fileURLWithPath: "\(NSHomeDirectory())/Library/Logs/DiagnosticReports", isDirectory: true)) + } } diff --git a/ClashX/zh-Hans.lproj/Main.strings b/ClashX/zh-Hans.lproj/Main.strings index b90866e6b..f19202567 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -315,3 +315,9 @@ /* Class = "NSMenuItem"; title = "Quit ClashX"; ObjectID = "TsN-g8-OjG"; */ "TsN-g8-OjG.title" = "退出 ClashX"; + +/* Class = "NSTextFieldCell"; title = "Crash Log Folder"; ObjectID = "LFb-xY-CNV"; */ +"LFb-xY-CNV.title" = "崩溃日志文件夹"; + +/* Class = "NSButtonCell"; title = "Open"; ObjectID = "Wci-1w-zlx"; */ +"Wci-1w-zlx.title" = "打开"; diff --git a/ClashX/zh-Hant.lproj/Main.strings b/ClashX/zh-Hant.lproj/Main.strings index b5100a509..556a07ab7 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -315,3 +315,9 @@ /* Class = "NSMenuItem"; title = "Quit ClashX"; ObjectID = "TsN-g8-OjG"; */ "TsN-g8-OjG.title" = "退出 ClashX"; + +/* Class = "NSTextFieldCell"; title = "Crash Log Folder"; ObjectID = "LFb-xY-CNV"; */ +"LFb-xY-CNV.title" = "崩潰日誌文件夾"; + +/* Class = "NSButtonCell"; title = "Open"; ObjectID = "Wci-1w-zlx"; */ +"Wci-1w-zlx.title" = "打開"; From ec117e5055f0124ea930b6c214a2cb3e65f9e621 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 9 Sep 2023 15:03:19 +0800 Subject: [PATCH 116/122] fix: disable menu width limit on macOS 14, preventing display issue --- ClashX/Views/ProxyGroupMenuItemView.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ClashX/Views/ProxyGroupMenuItemView.swift b/ClashX/Views/ProxyGroupMenuItemView.swift index 284b3185a..28917b4c1 100644 --- a/ClashX/Views/ProxyGroupMenuItemView.swift +++ b/ClashX/Views/ProxyGroupMenuItemView.swift @@ -67,7 +67,11 @@ class ProxyGroupMenuItemView: MenuItemBaseView { selectProxyLabel.leftAnchor.constraint(greaterThanOrEqualTo: groupNameLabel.rightAnchor, constant: 20).isActive = true // max - effectView.widthAnchor.constraint(lessThanOrEqualToConstant: 330).isActive = true + if #available(macOS 14, *) { + selectProxyLabel.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true + } else { + effectView.widthAnchor.constraint(lessThanOrEqualToConstant: 330).isActive = true + } // font & color groupNameLabel.font = type(of: self).labelFont selectProxyLabel.font = type(of: self).labelFont From 6a2b7274deba8c50b7128f809aff00bae11de292 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Sat, 14 Oct 2023 19:33:05 +0800 Subject: [PATCH 117/122] fix: should request location permission on macOS14 --- ClashX.xcodeproj/project.pbxproj | 4 + ClashX/Basic/Logger.swift | 4 +- ClashX/General/Utils/Command.swift | 34 ++++++ .../General/Utils/NetworkChangeNotifier.swift | 4 - ClashX/General/Utils/SSIDSuspendTool.swift | 112 ++++++++++++++++-- ClashX/Info.plist | 4 + .../en.lproj/Localizable.strings | 3 + .../zh-Hans.lproj/Localizable.strings | 3 + .../zh-Hant.lproj/Localizable.strings | 3 + .../GeneralSettingViewController.swift | 3 + Gemfile | 2 +- Gemfile.lock | 68 +++++------ 12 files changed, 190 insertions(+), 54 deletions(-) create mode 100644 ClashX/General/Utils/Command.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 1212e630e..c116f1b05 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -100,6 +100,7 @@ 49D767742A6195C800830333 /* ConnectionsReq.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D767732A6195C800830333 /* ConnectionsReq.swift */; }; 49D767762A6195E600830333 /* StructedLogReq.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D767752A6195E600830333 /* StructedLogReq.swift */; }; 49D84AD32A56E9760074CCDB /* ConnectionStatusIconCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D84AD22A56E9760074CCDB /* ConnectionStatusIconCellView.swift */; }; + 49FEC6692AD9369C00BAD9F5 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49FEC6682AD9369C00BAD9F5 /* Command.swift */; }; 8A2BBEA727A03ACB0081EBEF /* ProxySetting.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */; }; 8ACD21BB27A04C7800BC4632 /* ProxySettingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */; }; 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACD21BC27A04ED500BC4632 /* ProxyModeChangeCommand.swift */; }; @@ -269,6 +270,7 @@ 49D8276627E9B01700159D93 /* LoginKitWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginKitWrapper.h; sourceTree = ""; }; 49D8276727E9B01700159D93 /* LoginKitWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginKitWrapper.m; sourceTree = ""; }; 49D84AD22A56E9760074CCDB /* ConnectionStatusIconCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionStatusIconCellView.swift; sourceTree = ""; }; + 49FEC6682AD9369C00BAD9F5 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; 5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.debug.xcconfig"; sourceTree = ""; }; 8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = ProxySetting.sdef; sourceTree = ""; }; 8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxySettingCommand.swift; sourceTree = ""; }; @@ -361,6 +363,7 @@ 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */, 49B445152457CDF000B27E3E /* ClashStatusTool.swift */, 49D223382A1DA5F10002FFCB /* SSIDSuspendTool.swift */, + 49FEC6682AD9369C00BAD9F5 /* Command.swift */, ); path = Utils; sourceTree = ""; @@ -962,6 +965,7 @@ 49722FF0211F338B00650A41 /* EventStream.swift in Sources */, 499A486522EEA3FD00F6C675 /* Array+Safe.swift in Sources */, F92D0B24236BC12000575E15 /* SavedProxyModel.swift in Sources */, + 49FEC6692AD9369C00BAD9F5 /* Command.swift in Sources */, F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */, 49CCDA302A54FC3300FF1E13 /* ConnectionLeftPannelView.swift in Sources */, 49D223392A1DA5F10002FFCB /* SSIDSuspendTool.swift in Sources */, diff --git a/ClashX/Basic/Logger.swift b/ClashX/Basic/Logger.swift index 38348eb1b..e4fd5d6b0 100644 --- a/ClashX/Basic/Logger.swift +++ b/ClashX/Basic/Logger.swift @@ -41,8 +41,8 @@ class Logger { } } - static func log(_ msg: String, level: ClashLogLevel = .info, function: String = #function) { - shared.logToFile(msg: "[\(level.rawValue)] \(function) \(msg)", level: level) + static func log(_ msg: String, level: ClashLogLevel = .info, file: String = #file, function: String = #function) { + shared.logToFile(msg: "[\(level.rawValue)] \(file) \(function) \(msg)", level: level) } func logFilePath() -> String { diff --git a/ClashX/General/Utils/Command.swift b/ClashX/General/Utils/Command.swift new file mode 100644 index 000000000..528bb5f8d --- /dev/null +++ b/ClashX/General/Utils/Command.swift @@ -0,0 +1,34 @@ +// +// Command.swift +// ClashX +// +// Created by yicheng on 2023/10/13. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation + +struct Command { + let cmd: String + let args: [String] + + func run() -> String { + var output = "" + + let task = Process() + task.launchPath = cmd + task.arguments = args + + let outpipe = Pipe() + task.standardOutput = outpipe + + task.launch() + + task.waitUntilExit() + let outdata = outpipe.fileHandleForReading.readDataToEndOfFile() + if var string = String(data: outdata, encoding: .utf8) { + output = string.trimmingCharacters(in: .newlines) + } + return output + } +} diff --git a/ClashX/General/Utils/NetworkChangeNotifier.swift b/ClashX/General/Utils/NetworkChangeNotifier.swift index 655a774ed..e51162167 100644 --- a/ClashX/General/Utils/NetworkChangeNotifier.swift +++ b/ClashX/General/Utils/NetworkChangeNotifier.swift @@ -198,8 +198,4 @@ class NetworkChangeNotifier { } return allowIPV6 ? ipv6 : nil } - - static func getCurrentSSID() -> String? { - return CWWiFiClient.shared().interface()?.ssid() - } } diff --git a/ClashX/General/Utils/SSIDSuspendTool.swift b/ClashX/General/Utils/SSIDSuspendTool.swift index 467f4bf27..7e9ff48e7 100644 --- a/ClashX/General/Utils/SSIDSuspendTool.swift +++ b/ClashX/General/Utils/SSIDSuspendTool.swift @@ -6,24 +6,47 @@ // Copyright © 2023 west2online. All rights reserved. // +import CoreLocation +import CoreWLAN import Foundation import RxCocoa import RxSwift -class SSIDSuspendTool { +class SSIDSuspendTool: NSObject { static let shared = SSIDSuspendTool() - var disposeBag = DisposeBag() - func setup() { - NotificationCenter - .default - .rx - .notification(.systemNetworkStatusDidChange) - .observe(on: MainScheduler.instance) - .delay(.seconds(2), scheduler: MainScheduler.instance) - .bind { [weak self] _ in - self?.update() - }.disposed(by: disposeBag) + private var ssidChangePublisher = PublishSubject() + private var disposeBag = DisposeBag() + private lazy var locationManager = CLLocationManager() + var showNoticeOnNotPermission = false + + func setup() { + if AppVersionUtil.hasVersionChanged { + showNoticeOnNotPermission = true + } + requestPermissionIfNeed() + do { + try CWWiFiClient.shared().startMonitoringEvent(with: .ssidDidChange) + CWWiFiClient.shared().delegate = self + ssidChangePublisher + .observe(on: MainScheduler.instance) + .debounce(.seconds(1), scheduler: MainScheduler.instance) + .delay(.seconds(1), scheduler: MainScheduler.instance) + .bind { [weak self] _ in + self?.update() + }.disposed(by: disposeBag) + } catch let err { + Logger.log(String(describing: err), level: .warning) + NotificationCenter + .default + .rx + .notification(.systemNetworkStatusDidChange) + .observe(on: MainScheduler.instance) + .delay(.seconds(2), scheduler: MainScheduler.instance) + .bind { [weak self] _ in + self?.update() + }.disposed(by: disposeBag) + } ConfigManager.shared .proxyShouldPaused .asObservable() @@ -40,6 +63,27 @@ class SSIDSuspendTool { update() } + func requestPermissionIfNeed() { + defer { + showNoticeOnNotPermission = false + } + if #available(macOS 14, *) { + if Settings.disableSSIDList.isEmpty { return } + if locationManager.authorizationStatus == .notDetermined { + Logger.log("request location permission") + locationManager.desiredAccuracy = kCLLocationAccuracyReduced + locationManager.delegate = self + locationManager.requestAlwaysAuthorization() + } else if locationManager.authorizationStatus != .authorized { + if showNoticeOnNotPermission { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.openLocationSettings() + } + } + } + } + } + func update() { if shouldSuspend() { ConfigManager.shared.proxyShouldPaused.accept(true) @@ -49,10 +93,52 @@ class SSIDSuspendTool { } func shouldSuspend() -> Bool { - if let currentSSID = NetworkChangeNotifier.getCurrentSSID() { + if let currentSSID = getCurrentSSID() { return Settings.disableSSIDList.contains(currentSSID) } else { return false } } + + private func getCurrentSSID() -> String? { + if #available(macOS 14, *) { + if locationManager.authorizationStatus != .authorized { + let info = Command(cmd: "/System/Library/PrivateFrameworks/Apple80211.framework/Resources/airport", args: ["-I"]).run() + let ssid = info.components(separatedBy: "\n") + .lazy + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .first { $0.starts(with: "SSID:") }? + .components(separatedBy: ":") + .last?.trimmingCharacters(in: .whitespacesAndNewlines) + return ssid + } + } + return CWWiFiClient.shared().interface()?.ssid() + } + + private func openLocationSettings() { + NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Location")!) + NSApp.activate(ignoringOtherApps: true) + NSAlert.alert(with: NSLocalizedString("Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services.", comment: "")) + } +} + +extension SSIDSuspendTool: CLLocationManagerDelegate { + func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { + Logger.log("Location status: \(status.rawValue)") + if status != .authorized, showNoticeOnNotPermission { + openLocationSettings() + } + showNoticeOnNotPermission = false + } + + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {} + + func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {} +} + +extension SSIDSuspendTool: CWEventDelegate { + func ssidDidChangeForWiFiInterface(withName interfaceName: String) { + ssidChangePublisher.onNext(interfaceName) + } } diff --git a/ClashX/Info.plist b/ClashX/Info.plist index 250d7cc46..c8b4d0bcd 100644 --- a/ClashX/Info.plist +++ b/ClashX/Info.plist @@ -2,6 +2,10 @@ + NSLocationAlwaysAndWhenInUseUsageDescription + ClashX use location info to detect your current WiFi network SSID name and provide the auto suspend services. + NSLocationWhenInUseUsageDescription + ClashX use location info to detect your current WiFi network SSID name and provide the auto suspend services. BETA CFBundleDevelopmentRegion diff --git a/ClashX/Support Files/en.lproj/Localizable.strings b/ClashX/Support Files/en.lproj/Localizable.strings index d45893548..913ba0e90 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -172,6 +172,9 @@ /* No comment provided by engineer. */ "Open System Login Item Setting" = "Open System Login Item Setting"; +/* No comment provided by engineer. */ +"Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services." = "Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services."; + /* No comment provided by engineer. */ "Ports Open Fail, Please try to restart ClashX" = "Ports Open Fail, Please try to restart ClashX"; diff --git a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings index b3c874a76..b867ce1b5 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -172,6 +172,9 @@ /* No comment provided by engineer. */ "Open System Login Item Setting" = "打开系统登录项设置"; +/* No comment provided by engineer. */ +"Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services." = "请允许ClashX使用定位服务来获取当前所连接的WiFi名称从而提供按需暂停服务。"; + /* No comment provided by engineer. */ "Ports Open Fail, Please try to restart ClashX" = "端口打开失败,请尝试重启ClashX"; diff --git a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings index 5e3089c1f..83bf3f97a 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -172,6 +172,9 @@ /* No comment provided by engineer. */ "Open System Login Item Setting" = "打開系統登錄項設定"; +/* No comment provided by engineer. */ +"Please enable the location service for ClashX to detect your current WiFi network's SSID name and provide the auto-suspend services." = "請允許 ClashX 使用定位服務,以獲取您目前連接的 WiFi 名稱,並提供按需暫停服務。"; + /* No comment provided by engineer. */ "Ports Open Fail, Please try to restart ClashX" = "端口打開失敗,請嘗試重啟ClashX"; diff --git a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift index ef44eef9a..ee62ff637 100644 --- a/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/GeneralSettingViewController.swift @@ -127,6 +127,9 @@ class GeneralSettingViewController: NSViewController { if url.isUrlVaild() || url.isEmpty { Settings.benchMarkUrl = url } + SSIDSuspendTool.shared.showNoticeOnNotPermission = true + SSIDSuspendTool.shared.requestPermissionIfNeed() + SSIDSuspendTool.shared.update() } @IBAction func actionResetIgnoreList(_ sender: Any) { diff --git a/Gemfile b/Gemfile index e4b5be5c3..6a51e25e7 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,6 @@ source "https://rubygems.org" gem 'fastlane' gem 'cocoapods' - +gem "activesupport", "= 7.0.8" plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 082638597..e16815078 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,12 +3,12 @@ GEM specs: CFPropertyList (3.0.6) rexml - activesupport (7.0.5) + activesupport (7.0.8) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.4) + addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) @@ -16,27 +16,27 @@ GEM artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.779.0) - aws-sdk-core (3.174.0) + aws-partitions (1.835.0) + aws-sdk-core (3.185.1) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.66.0) - aws-sdk-core (~> 3, >= 3.174.0) + aws-sdk-kms (1.72.0) + aws-sdk-core (~> 3, >= 3.184.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.124.0) - aws-sdk-core (~> 3, >= 3.174.0) + aws-sdk-s3 (1.136.0) + aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.2) + aws-sigv4 (~> 1.6) + aws-sigv4 (1.6.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.1.0) - cocoapods (1.12.1) + cocoapods (1.13.0) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.12.1) + cocoapods-core (= 1.13.0) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.6.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -50,8 +50,8 @@ GEM molinillo (~> 0.8.0) nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.12.1) + xcodeproj (>= 1.23.0, < 2.0) + cocoapods-core (1.13.0) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -76,7 +76,7 @@ GEM highline (~> 2.0.0) concurrent-ruby (1.2.2) declarative (0.0.20) - digest-crc (0.6.4) + digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) @@ -85,7 +85,7 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.100.0) + excon (0.104.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -115,7 +115,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.7) - fastlane (2.213.0) + fastlane (2.216.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -136,6 +136,7 @@ GEM google-apis-playcustomapp_v1 (~> 0.1) google-cloud-storage (~> 1.31) highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) @@ -147,23 +148,23 @@ GEM security (= 0.1.3) simctl (~> 1.6.3) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-appcenter (2.1.0) + fastlane-plugin-appcenter (2.1.1) fastlane-plugin-update_xcodeproj (1.0.1) - fastlane-plugin-versioning (0.5.1) - ffi (1.15.5) + fastlane-plugin-versioning (0.5.2) + ffi (1.16.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.43.0) + google-apis-androidpublisher_v3 (0.50.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.0) + google-apis-core (0.11.1) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -192,10 +193,9 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.5.2) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) @@ -208,10 +208,9 @@ GEM jmespath (1.6.2) json (2.6.3) jwt (2.7.1) - memoist (0.16.2) mini_magick (4.12.0) - mini_mime (1.1.2) - minitest (5.18.0) + mini_mime (1.1.5) + minitest (5.20.0) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.3.0) @@ -229,13 +228,13 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.5) + rexml (3.2.6) rouge (2.0.7) ruby-macho (2.5.1) ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.17.0) + signet (0.18.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -244,8 +243,8 @@ GEM CFPropertyList naturally terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) @@ -259,10 +258,10 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (1.8.0) + unicode-display_width (2.5.0) webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.22.0) + xcodeproj (1.23.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -278,6 +277,7 @@ PLATFORMS ruby DEPENDENCIES + activesupport (= 7.0.8) cocoapods fastlane fastlane-plugin-appcenter From 1528477fb149113871abcd3d9076fc24e1d68d73 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:11:37 +0800 Subject: [PATCH 118/122] feat: use custom jsbridge, remove WebViewJavascriptBridge for xcode 15 issue --- ClashX.xcodeproj/project.pbxproj | 4 + ClashX/General/Utils/JSBridge.swift | 81 +++++++++++++++++++ ClashX/General/Utils/JSBridgeHandler.swift | 31 ++++--- .../ClashWebViewContoller.swift | 22 +---- Podfile | 1 - Podfile.lock | 6 +- 6 files changed, 104 insertions(+), 41 deletions(-) create mode 100644 ClashX/General/Utils/JSBridge.swift diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index c116f1b05..b0be7d541 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 4905A2C82A2058D400AEDA2E /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */; }; 4905A2CA2A20841B00AEDA2E /* NSView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */; }; 4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4908087A29F8F3FF007A4944 /* libresolv.tbd */; }; + 490C8A102AC26E67007140F2 /* JSBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 490C8A0F2AC26E67007140F2 /* JSBridge.swift */; }; 4913C82321157D0200F6B87C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913C82221157D0200F6B87C /* Notification.swift */; }; 491E6203258A424D00313AEF /* CommonUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 491E61FD258A424500313AEF /* CommonUtils.m */; }; 49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */; }; @@ -170,6 +171,7 @@ 4905A2C42A2058B000AEDA2E /* GlobalShortCutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalShortCutViewController.swift; sourceTree = ""; }; 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSView+Layout.swift"; sourceTree = ""; }; 4908087A29F8F3FF007A4944 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; }; + 490C8A0F2AC26E67007140F2 /* JSBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSBridge.swift; sourceTree = ""; }; 4913C82221157D0200F6B87C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 491E61FC258A424500313AEF /* CommonUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = ""; }; 491E61FD258A424500313AEF /* CommonUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUtils.m; sourceTree = ""; }; @@ -359,6 +361,7 @@ isa = PBXGroup; children = ( 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */, + 490C8A0F2AC26E67007140F2 /* JSBridge.swift */, 493AEAE2221AE3420016FE98 /* AppVersionUtil.swift */, 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */, 49B445152457CDF000B27E3E /* ClashStatusTool.swift */, @@ -927,6 +930,7 @@ F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */, 0AD7506D2A5B9B26001FFBBD /* ConnectionLeftTextCellView.swift in Sources */, 495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */, + 490C8A102AC26E67007140F2 /* JSBridge.swift in Sources */, 4991D2322A565E6A00978143 /* Combine+Ext.swift in Sources */, 49281C802A1F01FA00F60935 /* DebugSettingViewController.swift in Sources */, 49D767762A6195E600830333 /* StructedLogReq.swift in Sources */, diff --git a/ClashX/General/Utils/JSBridge.swift b/ClashX/General/Utils/JSBridge.swift new file mode 100644 index 000000000..6daf29662 --- /dev/null +++ b/ClashX/General/Utils/JSBridge.swift @@ -0,0 +1,81 @@ +// +// JSBridge.swift +// ClashX +// +// Created by yicheng on 2023/9/26. +// Copyright © 2023 west2online. All rights reserved. +// + +import Foundation +import WebKit + +class JSBridge: NSObject { + typealias ResponseCallback = (Any?) -> Void + typealias BridgeHandler = (Any?, @escaping ResponseCallback) -> Void + + private weak var webView: WKWebView? + private var handlers = [String: BridgeHandler]() + init(_ webView: WKWebView) { + self.webView = webView + super.init() + setup() + } + + deinit { + webView?.configuration.userContentController.removeAllUserScripts() + webView?.configuration.userContentController.removeScriptMessageHandler(forName: "jsBridge") + } + + private func setup() { + addScriptMessageHandler() + } + + private func addScriptMessageHandler() { + let scriptMessageHandler = ClashScriptMessageHandler(delegate: self) + webView?.configuration.userContentController.add(scriptMessageHandler, name: "jsBridge") + } + + private func sendBackMessage(data: Any?, eventID: String) { + let data = ["id": eventID, "data": data, "type": "jsBridge"] as [String: Any?] + do { + let jsonData = try JSONSerialization.data(withJSONObject: data, options: []) + let jsonString = String(data: jsonData, encoding: .utf8)! + let str = "window.postMessage(\(jsonString), window.origin);" + webView?.evaluateJavaScript(str) + } catch let err { + Logger.log(err.localizedDescription, level: .warning) + } + } + + func registerHandler(_ name: String, handler: @escaping BridgeHandler) { + handlers[name] = handler + } +} + +extension JSBridge: WKScriptMessageHandler { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + if + let body = message.body as? [String: Any], + let handlerName = body["name"] as? String, + let handler = handlers[handlerName], + let eventID = body["id"] as? String { + let data = body["data"] + handler(data) { [weak self] res in + self?.sendBackMessage(data: res, eventID: eventID) + } + } + } +} + +private class ClashScriptMessageHandler: NSObject, WKScriptMessageHandler { + weak var delegate: WKScriptMessageHandler? + + public init(delegate: WKScriptMessageHandler) { + self.delegate = delegate + super.init() + } + + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + delegate?.userContentController(userContentController, didReceive: message) + } +} diff --git a/ClashX/General/Utils/JSBridgeHandler.swift b/ClashX/General/Utils/JSBridgeHandler.swift index 6cf0c6c11..0877fa154 100644 --- a/ClashX/General/Utils/JSBridgeHandler.swift +++ b/ClashX/General/Utils/JSBridgeHandler.swift @@ -8,16 +8,14 @@ import Alamofire import SwiftyJSON -import WebViewJavascriptBridge +import WebKit class JsBridgeUtil { - static func initJSbridge(webview: Any, delegate: Any) -> WebViewJavascriptBridge { - let bridge = WebViewJavascriptBridge(webview)! - - bridge.setWebViewDelegate(delegate) + static func initJSbridge(webview: WKWebView, delegate: Any) -> JSBridge { + let bridge = JSBridge(webview) bridge.registerHandler("isSystemProxySet") { _, responseCallback in - responseCallback?(ConfigManager.shared.proxyPortAutoSet) + responseCallback(ConfigManager.shared.proxyPortAutoSet) } bridge.registerHandler("setSystemProxy") { anydata, responseCallback in @@ -29,22 +27,22 @@ class JsBridgeUtil { } else { SystemProxyManager.shared.disableProxy() } - responseCallback?(true) + responseCallback(true) } else { - responseCallback?(false) + responseCallback(false) } } bridge.registerHandler("getStartAtLogin") { _, responseCallback in - responseCallback?(LaunchAtLogin.shared.isEnabled) + responseCallback(LaunchAtLogin.shared.isEnabled) } bridge.registerHandler("setStartAtLogin") { anydata, responseCallback in if let enable = anydata as? Bool { LaunchAtLogin.shared.isEnabled = enable - responseCallback?(true) + responseCallback(true) } else { - responseCallback?(false) + responseCallback(false) } } @@ -57,10 +55,10 @@ class JsBridgeUtil { } else { resp = delay } - responseCallback?(resp) + responseCallback(resp) } } else { - responseCallback?(nil) + responseCallback(nil) } } @@ -77,13 +75,12 @@ class JsBridgeUtil { "port": port, "secret": ConfigManager.shared.overrideSecret ?? ConfigManager.shared.apiSecret ] - callback?(data) + callback(data) } // ping-pong - bridge.registerHandler("ping") { [weak bridge] _, responseCallback in - bridge?.callHandler("pong") - responseCallback?(true) + bridge.registerHandler("ping") { _, responseCallback in + responseCallback("pong") } return bridge } diff --git a/ClashX/ViewControllers/ClashWebViewContoller.swift b/ClashX/ViewControllers/ClashWebViewContoller.swift index 63ab61024..ba3723613 100644 --- a/ClashX/ViewControllers/ClashWebViewContoller.swift +++ b/ClashX/ViewControllers/ClashWebViewContoller.swift @@ -10,7 +10,6 @@ import Cocoa import RxCocoa import RxSwift import WebKit -import WebViewJavascriptBridge enum WebCacheCleaner { static func clean() { @@ -27,7 +26,7 @@ enum WebCacheCleaner { class ClashWebViewContoller: NSViewController { let webview: CustomWKWebView = .init() - var bridge: WebViewJavascriptBridge? + var bridge: JSBridge? let disposeBag = DisposeBag() let minSize = NSSize(width: 920, height: 580) @@ -53,15 +52,9 @@ class ClashWebViewContoller: NSViewController { webview.configuration.userContentController.addUserScript(script) bridge = JsBridgeUtil.initJSbridge(webview: webview, delegate: self) - registerExtenalJSBridgeFunction() webview.configuration.preferences.setValue(true, forKey: "developerExtrasEnabled") - NotificationCenter.default.rx.notification(.configFileChange).bind { - [weak self] _ in - self?.bridge?.callHandler("onConfigChange") - }.disposed(by: disposeBag) - NotificationCenter.default.rx.notification(.reloadDashboard).bind { [weak self] _ in self?.webview.reload() @@ -113,17 +106,10 @@ class ClashWebViewContoller: NSViewController { } Logger.log("load dashboard url fail", level: .error) } -} -extension ClashWebViewContoller { - func registerExtenalJSBridgeFunction() { - bridge?.registerHandler("setDragAreaHeight") { - [weak self] anydata, responseCallback in - if let height = anydata as? CGFloat { - self?.webview.dragableAreaHeight = height - } - responseCallback?(nil) - } + func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { + NSAlert.alert(with: message) + completionHandler() } } diff --git a/Podfile b/Podfile index 9f9050b28..03f8981a2 100644 --- a/Podfile +++ b/Podfile @@ -24,7 +24,6 @@ target 'ClashX' do pod 'RxSwift' pod 'RxCocoa' pod 'CocoaLumberjack/Swift' - pod 'WebViewJavascriptBridge' pod 'Starscream','3.1.1' pod 'AppCenter/Analytics' pod 'AppCenter/Crashes' diff --git a/Podfile.lock b/Podfile.lock index 3d45e8a2b..6f45af63b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -22,7 +22,6 @@ PODS: - SwiftFormat/CLI (0.52.3) - SwiftLint (0.52.4) - SwiftyJSON (5.0.1) - - WebViewJavascriptBridge (6.0.3) DEPENDENCIES: - Alamofire (~> 5.0) @@ -39,7 +38,6 @@ DEPENDENCIES: - SwiftFormat/CLI (~> 0.49) - SwiftLint - SwiftyJSON - - WebViewJavascriptBridge SPEC REPOS: trunk: @@ -57,7 +55,6 @@ SPEC REPOS: - SwiftFormat - SwiftLint - SwiftyJSON - - WebViewJavascriptBridge SPEC CHECKSUMS: Alamofire: 0e92e751b3e9e66d7982db43919d01f313b8eb91 @@ -74,8 +71,7 @@ SPEC CHECKSUMS: SwiftFormat: 5de81c42f043741a16e17ae2da012bbddc7c0b58 SwiftLint: 1cc5cd61ba9bacb2194e340aeb47a2a37fda00b3 SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e - WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 -PODFILE CHECKSUM: d3e952c889c7931151a654c076812b3b3294aa59 +PODFILE CHECKSUM: f3a6ebd40f44d7184ef9437d24b1979389395820 COCOAPODS: 1.12.1 From 233bc80b399982e31233a8f00ef32f89ae771e58 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:59:30 +0800 Subject: [PATCH 119/122] misc: update deps --- ClashX/goClash/go.mod | 2 +- ClashX/goClash/go.sum | 4 ++-- Podfile | 4 ++-- Podfile.lock | 42 +++++++++++++++++++++--------------------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/ClashX/goClash/go.mod b/ClashX/goClash/go.mod index 46fc2f8e2..45a7c6663 100644 --- a/ClashX/goClash/go.mod +++ b/ClashX/goClash/go.mod @@ -5,7 +5,7 @@ go 1.21 toolchain go1.21.0 require ( - github.com/Dreamacro/clash v1.18.1-0.20230902125642-db2b5db2c6e2 + github.com/Dreamacro/clash v1.18.1-0.20230911035213-d034a408be42 github.com/oschwald/geoip2-golang v1.9.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 ) diff --git a/ClashX/goClash/go.sum b/ClashX/goClash/go.sum index e3038263d..2250a336a 100644 --- a/ClashX/goClash/go.sum +++ b/ClashX/goClash/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v1.18.1-0.20230902125642-db2b5db2c6e2 h1:M1xQyDbW0SA+uHrTxGbrf04GGbF/08vrWrW+Q5hLaf4= -github.com/Dreamacro/clash v1.18.1-0.20230902125642-db2b5db2c6e2/go.mod h1:r//xe/2pA3Zl+3fjIiI/o6RjIVd+z87drCD58dpRnFg= +github.com/Dreamacro/clash v1.18.1-0.20230911035213-d034a408be42 h1:1gOoRDxOn2wF5u+ku5rfpX5z0vuw1lS4NW0aGvjXnZE= +github.com/Dreamacro/clash v1.18.1-0.20230911035213-d034a408be42/go.mod h1:r//xe/2pA3Zl+3fjIiI/o6RjIVd+z87drCD58dpRnFg= github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k= github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= diff --git a/Podfile b/Podfile index 03f8981a2..243b43b68 100644 --- a/Podfile +++ b/Podfile @@ -8,8 +8,8 @@ post_install do |installer| config.build_settings['SWIFT_VERSION'] = '5' end end - if config.build_settings['MACOSX_DEPLOYMENT_TARGET'] == '' || Gem::Version.new(config.build_settings['MACOSX_DEPLOYMENT_TARGET']) < Gem::Version.new("10.13") - config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.13' + if config.build_settings['MACOSX_DEPLOYMENT_TARGET'] == '' || Gem::Version.new(config.build_settings['MACOSX_DEPLOYMENT_TARGET']) < Gem::Version.new("10.14") + config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.14' end end end diff --git a/Podfile.lock b/Podfile.lock index 6f45af63b..b117f5aed 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Alamofire (5.8.0) - - AppCenter/Analytics (5.0.3): + - AppCenter/Analytics (5.0.4): - AppCenter/Core - - AppCenter/Core (5.0.3) - - AppCenter/Crashes (5.0.3): + - AppCenter/Core (5.0.4) + - AppCenter/Crashes (5.0.4): - AppCenter/Core - CocoaLumberjack/Core (3.8.1) - CocoaLumberjack/Swift (3.8.1): @@ -11,16 +11,16 @@ PODS: - FlexibleDiff (0.0.9) - GzipSwift (5.1.1) - LetsMove (1.25) - - RxCocoa (6.5.0): - - RxRelay (= 6.5.0) - - RxSwift (= 6.5.0) - - RxRelay (6.5.0): - - RxSwift (= 6.5.0) - - RxSwift (6.5.0) - - Sparkle (2.4.2) + - RxCocoa (6.6.0): + - RxRelay (= 6.6.0) + - RxSwift (= 6.6.0) + - RxRelay (6.6.0): + - RxSwift (= 6.6.0) + - RxSwift (6.6.0) + - Sparkle (2.5.1) - Starscream (3.1.1) - - SwiftFormat/CLI (0.52.3) - - SwiftLint (0.52.4) + - SwiftFormat/CLI (0.52.7) + - SwiftLint (0.53.0) - SwiftyJSON (5.0.1) DEPENDENCIES: @@ -58,20 +58,20 @@ SPEC REPOS: SPEC CHECKSUMS: Alamofire: 0e92e751b3e9e66d7982db43919d01f313b8eb91 - AppCenter: a4070ec3d4418b5539067a51f57155012e486ebd + AppCenter: 85c92db0759d2792a65eb61d6842d2e86611a49a CocoaLumberjack: 5c7e64cdb877770859bddec4d3d5a0d7c9299df9 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa LetsMove: 7b9fe44737707d984fbd3f47af46609a9a07b461 - RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b - RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd - RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 - Sparkle: 5ef7097e655c60f4aeb23fd1658fc3e8dd50f4ec + RxCocoa: 44a80de90e25b739b5aeaae3c8c371a32e3343cc + RxRelay: 45eaa5db8ee4fb50e5ebd57deec0159e97fa51e6 + RxSwift: a4b44f7d24599f674deebd1818eab82e58410632 + Sparkle: ce9957501a2655dd4c8264312c6134ff478a777c Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0 - SwiftFormat: 5de81c42f043741a16e17ae2da012bbddc7c0b58 - SwiftLint: 1cc5cd61ba9bacb2194e340aeb47a2a37fda00b3 + SwiftFormat: 2c4785ad647322b41e027b9d4df160aef526a656 + SwiftLint: 5ce4d6a8ff83f1b5fd5ad5dbf30965d35af65e44 SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e -PODFILE CHECKSUM: f3a6ebd40f44d7184ef9437d24b1979389395820 +PODFILE CHECKSUM: 8c6ef0c5999141047a530cd8b74a41d2161ae11b -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 From e5b191c9e7344341a192f1796836d70e7cfff4aa Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:47:37 +0800 Subject: [PATCH 120/122] feat: mark proxy group speedtestable --- ClashX/Models/ClashProxy.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index 0a1748b25..3b8166884 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -114,7 +114,7 @@ class ClashProxy: Codable { guard let resp = enclosingResp, let allProxys = all else { return [] } var proxys = [SpeedtestAbleItem]() for proxy in allProxys { - if let p = resp.proxiesMap[proxy], !ClashProxyType.isProxyGroup(p) { + if let p = resp.proxiesMap[proxy] { if let provider = p.enclosingProvider { proxys.append(.provider(name: p.name, provider: provider.name)) } else { From b3234a4cc7b3d487eccabce215b41633280311f8 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 18 Oct 2023 11:12:22 +0800 Subject: [PATCH 121/122] fix: macos 14 connection dashboard overlap issue --- .../Connections/Views/Cell/ConnectionTextCellView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift index 9f8004f97..e377be807 100644 --- a/ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift +++ b/ClashX/ViewControllers/Connections/Views/Cell/ConnectionTextCellView.swift @@ -24,6 +24,7 @@ class ConnectionTextCellView: NSView, ConnectionCellProtocol { } func setupUI() { + clipsToBounds = true addSubview(label) label.font = NSFont.systemFont(ofSize: 12) label.makeConstraints { From 498c6d79b8bf0ee534457062318c1e168674aee4 Mon Sep 17 00:00:00 2001 From: yicheng <11733500+yichengchen@users.noreply.github.com> Date: Wed, 18 Oct 2023 11:28:00 +0800 Subject: [PATCH 122/122] misc: sometimes dashboard won't popup to front, recall activate command --- ClashX/ClashWindowController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ClashX/ClashWindowController.swift b/ClashX/ClashWindowController.swift index a983af73f..ffc9d66c1 100644 --- a/ClashX/ClashWindowController.swift +++ b/ClashX/ClashWindowController.swift @@ -71,6 +71,8 @@ class ClashWindowController: NSWindowController, NSWindowDe } window?.makeKeyAndOrderFront(self) window?.delegate = self + NSApp.activate(ignoringOtherApps: true) + window?.makeKeyAndOrderFront(nil) } func windowWillClose(_ notification: Notification) {