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 eae9e35b8..daff931f8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,7 +56,10 @@ jobs: - name: build if: startsWith(github.ref, 'refs/tags/') - run: xcodebuild archive -workspace ClashX.xcworkspace -scheme ClashX -archivePath archive/ClashX.xcarchive -showBuildTimingSummary -allowProvisioningUpdates + run: | + xcodebuild -resolvePackageDependencies -project ClashX.xcodeproj + xcodebuild archive -project ClashX.xcodeproj -scheme ClashX\ Meta -archivePath archive/ClashX.xcarchive -showBuildTimingSummary -allowProvisioningUpdates + - name: build-SwiftUI run: | @@ -69,7 +72,7 @@ jobs: run: ditto -c -k --sequesterRsrc --keepParent archive/ClashX.xcarchive/Products/Applications/ClashX\ Meta.app "ClashX Meta.zip" - name: create SwiftUI zip - run: ditto -c -k --sequesterRsrc --keepParent archive/ClashX\ SwiftUI.xcarchive/Products/Applications/ClashX\ Meta.app "ClashX Meta SwiftUI macOS 12.0+.zip" + run: ditto -c -k --sequesterRsrc --keepParent archive/ClashX\ SwiftUI.xcarchive/Products/Applications/ClashX\ Meta.app "ClashX Meta macOS 12.0+.zip" - name: upload Artifact @@ -88,5 +91,5 @@ jobs: generate_release_notes: true files: | ClashX Meta.zip - ClashX Meta SwiftUI macOS 12.0+.zip + ClashX Meta macOS 12.0+.zip 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 SwiftUI.xcodeproj/project.pbxproj b/ClashX SwiftUI.xcodeproj/project.pbxproj index 013322414..626cc6ef1 100644 --- a/ClashX SwiftUI.xcodeproj/project.pbxproj +++ b/ClashX SwiftUI.xcodeproj/project.pbxproj @@ -10,8 +10,9 @@ 011946B62A32F64600502077 /* TunModeSettingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011946B52A32F64500502077 /* TunModeSettingCommand.swift */; }; 011946BA2A334C4900502077 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011946B92A334C4900502077 /* ClashWebViewContoller.swift */; }; 012875E52A2E39CD004FB615 /* ClashX Dashboard in Frameworks */ = {isa = PBXBuildFile; productRef = 012875E42A2E39CD004FB615 /* ClashX Dashboard */; }; - 015A00072A4ABBCC000FD974 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 015A00052A4ABBCC000FD974 /* NSMutableArray+Safe.m */; }; - 015EC9412A4888E400145F7F /* Hotfixs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015EC9402A4888E300145F7F /* Hotfixs.swift */; }; + 0144905C2A8150920056028A /* Combine+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0144905A2A8150920056028A /* Combine+Ext.swift */; }; + 0144905D2A8150920056028A /* SpeedUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0144905B2A8150920056028A /* SpeedUtils.swift */; }; + 0144905F2A8153260056028A /* ClashWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0144905E2A8153250056028A /* ClashWindowController.swift */; }; 015EC9432A48891000145F7F /* NormalMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015EC9422A48891000145F7F /* NormalMenuItemView.swift */; }; 015F1E91288E42A50052B20A /* ClashMetaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015F1E90288E42A50052B20A /* ClashMetaConfig.swift */; }; 015F1E92288E60D30052B20A /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; }; @@ -33,10 +34,10 @@ 01B009AE2854533300B93618 /* geoip.dat.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01B009AC2854533200B93618 /* geoip.dat.gz */; }; 01B009AF2854533300B93618 /* geosite.dat.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01B009AD2854533300B93618 /* geosite.dat.gz */; }; 01B1CB082A2E1EC10073EA34 /* DashboardManagerSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B1CB072A2E1EC10073EA34 /* DashboardManagerSwiftUI.swift */; }; - 01B1CB0C2A2E23F70073EA34 /* LoggerSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B1CB0B2A2E23F70073EA34 /* LoggerSwiftUI.swift */; }; 01B2274B29B845F100FE35C9 /* country.mmdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01B2274A29B845F100FE35C9 /* country.mmdb.gz */; }; 01BC9ABE2928EB5A00F9B177 /* MetaDNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BC9ABD2928E5C600F9B177 /* MetaDNS.swift */; }; 01C1462A28962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01C1462928962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz */; }; + 01C5B88D2A4FD1DB00C4E36A /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C5B88C2A4FD1DB00C4E36A /* Logger.swift */; }; 01E33AB229B5BF4200FD1006 /* NSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E33AB129B5BF4200FD1006 /* NSColor+Extension.swift */; }; 01E33AB529B5C5E400FD1006 /* menu_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 01E33AB429B5C5E300FD1006 /* menu_icon@2x.png */; }; 01EACC702A2F9408001C584B /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 01EACC6F2A2F9408001C584B /* KeyboardShortcuts */; }; @@ -162,9 +163,9 @@ /* Begin PBXFileReference section */ 011946B52A32F64500502077 /* TunModeSettingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunModeSettingCommand.swift; sourceTree = ""; }; 011946B92A334C4900502077 /* ClashWebViewContoller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashWebViewContoller.swift; sourceTree = ""; }; - 015A00052A4ABBCC000FD974 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+Safe.m"; sourceTree = ""; }; - 015A00062A4ABBCC000FD974 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+Safe.h"; sourceTree = ""; }; - 015EC9402A4888E300145F7F /* Hotfixs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hotfixs.swift; sourceTree = ""; }; + 0144905A2A8150920056028A /* Combine+Ext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Combine+Ext.swift"; sourceTree = ""; }; + 0144905B2A8150920056028A /* SpeedUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpeedUtils.swift; sourceTree = ""; }; + 0144905E2A8153250056028A /* ClashWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashWindowController.swift; sourceTree = ""; }; 015EC9422A48891000145F7F /* NormalMenuItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NormalMenuItemView.swift; sourceTree = ""; }; 015F1E90288E42A50052B20A /* ClashMetaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashMetaConfig.swift; sourceTree = ""; }; 0162E74D2864B818007218A6 /* com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h"; sourceTree = ""; }; @@ -176,10 +177,10 @@ 01B009AC2854533200B93618 /* geoip.dat.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = geoip.dat.gz; sourceTree = ""; }; 01B009AD2854533300B93618 /* geosite.dat.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = geosite.dat.gz; sourceTree = ""; }; 01B1CB072A2E1EC10073EA34 /* DashboardManagerSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardManagerSwiftUI.swift; sourceTree = ""; }; - 01B1CB0B2A2E23F70073EA34 /* LoggerSwiftUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggerSwiftUI.swift; sourceTree = ""; }; 01B2274A29B845F100FE35C9 /* country.mmdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = country.mmdb.gz; sourceTree = ""; }; 01BC9ABD2928E5C600F9B177 /* MetaDNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaDNS.swift; sourceTree = ""; }; 01C1462928962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = com.metacubex.ClashX.ProxyConfigHelper.meta.gz; sourceTree = ""; }; + 01C5B88C2A4FD1DB00C4E36A /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 01E33AB129B5BF4200FD1006 /* NSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Extension.swift"; sourceTree = ""; }; 01E33AB429B5C5E300FD1006 /* menu_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icon@2x.png"; sourceTree = ""; }; 01EACC712A2F9469001C584B /* SSIDSuspendTool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSIDSuspendTool.swift; sourceTree = ""; }; @@ -276,8 +277,6 @@ F977FAAB2366790500C17F1F /* AutoUpgardeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoUpgardeManager.swift; sourceTree = ""; }; F977FAAD23669D6400C17F1F /* ConnectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = ""; }; F9A7C0692306E874007163C7 /* com.metacubex.ClashX.ProxyConfigHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = com.metacubex.ClashX.ProxyConfigHelper; sourceTree = BUILT_PRODUCTS_DIR; }; - F9C180A1243C6590005EE8C4 /* goClash.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = goClash.a; path = ClashX/goClash/goClash.a; sourceTree = SOURCE_ROOT; }; - F9C180A2243C6590005EE8C4 /* goClash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = goClash.h; path = ClashX/goClash/goClash.h; sourceTree = SOURCE_ROOT; }; F9E754CF239CC21F00CEE7CC /* WebPortalManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebPortalManager.swift; sourceTree = ""; }; F9E754D1239CC28D00CEE7CC /* NSAlert+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAlert+Extension.swift"; sourceTree = ""; }; F9E8F34523A12B89002DE5E8 /* String+Encode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Encode.swift"; sourceTree = ""; }; @@ -321,15 +320,6 @@ name = Frameworks; sourceTree = ""; }; - 015A00042A4ABBCC000FD974 /* Safe */ = { - isa = PBXGroup; - children = ( - 015A00052A4ABBCC000FD974 /* NSMutableArray+Safe.m */, - 015A00062A4ABBCC000FD974 /* NSMutableArray+Safe.h */, - ); - path = Safe; - sourceTree = ""; - }; 4913C82021157CEB00F6B87C /* Macro */ = { isa = PBXGroup; children = ( @@ -397,7 +387,6 @@ 492C4868210EE6B9004554A0 /* ApiRequest.swift */, 015F1E90288E42A50052B20A /* ClashMetaConfig.swift */, 016BEAAF29D80102001586C5 /* AlphaMetaDownloader.swift */, - 015EC9402A4888E300145F7F /* Hotfixs.swift */, ); path = General; sourceTree = ""; @@ -441,7 +430,6 @@ children = ( 4929F683258CE07500A435F6 /* UserDefaultWrapper.swift */, F976275D23634F18000EDEFE /* LoginServiceKit */, - 015A00042A4ABBCC000FD974 /* Safe */, 49722FE9211F338B00650A41 /* Witness */, 49D8276627E9B01700159D93 /* LoginKitWrapper.h */, 49D8276727E9B01700159D93 /* LoginKitWrapper.m */, @@ -502,11 +490,13 @@ isa = PBXGroup; children = ( 495A44D220D267D000888A0A /* LaunchAtLogin.swift */, - 01B1CB0B2A2E23F70073EA34 /* LoggerSwiftUI.swift */, + 01C5B88C2A4FD1DB00C4E36A /* Logger.swift */, 4966E9E5211824F300A391FB /* NSImage+extension.swift */, 49B10869216A356D0064FFCE /* String+Extension.swift */, 49ABB748236B0F9E00535CD7 /* UnsafePointer+bridge.swift */, 01EACC772A2F94C7001C584B /* NSView+Layout.swift */, + 0144905B2A8150920056028A /* SpeedUtils.swift */, + 0144905A2A8150920056028A /* Combine+Ext.swift */, ); path = Basic; sourceTree = ""; @@ -562,6 +552,7 @@ 49CF3B2A20CD7465001EBF94 /* ClashX.entitlements */, 49CF3B3520CD75DF001EBF94 /* ClashX-Bridging-Header.h */, F9FAB31D262BE04800DE02A6 /* Images.xcassets */, + 0144905E2A8153250056028A /* ClashWindowController.swift */, ); path = ClashX; sourceTree = ""; @@ -569,8 +560,6 @@ 49CF3B3A20CD783A001EBF94 /* Support Files */ = { isa = PBXGroup; children = ( - F9C180A1243C6590005EE8C4 /* goClash.a */, - F9C180A2243C6590005EE8C4 /* goClash.h */, 4981C88D216BAE4A008CC14A /* Localizable.strings */, ); path = "Support Files"; @@ -762,6 +751,7 @@ 49ABB749236B0F9E00535CD7 /* UnsafePointer+bridge.swift in Sources */, 01EACC762A2F94AB001C584B /* GlobalShortCutViewController.swift in Sources */, 499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */, + 0144905C2A8150920056028A /* Combine+Ext.swift in Sources */, 49D6A45229AEEC15006487EF /* StatusItemTool.swift in Sources */, 49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */, 4952C3D02117027C004A4FA8 /* ConfigFileManager.swift in Sources */, @@ -773,7 +763,6 @@ F915A4622366ADEF004840BE /* ClashConnection.swift in Sources */, F977FAAE23669D6400C17F1F /* ConnectionManager.swift in Sources */, 495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */, - 01B1CB0C2A2E23F70073EA34 /* LoggerSwiftUI.swift in Sources */, F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */, 495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */, 01EACC722A2F9469001C584B /* SSIDSuspendTool.swift in Sources */, @@ -787,6 +776,7 @@ 016BEAB029D80103001586C5 /* AlphaMetaDownloader.swift in Sources */, 49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */, 4913C82321157D0200F6B87C /* Notification.swift in Sources */, + 0144905D2A8150920056028A /* SpeedUtils.swift in Sources */, 015F1E91288E42A50052B20A /* ClashMetaConfig.swift in Sources */, 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */, 498BC2552929CCAE00CA8084 /* GeneralSettingViewController.swift in Sources */, @@ -799,10 +789,10 @@ 492C4871210EF62E004554A0 /* ClashConfig.swift in Sources */, 492C4869210EE6B9004554A0 /* ApiRequest.swift in Sources */, 49CF3B6520CEE06C001EBF94 /* ConfigManager.swift in Sources */, + 0144905F2A8153260056028A /* ClashWindowController.swift in Sources */, F9E754D0239CC21F00CEE7CC /* WebPortalManager.swift in Sources */, 018F88F9286DD0CB004DD0F7 /* DualTitleMenuItem.swift in Sources */, 495BFB8821919B9800C8779D /* RemoteConfigManager.swift in Sources */, - 015EC9412A4888E400145F7F /* Hotfixs.swift in Sources */, 4982F51F2344A216008804B0 /* Cgo+Convert.swift in Sources */, 8ACD21BB27A04C7800BC4632 /* ProxySettingCommand.swift in Sources */, 49722FF1211F338B00650A41 /* Witness.swift in Sources */, @@ -814,7 +804,6 @@ 4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */, 49D6A45429AEEC3C006487EF /* NewStatusItemView.swift in Sources */, 015F1E92288E60D30052B20A /* MetaTask.swift in Sources */, - 015A00072A4ABBCC000FD974 /* NSMutableArray+Safe.m in Sources */, F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */, 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */, 49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */, @@ -826,6 +815,7 @@ F976275C23634DF8000EDEFE /* LoginServiceKit.swift in Sources */, 4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */, 011946B62A32F64600502077 /* TunModeSettingCommand.swift in Sources */, + 01C5B88D2A4FD1DB00C4E36A /* Logger.swift in Sources */, F92D0B2E236D35C000575E15 /* ProxyItemView.swift in Sources */, 49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */, 49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */, @@ -1280,7 +1270,7 @@ repositoryURL = "https://github.com/1024jp/GzipSwift"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.0.0; + minimumVersion = 6.0.0; }; }; 0192313629DD490400539EDD /* XCRemoteSwiftPackageReference "Yams" */ = { diff --git a/ClashX SwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ClashX SwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e3d2ce642..d2a26b42a 100644 --- a/ClashX SwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ClashX SwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/1024jp/GzipSwift", "state" : { - "revision" : "7a7f17761c76a932662ab77028a4329f67d645a4", - "version" : "5.2.0" + "revision" : "731037f6cc2be2ec01562f6597c1d0aa3fe6fd05", + "version" : "6.0.1" } }, { diff --git a/ClashX SwiftUI.xcodeproj/xcshareddata/xcschemes/com.metacubex.ClashX.ProxyConfigHelper.xcscheme b/ClashX SwiftUI.xcodeproj/xcshareddata/xcschemes/com.metacubex.ClashX.ProxyConfigHelper.xcscheme new file mode 100644 index 000000000..58aa08473 --- /dev/null +++ b/ClashX SwiftUI.xcodeproj/xcshareddata/xcschemes/com.metacubex.ClashX.ProxyConfigHelper.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index ee8db05d6..3a59d9f9b 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -3,10 +3,21 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 53; objects = { /* Begin PBXBuildFile section */ + 015B976A2A4F2F4500F9FA4D /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97692A4F2F4500F9FA4D /* Alamofire */; }; + 015B976D2A4F2F6C00F9FA4D /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 015B976C2A4F2F6C00F9FA4D /* RxCocoa */; }; + 015B976F2A4F2F6C00F9FA4D /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 015B976E2A4F2F6C00F9FA4D /* RxSwift */; }; + 015B97722A4F2F9900F9FA4D /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97712A4F2F9900F9FA4D /* SwiftyJSON */; }; + 015B97752A4F2FC600F9FA4D /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97742A4F2FC600F9FA4D /* CocoaLumberjack */; }; + 015B97772A4F2FC600F9FA4D /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97762A4F2FC600F9FA4D /* CocoaLumberjackSwift */; }; + 015B977A2A4F306800F9FA4D /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97792A4F306800F9FA4D /* Starscream */; }; + 015B977D2A4F30B900F9FA4D /* FlexibleDiff in Frameworks */ = {isa = PBXBuildFile; productRef = 015B977C2A4F30B900F9FA4D /* FlexibleDiff */; }; + 015B97802A4F30EA00F9FA4D /* Gzip in Frameworks */ = {isa = PBXBuildFile; productRef = 015B977F2A4F30EA00F9FA4D /* Gzip */; }; + 015B97832A4F311300F9FA4D /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97822A4F311300F9FA4D /* Yams */; }; + 015B97862A4F31AF00F9FA4D /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = 015B97852A4F31AF00F9FA4D /* PromiseKit */; }; 015F1E91288E42A50052B20A /* ClashMetaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015F1E90288E42A50052B20A /* ClashMetaConfig.swift */; }; 015F1E92288E60D30052B20A /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; }; 0162E74F2864B819007218A6 /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; }; @@ -22,7 +33,6 @@ 01C1462A28962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01C1462928962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz */; }; 01E33AB229B5BF4200FD1006 /* NSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E33AB129B5BF4200FD1006 /* NSColor+Extension.swift */; }; 01E33AB529B5C5E400FD1006 /* menu_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 01E33AB429B5C5E300FD1006 /* menu_icon@2x.png */; }; - 185CBAEDFE986E6E1B836359 /* libPods-ClashX Meta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA63125BBF03DC1A291D3351 /* libPods-ClashX Meta.a */; }; 275348502A3082FD0077B458 /* TunModeSettingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2753484F2A3082FD0077B458 /* TunModeSettingCommand.swift */; }; 4905A2C52A2058B000AEDA2E /* GlobalShortCutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4905A2C42A2058B000AEDA2E /* GlobalShortCutViewController.swift */; }; 4905A2C82A2058D400AEDA2E /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */; }; @@ -46,7 +56,6 @@ 495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495340B220DE68C300B0D3FF /* StatusItemView.swift */; }; 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 */; }; 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 */; }; 496BDEE021196F1E00C5207F /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496BDEDF21196F1E00C5207F /* Logger.swift */; }; @@ -61,9 +70,9 @@ 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 */; }; + 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 */; }; - 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 */; }; @@ -81,6 +90,7 @@ 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 */; }; + 49CCDA2A2A54F9AC00FF1E13 /* ClashWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CCDA292A54F9AC00FF1E13 /* ClashWindowController.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 */; }; @@ -142,7 +152,7 @@ F9A7C0672306E874007163C7 /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; + dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( ); @@ -168,7 +178,6 @@ 01E33AB129B5BF4200FD1006 /* NSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Extension.swift"; sourceTree = ""; }; 01E33AB429B5C5E300FD1006 /* menu_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icon@2x.png"; sourceTree = ""; }; 2753484F2A3082FD0077B458 /* TunModeSettingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunModeSettingCommand.swift; sourceTree = ""; }; - 3F86DA2DA3CC14731BE1ABF7 /* Pods-ClashX Meta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX Meta.release.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX Meta/Pods-ClashX Meta.release.xcconfig"; 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; }; @@ -191,7 +200,6 @@ 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 = ""; }; - 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 = ""; }; 496BDEDF21196F1E00C5207F /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; @@ -211,10 +219,9 @@ 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 = ""; }; + 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 = ""; }; - 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 = ""; }; @@ -232,6 +239,7 @@ 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 = ""; }; + 49CCDA292A54F9AC00FF1E13 /* ClashWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashWindowController.swift; sourceTree = ""; }; 49CF3B1D20CD7463001EBF94 /* ClashX Meta.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ClashX Meta.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 = ""; }; @@ -249,12 +257,9 @@ 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 = ""; }; 8A2BBEA627A03ACB0081EBEF /* ProxySetting.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = ProxySetting.sdef; sourceTree = ""; }; 8ACD21BA27A04C7800BC4632 /* ProxySettingCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxySettingCommand.swift; sourceTree = ""; }; 8ACD21BC27A04ED500BC4632 /* ProxyModeChangeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyModeChangeCommand.swift; sourceTree = ""; }; - A1485BCE642059532D01B8BA /* Pods-ClashX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.release.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.release.xcconfig"; sourceTree = ""; }; - AA63125BBF03DC1A291D3351 /* libPods-ClashX Meta.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ClashX Meta.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F910AA23240134AF00116E95 /* ProxyGroupMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupMenu.swift; sourceTree = ""; }; F915A4612366ADEF004840BE /* ClashConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashConnection.swift; sourceTree = ""; }; F9203A25236342820020D57D /* AppDelegate+..swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+..swift"; sourceTree = ""; }; @@ -280,7 +285,6 @@ F9E754D1239CC28D00CEE7CC /* NSAlert+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAlert+Extension.swift"; sourceTree = ""; }; F9E8F34523A12B89002DE5E8 /* String+Encode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Encode.swift"; sourceTree = ""; }; F9FAB31D262BE04800DE02A6 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - FA2FD2FAF2E12CDFC01E72D3 /* Pods-ClashX Meta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX Meta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX Meta/Pods-ClashX Meta.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -288,9 +292,19 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 015B976F2A4F2F6C00F9FA4D /* RxSwift in Frameworks */, + 015B97722A4F2F9900F9FA4D /* SwiftyJSON in Frameworks */, + 015B97802A4F30EA00F9FA4D /* Gzip in Frameworks */, + 015B97772A4F2FC600F9FA4D /* CocoaLumberjackSwift in Frameworks */, + 015B977A2A4F306800F9FA4D /* Starscream in Frameworks */, + 015B97862A4F31AF00F9FA4D /* PromiseKit in Frameworks */, 4905A2C82A2058D400AEDA2E /* KeyboardShortcuts in Frameworks */, + 015B976A2A4F2F4500F9FA4D /* Alamofire in Frameworks */, + 015B97752A4F2FC600F9FA4D /* CocoaLumberjack in Frameworks */, + 015B97832A4F311300F9FA4D /* Yams in Frameworks */, + 015B976D2A4F2F6C00F9FA4D /* RxCocoa in Frameworks */, + 015B977D2A4F30B900F9FA4D /* FlexibleDiff in Frameworks */, 4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */, - 185CBAEDFE986E6E1B836359 /* libPods-ClashX Meta.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -334,7 +348,6 @@ 491C250021BD55B900AB5D44 /* Utils */ = { isa = PBXGroup; children = ( - 4960A6DA2136529200B940C9 /* JSBridgeHandler.swift */, 493AEAE2221AE3420016FE98 /* AppVersionUtil.swift */, 49D176A62355FE680093DD7B /* NetworkChangeNotifier.swift */, 49B445152457CDF000B27E3E /* ClashStatusTool.swift */, @@ -370,7 +383,6 @@ 491C250121BD561200AB5D44 /* Managers */, 491C250021BD55B900AB5D44 /* Utils */, 492C4868210EE6B9004554A0 /* ApiRequest.swift */, - 4994B5552A47C5FA00E595B9 /* Hotfixs.swift */, 015F1E90288E42A50052B20A /* ClashMetaConfig.swift */, 016BEAAF29D80102001586C5 /* AlphaMetaDownloader.swift */, ); @@ -414,7 +426,6 @@ 49722FDD211ED2A900650A41 /* Vendor */ = { isa = PBXGroup; children = ( - 4994B54F2A47C1A800E595B9 /* Safe */, 4929F683258CE07500A435F6 /* UserDefaultWrapper.swift */, F976275D23634F18000EDEFE /* LoginServiceKit */, 49722FE9211F338B00650A41 /* Witness */, @@ -473,15 +484,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 = ( @@ -491,6 +493,8 @@ 4905A2C92A20841B00AEDA2E /* NSView+Layout.swift */, 49B10869216A356D0064FFCE /* String+Extension.swift */, 49ABB748236B0F9E00535CD7 /* UnsafePointer+bridge.swift */, + 4991D22F2A564DDC00978143 /* SpeedUtils.swift */, + 4991D2312A565E6A00978143 /* Combine+Ext.swift */, ); path = Basic; sourceTree = ""; @@ -513,7 +517,6 @@ 49CF3B1F20CD7463001EBF94 /* ClashX */, F9A7C06A2306E874007163C7 /* ProxyConfigHelper */, 49CF3B1E20CD7463001EBF94 /* Products */, - 76229F122B00E935D126742A /* Pods */, CF1AC9FACC36FCE7663C5583 /* Frameworks */, ); sourceTree = ""; @@ -547,6 +550,7 @@ 49CF3B2A20CD7465001EBF94 /* ClashX.entitlements */, 49CF3B3520CD75DF001EBF94 /* ClashX-Bridging-Header.h */, F9FAB31D262BE04800DE02A6 /* Images.xcassets */, + 49CCDA292A54F9AC00FF1E13 /* ClashWindowController.swift */, ); path = ClashX; sourceTree = ""; @@ -559,17 +563,6 @@ path = "Support Files"; sourceTree = ""; }; - 76229F122B00E935D126742A /* Pods */ = { - isa = PBXGroup; - children = ( - 5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */, - A1485BCE642059532D01B8BA /* Pods-ClashX.release.xcconfig */, - FA2FD2FAF2E12CDFC01E72D3 /* Pods-ClashX Meta.debug.xcconfig */, - 3F86DA2DA3CC14731BE1ABF7 /* Pods-ClashX Meta.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; 8A2BBEA527A03A920081EBEF /* AppleScript */ = { isa = PBXGroup; children = ( @@ -585,7 +578,6 @@ isa = PBXGroup; children = ( 4908087A29F8F3FF007A4944 /* libresolv.tbd */, - AA63125BBF03DC1A291D3351 /* libPods-ClashX Meta.a */, ); name = Frameworks; sourceTree = ""; @@ -626,7 +618,6 @@ isa = PBXNativeTarget; buildConfigurationList = 49CF3B2D20CD7465001EBF94 /* Build configuration list for PBXNativeTarget "ClashX Meta" */; buildPhases = ( - 2FD76081F5FF64514C3E5C9D /* [CP] Check Pods Manifest.lock */, 49CF3B1920CD7463001EBF94 /* Sources */, 49CF3B1A20CD7463001EBF94 /* Frameworks */, 49CF3B1B20CD7463001EBF94 /* Resources */, @@ -640,6 +631,17 @@ name = "ClashX Meta"; packageProductDependencies = ( 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */, + 015B97692A4F2F4500F9FA4D /* Alamofire */, + 015B976C2A4F2F6C00F9FA4D /* RxCocoa */, + 015B976E2A4F2F6C00F9FA4D /* RxSwift */, + 015B97712A4F2F9900F9FA4D /* SwiftyJSON */, + 015B97742A4F2FC600F9FA4D /* CocoaLumberjack */, + 015B97762A4F2FC600F9FA4D /* CocoaLumberjackSwift */, + 015B97792A4F306800F9FA4D /* Starscream */, + 015B977C2A4F30B900F9FA4D /* FlexibleDiff */, + 015B977F2A4F30EA00F9FA4D /* Gzip */, + 015B97822A4F311300F9FA4D /* Yams */, + 015B97852A4F31AF00F9FA4D /* PromiseKit */, ); productName = ClashX; productReference = 49CF3B1D20CD7463001EBF94 /* ClashX Meta.app */; @@ -704,6 +706,15 @@ mainGroup = 49CF3B1420CD7463001EBF94; packageReferences = ( 4905A2C62A2058D400AEDA2E /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */, + 015B97682A4F2F4500F9FA4D /* XCRemoteSwiftPackageReference "Alamofire" */, + 015B976B2A4F2F6C00F9FA4D /* XCRemoteSwiftPackageReference "RxSwift" */, + 015B97702A4F2F9900F9FA4D /* XCRemoteSwiftPackageReference "SwiftyJSON" */, + 015B97732A4F2FC600F9FA4D /* XCRemoteSwiftPackageReference "CocoaLumberjack" */, + 015B97782A4F306800F9FA4D /* XCRemoteSwiftPackageReference "Starscream" */, + 015B977B2A4F30B900F9FA4D /* XCRemoteSwiftPackageReference "FlexibleDiff" */, + 015B977E2A4F30EA00F9FA4D /* XCRemoteSwiftPackageReference "GzipSwift" */, + 015B97812A4F311300F9FA4D /* XCRemoteSwiftPackageReference "Yams" */, + 015B97842A4F31AE00F9FA4D /* XCRemoteSwiftPackageReference "PromiseKit" */, ); productRefGroup = 49CF3B1E20CD7463001EBF94 /* Products */; projectDirPath = ""; @@ -738,27 +749,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 2FD76081F5FF64514C3E5C9D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ClashX Meta-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 49CF3B1920CD7463001EBF94 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -780,11 +770,14 @@ 495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */, F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */, 495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */, + 4991D2322A565E6A00978143 /* Combine+Ext.swift in Sources */, 49281C802A1F01FA00F60935 /* DebugSettingViewController.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 */, + 4991D2302A564DDC00978143 /* SpeedUtils.swift in Sources */, 496BDEE021196F1E00C5207F /* Logger.swift in Sources */, 4905A2CA2A20841B00AEDA2E /* NSView+Layout.swift in Sources */, 49722FEF211F338B00650A41 /* FileEvent.swift in Sources */, @@ -824,7 +817,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 */, @@ -837,12 +829,10 @@ F92D0B2C236C7C3600575E15 /* MenuItemBaseView.swift in Sources */, 499ADAFD2498CC5900C488FE /* RemoteControlManager.swift in Sources */, 49769FB427E9B3E400E3D664 /* LoginKitWrapper.m in Sources */, - 4960A6DB2136529200B940C9 /* JSBridgeHandler.swift in Sources */, 493AEAE5221AE7230016FE98 /* ProxyMenuItem.swift in Sources */, 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 */, 01943259287D19BC008CC51A /* ClashRuleProvider.swift in Sources */, 49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */, @@ -1020,7 +1010,6 @@ }; 49CF3B2E20CD7465001EBF94 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FA2FD2FAF2E12CDFC01E72D3 /* Pods-ClashX Meta.debug.xcconfig */; buildSettings = { ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1065,7 +1054,6 @@ }; 49CF3B2F20CD7465001EBF94 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3F86DA2DA3CC14731BE1ABF7 /* Pods-ClashX Meta.release.xcconfig */; buildSettings = { ARCHS = "$(ARCHS_STANDARD)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1228,6 +1216,78 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 015B97682A4F2F4500F9FA4D /* XCRemoteSwiftPackageReference "Alamofire" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Alamofire/Alamofire"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; + 015B976B2A4F2F6C00F9FA4D /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.0.0; + }; + }; + 015B97702A4F2F9900F9FA4D /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; + 015B97732A4F2FC600F9FA4D /* XCRemoteSwiftPackageReference "CocoaLumberjack" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/CocoaLumberjack/CocoaLumberjack"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; + 015B97782A4F306800F9FA4D /* XCRemoteSwiftPackageReference "Starscream" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/daltoniam/Starscream"; + requirement = { + kind = exactVersion; + version = 3.1.1; + }; + }; + 015B977B2A4F30B900F9FA4D /* XCRemoteSwiftPackageReference "FlexibleDiff" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RACCommunity/FlexibleDiff"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.0.9; + }; + }; + 015B977E2A4F30EA00F9FA4D /* XCRemoteSwiftPackageReference "GzipSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/1024jp/GzipSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.0.0; + }; + }; + 015B97812A4F311300F9FA4D /* XCRemoteSwiftPackageReference "Yams" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/jpsim/Yams"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; + 015B97842A4F31AE00F9FA4D /* XCRemoteSwiftPackageReference "PromiseKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/mxcl/PromiseKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 8.0.0; + }; + }; 4905A2C62A2058D400AEDA2E /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/sindresorhus/KeyboardShortcuts.git"; @@ -1239,6 +1299,61 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 015B97692A4F2F4500F9FA4D /* Alamofire */ = { + isa = XCSwiftPackageProductDependency; + package = 015B97682A4F2F4500F9FA4D /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = Alamofire; + }; + 015B976C2A4F2F6C00F9FA4D /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 015B976B2A4F2F6C00F9FA4D /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + 015B976E2A4F2F6C00F9FA4D /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 015B976B2A4F2F6C00F9FA4D /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + 015B97712A4F2F9900F9FA4D /* SwiftyJSON */ = { + isa = XCSwiftPackageProductDependency; + package = 015B97702A4F2F9900F9FA4D /* XCRemoteSwiftPackageReference "SwiftyJSON" */; + productName = SwiftyJSON; + }; + 015B97742A4F2FC600F9FA4D /* CocoaLumberjack */ = { + isa = XCSwiftPackageProductDependency; + package = 015B97732A4F2FC600F9FA4D /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; + productName = CocoaLumberjack; + }; + 015B97762A4F2FC600F9FA4D /* CocoaLumberjackSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 015B97732A4F2FC600F9FA4D /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; + productName = CocoaLumberjackSwift; + }; + 015B97792A4F306800F9FA4D /* Starscream */ = { + isa = XCSwiftPackageProductDependency; + package = 015B97782A4F306800F9FA4D /* XCRemoteSwiftPackageReference "Starscream" */; + productName = Starscream; + }; + 015B977C2A4F30B900F9FA4D /* FlexibleDiff */ = { + isa = XCSwiftPackageProductDependency; + package = 015B977B2A4F30B900F9FA4D /* XCRemoteSwiftPackageReference "FlexibleDiff" */; + productName = FlexibleDiff; + }; + 015B977F2A4F30EA00F9FA4D /* Gzip */ = { + isa = XCSwiftPackageProductDependency; + package = 015B977E2A4F30EA00F9FA4D /* XCRemoteSwiftPackageReference "GzipSwift" */; + productName = Gzip; + }; + 015B97822A4F311300F9FA4D /* Yams */ = { + isa = XCSwiftPackageProductDependency; + package = 015B97812A4F311300F9FA4D /* XCRemoteSwiftPackageReference "Yams" */; + productName = Yams; + }; + 015B97852A4F31AF00F9FA4D /* PromiseKit */ = { + isa = XCSwiftPackageProductDependency; + package = 015B97842A4F31AE00F9FA4D /* XCRemoteSwiftPackageReference "PromiseKit" */; + productName = PromiseKit; + }; 4905A2C72A2058D400AEDA2E /* KeyboardShortcuts */ = { isa = XCSwiftPackageProductDependency; package = 4905A2C62A2058D400AEDA2E /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */; diff --git a/ClashX.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ClashX.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/ClashX.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ClashX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ClashX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7555ef43f..0a26aa18a 100644 --- a/ClashX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ClashX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Alamofire/Alamofire", "state" : { - "revision" : "78424be314842833c04bc3bef5b72e85fff99204", - "version" : "5.6.4" + "revision" : "bc268c28fb170f494de9e9927c371b8342979ece", + "version" : "5.7.1" } }, { @@ -18,24 +18,6 @@ "version" : "3.8.0" } }, - { - "identity" : "differencekit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ra1028/DifferenceKit.git", - "state" : { - "revision" : "073b9671ce2b9b5b96398611427a1f929927e428", - "version" : "1.3.0" - } - }, - { - "identity" : "dsfsparkline", - "kind" : "remoteSourceControl", - "location" : "https://github.com/dagronf/DSFSparkline.git", - "state" : { - "revision" : "cfcf80b9e214500cbc12b795b7c9943151b8bd4f", - "version" : "4.6.4" - } - }, { "identity" : "flexiblediff", "kind" : "remoteSourceControl", @@ -50,8 +32,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/1024jp/GzipSwift", "state" : { - "revision" : "7a7f17761c76a932662ab77028a4329f67d645a4", - "version" : "5.2.0" + "revision" : "731037f6cc2be2ec01562f6597c1d0aa3fe6fd05", + "version" : "6.0.1" + } + }, + { + "identity" : "keyboardshortcuts", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/KeyboardShortcuts.git", + "state" : { + "revision" : "018e445b340dac78dfc2a4c97c45d9571507c3b5", + "version" : "1.11.0" } }, { @@ -68,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mxcl/PromiseKit", "state" : { - "revision" : "8a98e31a47854d3180882c8068cc4d9381bf382d", - "version" : "6.22.1" + "revision" : "143091e1b8a0e186b8eba5aa2a38f4dc49ec73ac", + "version" : "8.0.0" } }, { @@ -86,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ReactiveX/RxSwift", "state" : { - "revision" : "b4307ba0b6425c0ba4178e138799946c3da594f8", - "version" : "6.5.0" + "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version" : "6.6.0" } }, { @@ -117,15 +108,6 @@ "version" : "1.0.0" } }, - { - "identity" : "swiftui-introspect", - "kind" : "remoteSourceControl", - "location" : "https://github.com/siteline/SwiftUI-Introspect.git", - "state" : { - "revision" : "5b3f3996c7a2a84d5f4ba0e03cd7d584154778f2", - "version" : "0.3.1" - } - }, { "identity" : "swiftyjson", "kind" : "remoteSourceControl", @@ -140,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/jpsim/Yams", "state" : { - "revision" : "f47ba4838c30dbd59998a4e4c87ab620ff959e8a", - "version" : "5.0.5" + "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", + "version" : "5.0.6" } } ], diff --git a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX Meta.xcscheme similarity index 87% rename from ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme rename to ClashX.xcodeproj/xcshareddata/xcschemes/ClashX Meta.xcscheme index c92c795ba..d067dba76 100644 --- a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX.xcscheme +++ b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX Meta.xcscheme @@ -1,6 +1,6 @@ - - - - diff --git a/ClashX.xcodeproj/xcshareddata/xcschemes/com.west2online.ClashX.ProxyConfigHelper.xcscheme b/ClashX.xcodeproj/xcshareddata/xcschemes/com.metacubex.ClashX.ProxyConfigHelper.xcscheme similarity index 96% rename from ClashX.xcodeproj/xcshareddata/xcschemes/com.west2online.ClashX.ProxyConfigHelper.xcscheme rename to ClashX.xcodeproj/xcshareddata/xcschemes/com.metacubex.ClashX.ProxyConfigHelper.xcscheme index 75d66f881..511b4d79a 100644 --- a/ClashX.xcodeproj/xcshareddata/xcschemes/com.west2online.ClashX.ProxyConfigHelper.xcscheme +++ b/ClashX.xcodeproj/xcshareddata/xcschemes/com.metacubex.ClashX.ProxyConfigHelper.xcscheme @@ -1,6 +1,6 @@ + allowLocationSimulation = "YES" + viewDebuggingEnabled = "No"> - - - - - - diff --git a/ClashX.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ClashX.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/ClashX.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 3aa64d11b..000000000 --- a/ClashX.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "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 b30ee4cb2..6578c8d85 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -47,8 +47,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet var showProxyGroupCurrentMenuItem: NSMenuItem! @IBOutlet var copyExportCommandMenuItem: NSMenuItem! @IBOutlet var copyExportCommandExternalMenuItem: NSMenuItem! - @IBOutlet var experimentalMenu: NSMenu! @IBOutlet var externalControlSeparator: NSMenuItem! + @IBOutlet var connectionsMenuItem: NSMenuItem! @IBOutlet var tunModeMenuItem: NSMenuItem! @@ -121,11 +121,17 @@ 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() ICloudManager.shared.setup() - setupExperimentalMenuItem() + + if WebPortalManager.hasWebProtal { + WebPortalManager.shared.addWebProtalMenuItem(&statusMenu) + } + AutoUpgardeManager.shared.setup() + AutoUpgardeManager.shared.setupCheckForUpdatesMenuItem(checkForUpdateMenuItem) // install proxy helper _ = ClashResourceManager.check() @@ -149,7 +155,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { setupNetworkNotifier() registCrashLogger() KeyboardShortCutManager.setup() - Hotfixs.applyMacOS14Hotfix(modeItem: proxyModeMenuItem) + RemoteControlManager.setupMenuItem(separator: externalControlSeparator) } func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { @@ -363,20 +369,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { ConfigManager.shared.isTunModeVariable.accept(config.tun.enable) }.disposed(by: disposeBag) - if !PrivilegedHelperManager.shared.isHelperCheckFinished.value { - proxySettingMenuItem.target = nil - tunModeMenuItem.target = nil - PrivilegedHelperManager.shared.isHelperCheckFinished - .filter({$0}) - .take(1) - .observe(on: MainScheduler.instance) - .subscribe { [weak self] _ in - guard let self = self else { return } - self.proxySettingMenuItem.target = self - self.tunModeMenuItem.target = self - }.disposed(by: disposeBag) - } - // start proxy PrivilegedHelperManager.shared.isHelperReady .filter({$0}) @@ -400,7 +392,23 @@ class AppDelegate: NSObject, NSApplicationDelegate { PrivilegedHelperManager.shared.isHelperReady.accept(true) } } - helperStatusTimer?.fire() + + if !PrivilegedHelperManager.shared.isHelperCheckFinished.value { + proxySettingMenuItem.target = nil + tunModeMenuItem.target = nil + PrivilegedHelperManager.shared.isHelperCheckFinished + .filter({$0}) + .take(1) + .observe(on: MainScheduler.instance) + .subscribe { [weak self] _ in + guard let self = self else { return } + self.proxySettingMenuItem.target = self + self.tunModeMenuItem.target = self + + self.helperStatusTimer?.fire() + }.disposed(by: disposeBag) + } + Logger.log("Fire helperStatusTimer") } @@ -489,7 +497,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.. - + - + - + @@ -97,7 +97,7 @@ - + @@ -254,13 +254,13 @@ - + - + - + @@ -287,7 +287,7 @@ - + @@ -319,6 +319,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -326,7 +389,7 @@ - + @@ -334,7 +397,7 @@ - + @@ -342,7 +405,7 @@ - + @@ -353,7 +416,7 @@ - + @@ -371,7 +434,7 @@ + + - + @@ -1709,8 +1774,39 @@ + + + + + + + + + + + + + + + + + + + + + + - + @@ -1741,7 +1837,7 @@ - + @@ -1784,7 +1880,7 @@ - + @@ -1814,9 +1910,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -1824,6 +1949,10 @@ + + + + @@ -1831,6 +1960,10 @@ + + + + @@ -1851,7 +1984,10 @@ + + + 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/Logger.swift b/ClashX/Basic/Logger.swift index fa9f5279d..e095c05f0 100644 --- a/ClashX/Basic/Logger.swift +++ b/ClashX/Basic/Logger.swift @@ -6,7 +6,7 @@ // Copyright © 2018年 yichengchen. All rights reserved. // -import CocoaLumberjack +import CocoaLumberjackSwift import Foundation class Logger { static let shared = Logger() diff --git a/ClashX/Basic/LoggerSwiftUI.swift b/ClashX/Basic/LoggerSwiftUI.swift deleted file mode 100644 index ee8816891..000000000 --- a/ClashX/Basic/LoggerSwiftUI.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Logger.swift -// ClashX -// -// Created by CYC on 2018/8/7. -// Copyright © 2018年 yichengchen. All rights reserved. -// - -import CocoaLumberjackSwift -import Foundation -class Logger { - static let shared = Logger() - var fileLogger: DDFileLogger = DDFileLogger() - - private init() { - #if DEBUG - DDLog.add(DDOSLogger.sharedInstance) - #endif - // default time zone is "UTC" - let dataFormatter = DateFormatter() - dataFormatter.setLocalizedDateFormatFromTemplate("YYYY/MM/dd HH:mm:ss:SSS") - fileLogger.logFormatter = DDLogFileFormatterDefault.init(dateFormatter: dataFormatter) - 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) { - switch level { - case .debug, .silent: - DDLogDebug(msg) - case .error: - DDLogError(msg) - case .info: - DDLogInfo(msg) - case .warning: - DDLogWarn(msg) - case .unknow: - DDLogWarn(msg) - } - } - - static func log(_ msg: String, level: ClashLogLevel = .info) { - shared.logToFile(msg: "[\(level.rawValue)] \(msg)", level: level) - } - - func logFilePath() -> String { - return fileLogger.logFileManager.sortedLogFilePaths.first ?? "" - } - - func logFolder() -> String { - return fileLogger.logFileManager.logsDirectory - } -} 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 45402ce6d..20f425cfe 100644 --- a/ClashX/ClashX-Bridging-Header.h +++ b/ClashX/ClashX-Bridging-Header.h @@ -4,3 +4,5 @@ #import "ProxyConfigRemoteProcessProtocol.h" #import "LoginKitWrapper.h" +#import +#import diff --git a/ClashX/Extensions/DateFormatter+.swift b/ClashX/Extensions/DateFormatter+.swift index 22aa80a61..24081aaf5 100644 --- a/ClashX/Extensions/DateFormatter+.swift +++ b/ClashX/Extensions/DateFormatter+.swift @@ -16,6 +16,12 @@ extension DateFormatter { return dateFormatter } + static var simple: DateFormatter { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "MM-dd HH:mm:ss" + return dateFormatter + } + static var provider: DateFormatter { let f = DateFormatter() f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSZZ" diff --git a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift index d29f1c170..2a796cc49 100644 --- a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift +++ b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift @@ -115,8 +115,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 64e6c596b..56903aa4e 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -48,9 +48,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 @@ -302,8 +302,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) @@ -314,8 +314,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/ClashMetaConfig.swift b/ClashX/General/ClashMetaConfig.swift index 938740dc0..0a3fe2b39 100644 --- a/ClashX/General/ClashMetaConfig.swift +++ b/ClashX/General/ClashMetaConfig.swift @@ -86,6 +86,11 @@ class ClashMetaConfig: NSObject { if let urls = yaml[keys.geoxUrl.rawValue] as? [String: String] { geoxUrl = urls } + + if let mode = yaml[keys.geodataMode.rawValue] as? Bool { + geodataMode = mode + } + } mutating func updatePorts(_ usedPorts: String) { 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/General/Managers/AutoUpgardeManager.swift b/ClashX/General/Managers/AutoUpgardeManager.swift index b3818d952..411f1df69 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 c89df5d89..f9c93061d 100644 --- a/ClashX/General/Managers/ClashResourceManager.swift +++ b/ClashX/General/Managers/ClashResourceManager.swift @@ -37,7 +37,7 @@ class ClashResourceManager { static func checkRule(_ file: RuleFiles) { let fileManage = FileManager.default - let destPath = "\(kConfigFolderPath)/\(file.rawValue)" + let destPath = kConfigFolderPath + file.rawValue // Remove old mmdb file after version update. if fileManage.fileExists(atPath: destPath) { @@ -79,13 +79,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") @@ -115,8 +110,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.isEmpty { + inputView.stringValue = Settings.defaultMmdbDownloadUrl + } else { + inputView.stringValue = Settings.mmdbDownloadUrl + } alert.accessoryView = inputView alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) diff --git a/ClashX/General/Managers/ConnectionManager.swift b/ClashX/General/Managers/ConnectionManager.swift index 406eae53d..9eeb91e51 100644 --- a/ClashX/General/Managers/ConnectionManager.swift +++ b/ClashX/General/Managers/ConnectionManager.swift @@ -8,28 +8,11 @@ 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) + ApiRequest.closeConnection(conn.id) } } } @@ -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/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 4cfdc9c2d..3486aff30 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 var useViewToRenderProxy: Bool = AppDelegate.isAboveMacOS152 static var hideUnselectable: Int = UserDefaults.standard.object(forKey: "hideUnselectable") as? Int ?? NSControl.StateValue.off.rawValue { didSet { @@ -156,7 +152,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) @@ -244,7 +240,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) diff --git a/ClashX/General/Managers/RemoteControlManager.swift b/ClashX/General/Managers/RemoteControlManager.swift index 87a4f0a29..f1668230a 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 2f501f805..8a6c93dbd 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.isEmpty { + 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 deleted file mode 100644 index 835d821a5..000000000 --- a/ClashX/General/Utils/JSBridgeHandler.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// JSBridgeHandler.swift -// ClashX -// -// Created by CYC on 2018/8/29. -// Copyright © 2018年 west2online. All rights reserved. -// - -import Alamofire -import SwiftyJSON -import WebViewJavascriptBridge - -class JsBridgeUtil { - static func initJSbridge(webview: Any, delegate: Any) -> WebViewJavascriptBridge { - let bridge = WebViewJavascriptBridge(webview)! - - bridge.setWebViewDelegate(delegate) - - bridge.registerHandler("isSystemProxySet") { _, responseCallback in - responseCallback?(ConfigManager.shared.proxyPortAutoSet) - } - - bridge.registerHandler("setSystemProxy") { anydata, responseCallback in - if let enable = anydata as? Bool { - ConfigManager.shared.proxyPortAutoSet = enable - if enable { - SystemProxyManager.shared.saveProxy() - SystemProxyManager.shared.enableProxy() - } else { - SystemProxyManager.shared.disableProxy() - } - responseCallback?(true) - } else { - responseCallback?(false) - } - } - - bridge.registerHandler("getStartAtLogin") { _, responseCallback in - responseCallback?(LaunchAtLogin.shared.isEnabled) - } - - bridge.registerHandler("setStartAtLogin") { anydata, responseCallback in - if let enable = anydata as? Bool { - LaunchAtLogin.shared.isEnabled = enable - responseCallback?(true) - } else { - responseCallback?(false) - } - } - - 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 - var resp: Int - if delay == Int.max { - resp = 0 - } else { - resp = delay - } - responseCallback?(resp) - } - } else { - responseCallback?(nil) - } - } - - bridge.registerHandler("apiInfo") { _, callback in - var host = "127.0.0.1" - var port = ConfigManager.shared.apiPort - if let override = ConfigManager.shared.overrideApiURL, - let overridedHost = override.host { - host = overridedHost - port = "\(override.port ?? 80)" - } - let data = [ - "host": host, - "port": port, - "secret": ConfigManager.shared.overrideSecret ?? ConfigManager.shared.apiSecret - ] - callback?(data) - } - - // ping-pong - bridge.registerHandler("ping") { [weak bridge] _, responseCallback in - bridge?.callHandler("pong") - responseCallback?(true) - } - return bridge - } -} 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 000000000..ea79ade03 Binary files /dev/null and b/ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected.png differ 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 000000000..51af80c40 Binary files /dev/null and b/ClashX/Images.xcassets/icon_connection_done.imageset/icon_connected@2x.png differ 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 000000000..0a3663e6f Binary files /dev/null and b/ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail.png differ diff --git a/ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail@2x.png b/ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail@2x.png new file mode 100644 index 000000000..0518b4698 Binary files /dev/null and b/ClashX/Images.xcassets/icon_connection_fail.imageset/icon_connection_fail@2x.png differ diff --git a/ClashX/Images.xcassets/icon_connection_inprogress.imageset/Contents.json b/ClashX/Images.xcassets/icon_connection_inprogress.imageset/Contents.json new file mode 100644 index 000000000..373c4b379 --- /dev/null +++ b/ClashX/Images.xcassets/icon_connection_inprogress.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "icon_connecting.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_connecting@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting.png b/ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting.png new file mode 100644 index 000000000..7fe752a5b Binary files /dev/null and b/ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting.png differ 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 000000000..35fd939bb Binary files /dev/null and b/ClashX/Images.xcassets/icon_connection_inprogress.imageset/icon_connecting@2x.png differ diff --git a/ClashX/Info.plist b/ClashX/Info.plist index d5d676e99..b7c7ab4a0 100644 --- a/ClashX/Info.plist +++ b/ClashX/Info.plist @@ -3,7 +3,7 @@ BETA - + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDocumentTypes @@ -101,12 +101,12 @@ NSUbiquitousContainers - iCloud.com.west2online.ClashX + iCloud.com.metacubex.ClashX NSUbiquitousContainerIsDocumentScopePublic NSUbiquitousContainerName - ClashX + ClashX Meta NSUbiquitousContainerSupportedFolderLevels Any @@ -115,12 +115,12 @@ ProxySetting.sdef SMAuthorizedClients - identifier "com.west2online.ClashX" and anchor apple generic and certificate leaf[subject.CN] = "Mac Developer: chen yicheng (96U846XGYH)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */ + identifier "com.west2online.ClashX" and anchor apple generic and certificate leaf[subject.CN] = "Mac Developer: chen yicheng (96U846XGYH)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */ SMPrivilegedExecutables com.west2online.ClashX.ProxyConfigHelper - anchor apple generic and identifier "com.metacubex.ClashX.ProxyConfigHelper" 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) + anchor apple generic and identifier "com.metacubex.ClashX.ProxyConfigHelper" 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) SUDisallowSelectChannel diff --git a/ClashX/Models/ClashConnection.swift b/ClashX/Models/ClashConnection.swift index 0cc323d3d..270f5490b 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 { + @objc 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 cc5a67b7d..874286b2a 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -141,7 +141,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 4623a5247..8206b97a6 100644 --- a/ClashX/Support Files/en.lproj/Localizable.strings +++ b/ClashX/Support Files/en.lproj/Localizable.strings @@ -1,3 +1,6 @@ +/* No comment provided by engineer. */ +"Active Connections" = "Active Connections"; + /* Provider update time title */ " ago" = " ago"; @@ -11,7 +14,13 @@ "Alpha Meta core Warning" = "If you don't know what you're doing, never turn this switch on.\nRunning Meta Core without any authentication under 'su' privileges may damage your mac."; /* No comment provided by engineer. */ -"Auto Close Connection" = "Auto Close Connection"; +"All Clients" = "All Clients"; + +/* No comment provided by engineer. */ +"All Hosts" = "All Hosts"; + +/* No comment provided by engineer. */ +"Apply and Quit" = "Apply and Quit"; /* No comment provided by engineer. */ "Benchmark" = "Benchmark"; @@ -43,12 +52,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"; @@ -64,6 +82,9 @@ /* No comment provided by engineer. */ "Decode current config failed." = "Decode current config failed."; +/* No comment provided by engineer. */ +"Date" = "Date"; + /* No comment provided by engineer. */ "Details" = "Details"; @@ -74,7 +95,10 @@ "Direct Mode" = "Direct Mode"; /* No comment provided by engineer. */ -"Disable Restore Proxy Setting" = "Disable Restore Proxy Setting"; +"Done" = "Done"; + +/* No comment provided by engineer. */ +"Download" = "Download"; /* No comment provided by engineer. */ "Download fail" = "Download fail"; @@ -85,14 +109,17 @@ /* No comment provided by engineer. */ "Enhance proxy list render" = "Enhance proxy list render"; +/* No comment provided by engineer. */ +"Download speed" = "Download speed"; + /* No comment provided by engineer. */ "fail" = "fail"; /* No comment provided by engineer. */ -"Fail:" = "Fail:"; +"Fail" = "Fail"; /* No comment provided by engineer. */ -"fails: %@" = "fails: %@"; +"Fail:" = "Fail:"; /* No comment provided by engineer. */ "Flush fake-ip cache" = "Flush fake-ip cache"; @@ -106,6 +133,12 @@ /* No comment provided by engineer. */ "Good luck to you 🙃" = "Good luck to you 🙃"; +/* No comment provided by engineer. */ +"Host" = "Host"; + +/* No comment provided by engineer. */ +"Hosts" = "Hosts"; + /* No comment provided by engineer. */ "hours" = "hours"; @@ -127,6 +160,9 @@ /* No comment provided by engineer. */ "Load Balance" = "Load Balance"; +/* No comment provided by engineer. */ +"Local Clients" = "Local Clients"; + /* No comment provided by engineer. */ "Never show again" = "Never show again"; @@ -140,6 +176,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"; @@ -170,6 +209,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"; @@ -188,6 +230,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"; @@ -200,24 +245,24 @@ /* 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. */ "Some thing failed." = "Some thing failed."; +/* No comment provided by engineer. */ +"Sources" = "Sources"; + /* No comment provided by engineer. */ "Stable" = "Stable"; /* No comment provided by engineer. */ "Start Meta Fail!" = "Start Meta Fail!"; +/* No comment provided by engineer. */ +"Status" = "Status"; + /* No comment provided by engineer. */ "Succeed!" = "Succeed!"; @@ -254,6 +299,12 @@ /* No comment provided by engineer. */ "Update Alpha Meta core" = "Update Alpha Meta core"; +/* 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"; @@ -270,10 +321,10 @@ "Upgrade Channel" = "Upgrade Channel"; /* No comment provided by engineer. */ -"URL is not valid" = "URL is not valid"; +"Upload" = "Upload"; /* No comment provided by engineer. */ -"Use iCloud" = "Use iCloud"; +"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 e6c8390cf..d4a75043c 100644 --- a/ClashX/Support Files/zh-Hans.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hans.lproj/Localizable.strings @@ -1,3 +1,6 @@ +/* No comment provided by engineer. */ +"Active Connections" = "活动连接"; + /* Provider update time title */ " ago" = "前"; @@ -11,7 +14,13 @@ "Alpha Meta core Warning" = "如果你不知道你在做什么, 请绝对不要打开这个开关.\n在 'su' 权限下运行未经验证的 Meta 核心, 可能损坏你的 mac."; /* No comment provided by engineer. */ -"Auto Close Connection" = "切换代理时中断连接"; +"All Clients" = "所有客户端"; + +/* No comment provided by engineer. */ +"All Hosts" = "所有主机名"; + +/* No comment provided by engineer. */ +"Apply and Quit" = "应用并退出"; /* No comment provided by engineer. */ "Benchmark" = "延迟测速"; @@ -43,12 +52,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" = "复制终端代理命令"; @@ -64,6 +82,9 @@ /* No comment provided by engineer. */ "Decode current config failed." = "无法解析当前的配置文件."; +/* No comment provided by engineer. */ +"Date" = "时间"; + /* No comment provided by engineer. */ "Details" = "查看详情"; @@ -74,7 +95,10 @@ "Direct Mode" = "直接连接"; /* No comment provided by engineer. */ -"Disable Restore Proxy Setting" = "关闭自动还原之前代理"; +"Done" = "完成"; + +/* No comment provided by engineer. */ +"Download" = "下载"; /* No comment provided by engineer. */ "Download fail" = "下载失败"; @@ -85,14 +109,17 @@ /* No comment provided by engineer. */ "Enhance proxy list render" = "增强渲染代理列表"; +/* No comment provided by engineer. */ +"Download speed" = "下载速率"; + /* No comment provided by engineer. */ "fail" = "失败"; /* No comment provided by engineer. */ -"Fail:" = "失败:"; +"Fail" = "失败"; /* No comment provided by engineer. */ -"fails: %@" = "失败: %@"; +"Fail:" = "失败:"; /* No comment provided by engineer. */ "Flush fake-ip cache" = "清空 fake-ip 缓存"; @@ -106,6 +133,12 @@ /* No comment provided by engineer. */ "Good luck to you 🙃" = "祝你好运 🙃"; +/* No comment provided by engineer. */ +"Host" = "主机名"; + +/* No comment provided by engineer. */ +"Hosts" = "主机名"; + /* No comment provided by engineer. */ "hours" = "小时"; @@ -127,6 +160,15 @@ /* 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"; + +/* No comment provided by engineer. */ +"Never" = "从未"; + /* No comment provided by engineer. */ "Never show again" = "不再提示"; @@ -139,6 +181,9 @@ /* No comment provided by engineer. */ "OK" = "确定"; +/* No comment provided by engineer. */ +"Open Connection Details" = "打开连接查看器"; + /* No comment provided by engineer. */ "Open Dashboard" = "打开控制台"; @@ -169,6 +214,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" = "在通知权限关闭时减少提示弹窗"; @@ -187,6 +235,9 @@ /* No comment provided by engineer. */ "Remote Config Update Fail" = "托管配置文件更新失败"; +/* No comment provided by engineer. */ +"Requests" = "请求"; + /* No comment provided by engineer. */ "Reset Daemon" = "重置守护程序"; @@ -199,24 +250,24 @@ /* 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. */ "Some thing failed." = "发生了一些错误."; +/* No comment provided by engineer. */ +"Sources" = "来源"; + /* No comment provided by engineer. */ "Stable" = "Stable"; /* No comment provided by engineer. */ "Start Meta Fail!" = "打开Meta 失败!"; +/* No comment provided by engineer. */ +"Status" = "状态"; + /* No comment provided by engineer. */ "Succeed!" = "成功!"; @@ -256,6 +307,15 @@ /* No comment provided by engineer. */ "Update GEOIP Database" = "更新 GEOIP 数据库"; +/* No comment provided by engineer. */ +"Type" = "类型"; + +/* No comment provided by engineer. */ +"Unknown" = "未知"; + +/* No comment provided by engineer. */ +"Update GEOIP Database" = "更新IP数据库"; + /* No comment provided by engineer. */ "Update remote config update interval" = "远程配置自动更新间隔"; @@ -269,10 +329,10 @@ "Upgrade Channel" = "更新通道"; /* No comment provided by engineer. */ -"URL is not valid" = "链接不合法"; +"Upload" = "上传"; /* No comment provided by engineer. */ -"Use iCloud" = "使用iCloud"; +"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 63cd2a50c..2ac59cf83 100644 --- a/ClashX/Support Files/zh-Hant.lproj/Localizable.strings +++ b/ClashX/Support Files/zh-Hant.lproj/Localizable.strings @@ -1,3 +1,6 @@ +/* No comment provided by engineer. */ +"Active Connections" = "活動連線"; + /* No comment provided by engineer. */ "Add a remote config" = "添加託管配置文件"; @@ -5,10 +8,13 @@ "Add a remote control config" = "添加遠程控制器配置"; /* No comment provided by engineer. */ -"Apply and Quit" = "應用並退出"; +"All Clients" = "所有客戶端"; /* No comment provided by engineer. */ -"Auto Close Connection" = "切換代理時中斷連接"; +"All Hosts" = "所有主機名"; + +/* No comment provided by engineer. */ +"Apply and Quit" = "應用並退出"; /* No comment provided by engineer. */ "Benchmark" = "延遲測速"; @@ -40,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" = "複製終端代理命令"; @@ -55,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" = "查看詳情"; @@ -65,22 +83,25 @@ "Direct Mode" = "直接連接"; /* No comment provided by engineer. */ -"Disable Restore Proxy Setting" = "關閉自動還原之前代理"; +"Done" = "完成"; + +/* No comment provided by engineer. */ +"Download" = "下載"; /* No comment provided by engineer. */ "Download fail" = "下載失敗"; /* No comment provided by engineer. */ -"Enhance proxy list render" = "增強渲染代理列表"; +"Download speed" = "下載速率"; /* No comment provided by engineer. */ "fail" = "失敗"; /* No comment provided by engineer. */ -"Fail:" = "失敗:"; +"Fail" = "失敗"; /* No comment provided by engineer. */ -"fails: %@" = "失敗: %@"; +"Fail:" = "失敗:"; /* No comment provided by engineer. */ "Global" = "全局"; @@ -88,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" = "小時"; @@ -109,7 +136,8 @@ /* No comment provided by engineer. */ "Load Balance" = "負載均衡"; -"Mode" = "模式"; +/* 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"; @@ -123,6 +151,9 @@ /* No comment provided by engineer. */ "OK" = "確定"; +/* No comment provided by engineer. */ +"Open Connection Details" = "打開連接查看器"; + /* No comment provided by engineer. */ "Open Dashboard" = "打開控制台"; @@ -150,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" = "在通知權限關閉時減少提示彈窗"; @@ -168,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" = "重置守護進程"; @@ -180,21 +217,18 @@ /* 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"; +"Sources" = "來源"; /* No comment provided by engineer. */ "Stable" = "穩定"; +/* No comment provided by engineer. */ +"Status" = "狀態"; + /* No comment provided by engineer. */ "Succeed!" = "成功!"; @@ -225,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 數據庫"; @@ -235,13 +275,10 @@ "Updating" = "更新中"; /* No comment provided by engineer. */ -"Upgrade Channel" = "升級通道"; - -/* No comment provided by engineer. */ -"URL is not valid" = "網址無效"; +"Upload" = "上傳"; /* No comment provided by engineer. */ -"Use iCloud" = "使用 iCloud"; +"Upload speed" = "上傳速率"; /* No comment provided by engineer. */ "Use reload config to try reconnect." = "使用重新加載配置嘗試重新連接"; 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 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/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index 934edf124..73b259dbb 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() @@ -20,6 +23,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() @@ -51,4 +57,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 c4b43753a..f711d9d71 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) @@ -118,6 +122,14 @@ class GeneralSettingViewController: NSViewController { view.window?.makeFirstResponder(nil) } + override func viewWillDisappear() { + super.viewWillDisappear() + let url = speedTestUrlField.stringValue + if url.isUrlVaild() || url.isEmpty { + Settings.benchMarkUrl = url + } + } + @IBAction func actionResetIgnoreList(_ sender: Any) { ignoreListTextView.string = Settings.proxyIgnoreListDefaultValue.joined(separator: ",") Settings.proxyIgnoreList = Settings.proxyIgnoreListDefaultValue diff --git a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift index 2e9a16a0c..bc362ed36 100644 --- a/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift +++ b/ClashX/ViewControllers/Settings/GlobalShortCutViewController.swift @@ -23,6 +23,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") } @@ -97,11 +98,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/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 50d5d4cb0..9466ac043 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.withDisabledEffect() diff --git a/ClashX/Views/StatusItem/StatusItemView.swift b/ClashX/Views/StatusItem/StatusItemView.swift index 29712c46e..2669f9234 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/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 de9f06831..3daabed2c 100644 --- a/ClashX/zh-Hans.lproj/Main.strings +++ b/ClashX/zh-Hans.lproj/Main.strings @@ -103,9 +103,6 @@ /* 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"; @@ -142,9 +139,6 @@ /* 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" = "检查更新"; @@ -369,3 +363,24 @@ /* 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 e1842963e..6dd7da7f1 100644 --- a/ClashX/zh-Hant.lproj/Main.strings +++ b/ClashX/zh-Hant.lproj/Main.strings @@ -1,6 +1,3 @@ -/* Class = "NSMenu"; title = "Experimental"; ObjectID = "sbS-Fj-gxn"; */ -"sbS-Fj-gxn.title" = "試驗性功能"; - /* Class = "NSMenu"; title = "Config"; ObjectID = "tck-zU-JKQ"; */ "tck-zU-JKQ.title" = "配置"; @@ -16,9 +13,6 @@ /* 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" = "控制台"; @@ -91,9 +85,6 @@ /* 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"; @@ -118,9 +109,6 @@ /* 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" = "檢查更新"; @@ -133,9 +121,6 @@ /* 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" = "遙控"; @@ -303,3 +288,24 @@ /* 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/Podfile b/Podfile deleted file mode 100644 index ac1443204..000000000 --- a/Podfile +++ /dev/null @@ -1,36 +0,0 @@ -source 'https://cdn.cocoapods.org/' - -project 'ClashX.xcodeproj' - -post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - if ['FlexibleDiff'].include? target.name - target.build_configurations.each do |config| - 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' - end - end - end -end - -target 'ClashX Meta' do - inhibit_all_warnings! - use_modular_headers! - pod 'Alamofire', '~> 5.0' - pod 'SwiftyJSON' - pod 'RxSwift' - pod 'RxCocoa' - pod 'CocoaLumberjack/Swift' - pod 'WebViewJavascriptBridge' - pod 'Starscream','3.1.1' - pod "FlexibleDiff" - pod 'GzipSwift' - pod 'SwiftLint' - pod 'Yams' - pod "PromiseKit", "~> 6.8" -end - diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index 66ad541f3..000000000 --- a/Podfile.lock +++ /dev/null @@ -1,74 +0,0 @@ -PODS: - - Alamofire (5.7.1) - - CocoaLumberjack/Core (3.8.0) - - CocoaLumberjack/Swift (3.8.0): - - CocoaLumberjack/Core - - FlexibleDiff (0.0.9) - - GzipSwift (5.1.1) - - PromiseKit (6.18.1): - - PromiseKit/CorePromise (= 6.18.1) - - PromiseKit/Foundation (= 6.18.1) - - PromiseKit/UIKit (= 6.18.1) - - PromiseKit/CorePromise (6.18.1) - - PromiseKit/Foundation (6.18.1): - - PromiseKit/CorePromise - - RxCocoa (6.5.0): - - RxRelay (= 6.5.0) - - RxSwift (= 6.5.0) - - RxRelay (6.5.0): - - RxSwift (= 6.5.0) - - RxSwift (6.5.0) - - Starscream (3.1.1) - - SwiftLint (0.52.2) - - SwiftyJSON (5.0.1) - - WebViewJavascriptBridge (6.0.3) - - Yams (5.0.5) - -DEPENDENCIES: - - Alamofire (~> 5.0) - - CocoaLumberjack/Swift - - FlexibleDiff - - GzipSwift - - PromiseKit (~> 6.8) - - RxCocoa - - RxSwift - - Starscream (= 3.1.1) - - SwiftLint - - SwiftyJSON - - WebViewJavascriptBridge - - Yams - -SPEC REPOS: - trunk: - - Alamofire - - CocoaLumberjack - - FlexibleDiff - - GzipSwift - - PromiseKit - - RxCocoa - - RxRelay - - RxSwift - - Starscream - - SwiftLint - - SwiftyJSON - - WebViewJavascriptBridge - - Yams - -SPEC CHECKSUMS: - Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88 - CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 - FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 - GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa - PromiseKit: 49d70c53d5d20e346beaea4b276b5dd2ab446bb4 - RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b - RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd - RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 - Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0 - SwiftLint: 1ac76dac888ca05cb0cf24d0c85887ec1209961d - SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e - WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 - Yams: 271b5757cee031e087ae6322128895c04826c4f3 - -PODFILE CHECKSUM: a3cddfe8a1d58df403bf512a72481e62c0ae6439 - -COCOAPODS: 1.12.1 diff --git a/install_dependency.sh b/install_dependency.sh index 04b1d74be..a43854e9e 100755 --- a/install_dependency.sh +++ b/install_dependency.sh @@ -18,8 +18,6 @@ gzip com.metacubex.ClashX.ProxyConfigHelper.meta cp com.metacubex.ClashX.ProxyConfigHelper.meta.gz ../ClashX/Resources/ cd .. -echo "Pod install" -pod install echo "delete old files" rm -f ./ClashX/Resources/country.mmdb rm -f ./ClashX/Resources/geosite.dat diff --git a/ClashX/swiftFormate.sh b/updateLocalization.sh similarity index 100% rename from ClashX/swiftFormate.sh rename to updateLocalization.sh