diff --git a/.swiftlint.yml b/.swiftlint.yml index fe3fbf0..da64edd 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,6 +1,7 @@ only_rules: - accessibility_trait_for_button - array_init + - blanket_disable_command - block_based_kvo - class_delegate_protocol - closing_brace @@ -21,6 +22,7 @@ only_rules: - control_statement - custom_rules - deployment_target + - direct_return - discarded_notification_center_observer - discouraged_assert - discouraged_direct_init @@ -28,6 +30,7 @@ only_rules: - discouraged_object_literal - discouraged_optional_boolean - discouraged_optional_collection + - duplicate_conditions - duplicate_enum_cases - duplicate_imports - duplicated_key_in_dictionary_literal @@ -53,7 +56,7 @@ only_rules: - implicit_getter - implicit_return - inclusive_language - - inert_defer + - invalid_swiftlint_command - is_disjoint - joined_default_parameter - last_where @@ -80,6 +83,7 @@ only_rules: - notification_center_detachment - ns_number_init_as_function_reference - nsobject_prefer_isequal + - number_separator - opening_brace - operator_usage_whitespace - operator_whitespace @@ -131,7 +135,6 @@ only_rules: - unneeded_parentheses_in_closure_argument - unowned_variable_capture - untyped_error_in_catch - - unused_capture_list - unused_closure_parameter - unused_control_flow_label - unused_enumerated @@ -153,6 +156,8 @@ analyzer_rules: - typesafe_array_init for_where: allow_for_as_filter: true +number_separator: + minimum_length: 5 identifier_name: max_length: warning: 100 diff --git a/Color Picker.xcodeproj/project.pbxproj b/Color Picker.xcodeproj/project.pbxproj index 6abdea4..01b2079 100644 --- a/Color Picker.xcodeproj/project.pbxproj +++ b/Color Picker.xcodeproj/project.pbxproj @@ -7,7 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - E32CF5572793FACD002DDBC0 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = E32CF5562793FACD002DDBC0 /* Defaults */; }; + E31561302988E515009B0A37 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = E315612F2988E515009B0A37 /* LaunchAtLogin */; }; + E34BFA972988F3AF002AB421 /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = E34BFA962988F3AF002AB421 /* Main.swift */; }; + E34BFA9E2988F3AF002AB421 /* Intents Extension.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = E34BFA942988F3AF002AB421 /* Intents Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E34BFAA32988F3CA002AB421 /* Intents.swift in Sources */ = {isa = PBXBuildFile; fileRef = E31561312988ECF4009B0A37 /* Intents.swift */; }; + E34BFAA42988F410002AB421 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38D4332263AB24E00701B82 /* Utilities.swift */; }; + E34BFAA62988F4C7002AB421 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = E34BFAA52988F4C7002AB421 /* Defaults */; }; E38D432F263AAE8500701B82 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38D432E263AAE8500701B82 /* SettingsScreen.swift */; }; E38D4331263AAEA900701B82 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38D4330263AAEA900701B82 /* Constants.swift */; }; E38D4333263AB24E00701B82 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38D4332263AB24E00701B82 /* Utilities.swift */; }; @@ -16,41 +21,42 @@ E390D736263C6ACD005FCB34 /* ColorPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E390D735263C6ACD005FCB34 /* ColorPanel.swift */; }; E390D738263C74C2005FCB34 /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E390D737263C74C2005FCB34 /* WelcomeScreen.swift */; }; E394C2F62642BE4C006B51CF /* InternetAccessPolicy.json in Resources */ = {isa = PBXBuildFile; fileRef = E394C2F52642BE4C006B51CF /* InternetAccessPolicy.json */; }; - E394DAAB263D73AD00F5B042 /* Regex in Frameworks */ = {isa = PBXBuildFile; productRef = E394DAAA263D73AD00F5B042 /* Regex */; }; - E394DAAE263E95D900F5B042 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = E394DAAD263E95D900F5B042 /* LaunchAtLogin */; }; E394DAB2263E965500F5B042 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = E394DAB1263E965500F5B042 /* KeyboardShortcuts */; }; E3A3B11D25904E7B001B4D0C /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3A3B11C25904E7B001B4D0C /* App.swift */; }; E3A3B12125904E7D001B4D0C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E3A3B12025904E7D001B4D0C /* Assets.xcassets */; }; E3DFA8C92662514800D2623E /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3DFA8C82662514800D2623E /* Events.swift */; }; - E3E7D79B27218903009D71F4 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3E7D79A27218903009D71F4 /* Intents.framework */; platformFilter = maccatalyst; }; - E3E7D79E27218903009D71F4 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E7D79D27218903009D71F4 /* IntentHandler.swift */; }; - E3E7D7A227218903009D71F4 /* IntentsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E3E7D79927218903009D71F4 /* IntentsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - E3E7D7A927218959009D71F4 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = E3E7D7A827218959009D71F4 /* Intents.intentdefinition */; }; - E3E7D7AA27218959009D71F4 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = E3E7D7A827218959009D71F4 /* Intents.intentdefinition */; }; - E3E7D7AC27218C03009D71F4 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38D4332263AB24E00701B82 /* Utilities.swift */; }; - E3E7D7AE27218C8A009D71F4 /* Regex in Frameworks */ = {isa = PBXBuildFile; productRef = E3E7D7AD27218C8A009D71F4 /* Regex */; }; E3F4BC872788A5780075DC52 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = E3F4BC862788A5780075DC52 /* Sentry */; }; E3F4BC892788A64F0075DC52 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = E3F4BC882788A64F0075DC52 /* Defaults */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - E3E7D7A027218903009D71F4 /* PBXContainerItemProxy */ = { + E34BFA9C2988F3AF002AB421 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E3A3B11125904E7B001B4D0C /* Project object */; proxyType = 1; - remoteGlobalIDString = E3E7D79827218903009D71F4; - remoteInfo = IntentsExtension; + remoteGlobalIDString = E34BFA932988F3AF002AB421; + remoteInfo = "Intents Extension"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + E34BFA9F2988F3AF002AB421 /* Embed ExtensionKit Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(EXTENSIONS_FOLDER_PATH)"; + dstSubfolderSpec = 16; + files = ( + E34BFA9E2988F3AF002AB421 /* Intents Extension.appex in Embed ExtensionKit Extensions */, + ); + name = "Embed ExtensionKit Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; E3E7D7A327218903009D71F4 /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( - E3E7D7A227218903009D71F4 /* IntentsExtension.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -58,6 +64,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + E31561312988ECF4009B0A37 /* Intents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Intents.swift; sourceTree = ""; }; + E34BFA942988F3AF002AB421 /* Intents Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = "Intents Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + E34BFA962988F3AF002AB421 /* Main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Main.swift; sourceTree = ""; }; + E34BFA9A2988F3AF002AB421 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E34BFA9B2988F3AF002AB421 /* Intents_Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Intents_Extension.entitlements; sourceTree = ""; }; E38D432E263AAE8500701B82 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = ""; }; E38D4330263AAEA900701B82 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; E38D4332263AB24E00701B82 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; @@ -72,35 +83,27 @@ E3A3B12525904E7D001B4D0C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E3A3B12625904E7D001B4D0C /* Color_Picker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Color_Picker.entitlements; sourceTree = ""; }; E3DFA8C82662514800D2623E /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; - E3E7D79927218903009D71F4 /* IntentsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = IntentsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; E3E7D79A27218903009D71F4 /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; }; - E3E7D79D27218903009D71F4 /* IntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; - E3E7D79F27218903009D71F4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - E3E7D7A72721892E009D71F4 /* IntentsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IntentsExtension.entitlements; sourceTree = ""; }; - E3E7D7A827218959009D71F4 /* Intents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Intents.intentdefinition; path = "Color Picker/Intents.intentdefinition"; sourceTree = SOURCE_ROOT; }; E3E7D7AB27218A50009D71F4 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - E3A3B11625904E7B001B4D0C /* Frameworks */ = { + E34BFA912988F3AF002AB421 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E3F4BC872788A5780075DC52 /* Sentry in Frameworks */, - E394DAAE263E95D900F5B042 /* LaunchAtLogin in Frameworks */, - E394DAB2263E965500F5B042 /* KeyboardShortcuts in Frameworks */, - E394DAAB263D73AD00F5B042 /* Regex in Frameworks */, - E3F4BC892788A64F0075DC52 /* Defaults in Frameworks */, + E34BFAA62988F4C7002AB421 /* Defaults in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - E3E7D79627218903009D71F4 /* Frameworks */ = { + E3A3B11625904E7B001B4D0C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E32CF5572793FACD002DDBC0 /* Defaults in Frameworks */, - E3E7D7AE27218C8A009D71F4 /* Regex in Frameworks */, - E3E7D79B27218903009D71F4 /* Intents.framework in Frameworks */, + E3F4BC872788A5780075DC52 /* Sentry in Frameworks */, + E394DAB2263E965500F5B042 /* KeyboardShortcuts in Frameworks */, + E31561302988E515009B0A37 /* LaunchAtLogin in Frameworks */, + E3F4BC892788A64F0075DC52 /* Defaults in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -117,6 +120,17 @@ name = Other; sourceTree = ""; }; + E34BFA952988F3AF002AB421 /* Intents Extension */ = { + isa = PBXGroup; + children = ( + E34BFA962988F3AF002AB421 /* Main.swift */, + E31561312988ECF4009B0A37 /* Intents.swift */, + E34BFA9A2988F3AF002AB421 /* Info.plist */, + E34BFA9B2988F3AF002AB421 /* Intents_Extension.entitlements */, + ); + path = "Intents Extension"; + sourceTree = ""; + }; E394DAA6263D62E400F5B042 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -130,7 +144,7 @@ children = ( E3E7D7AB27218A50009D71F4 /* Config.xcconfig */, E3A3B11B25904E7B001B4D0C /* Color Picker */, - E3E7D79C27218903009D71F4 /* IntentsExtension */, + E34BFA952988F3AF002AB421 /* Intents Extension */, E3A3B11A25904E7B001B4D0C /* Products */, E394DAA6263D62E400F5B042 /* Frameworks */, ); @@ -140,7 +154,7 @@ isa = PBXGroup; children = ( E3A3B11925904E7B001B4D0C /* Color Picker.app */, - E3E7D79927218903009D71F4 /* IntentsExtension.appex */, + E34BFA942988F3AF002AB421 /* Intents Extension.appex */, ); name = Products; sourceTree = ""; @@ -158,25 +172,34 @@ E390D737263C74C2005FCB34 /* WelcomeScreen.swift */, E38D4332263AB24E00701B82 /* Utilities.swift */, E3A3B12025904E7D001B4D0C /* Assets.xcassets */, - E3E7D7A827218959009D71F4 /* Intents.intentdefinition */, E322B6DB26416E1100ABB691 /* Other */, ); path = "Color Picker"; sourceTree = ""; }; - E3E7D79C27218903009D71F4 /* IntentsExtension */ = { - isa = PBXGroup; - children = ( - E3E7D79D27218903009D71F4 /* IntentHandler.swift */, - E3E7D7A72721892E009D71F4 /* IntentsExtension.entitlements */, - E3E7D79F27218903009D71F4 /* Info.plist */, - ); - path = IntentsExtension; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + E34BFA932988F3AF002AB421 /* Intents Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = E34BFAA22988F3AF002AB421 /* Build configuration list for PBXNativeTarget "Intents Extension" */; + buildPhases = ( + E34BFA902988F3AF002AB421 /* Sources */, + E34BFA912988F3AF002AB421 /* Frameworks */, + E34BFA922988F3AF002AB421 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Intents Extension"; + packageProductDependencies = ( + E34BFAA52988F4C7002AB421 /* Defaults */, + ); + productName = "Intents Extension"; + productReference = E34BFA942988F3AF002AB421 /* Intents Extension.appex */; + productType = "com.apple.product-type.extensionkit-extension"; + }; E3A3B11825904E7B001B4D0C /* Color Picker */ = { isa = PBXNativeTarget; buildConfigurationList = E3A3B12925904E7D001B4D0C /* Build configuration list for PBXNativeTarget "Color Picker" */; @@ -185,62 +208,40 @@ E3A3B11525904E7B001B4D0C /* Sources */, E3A3B11625904E7B001B4D0C /* Frameworks */, E3A3B11725904E7B001B4D0C /* Resources */, - E394DAAF263E95E200F5B042 /* Copy “Launch at Login Helper” */, E3E7D7A327218903009D71F4 /* Embed Foundation Extensions */, + E34BFA9F2988F3AF002AB421 /* Embed ExtensionKit Extensions */, ); buildRules = ( ); dependencies = ( - E3E7D7A127218903009D71F4 /* PBXTargetDependency */, + E34BFA9D2988F3AF002AB421 /* PBXTargetDependency */, ); name = "Color Picker"; packageProductDependencies = ( - E394DAAA263D73AD00F5B042 /* Regex */, - E394DAAD263E95D900F5B042 /* LaunchAtLogin */, E394DAB1263E965500F5B042 /* KeyboardShortcuts */, E3F4BC862788A5780075DC52 /* Sentry */, E3F4BC882788A64F0075DC52 /* Defaults */, + E315612F2988E515009B0A37 /* LaunchAtLogin */, ); productName = "Color Picker"; productReference = E3A3B11925904E7B001B4D0C /* Color Picker.app */; productType = "com.apple.product-type.application"; }; - E3E7D79827218903009D71F4 /* IntentsExtension */ = { - isa = PBXNativeTarget; - buildConfigurationList = E3E7D7A627218903009D71F4 /* Build configuration list for PBXNativeTarget "IntentsExtension" */; - buildPhases = ( - E3E7D79527218903009D71F4 /* Sources */, - E3E7D79627218903009D71F4 /* Frameworks */, - E3E7D79727218903009D71F4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = IntentsExtension; - packageProductDependencies = ( - E3E7D7AD27218C8A009D71F4 /* Regex */, - E32CF5562793FACD002DDBC0 /* Defaults */, - ); - productName = IntentsExtension; - productReference = E3E7D79927218903009D71F4 /* IntentsExtension.appex */; - productType = "com.apple.product-type.app-extension"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ E3A3B11125904E7B001B4D0C /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1310; + LastSwiftUpdateCheck = 1420; LastUpgradeCheck = 1400; TargetAttributes = { + E34BFA932988F3AF002AB421 = { + CreatedOnToolsVersion = 14.2; + }; E3A3B11825904E7B001B4D0C = { CreatedOnToolsVersion = 12.3; }; - E3E7D79827218903009D71F4 = { - CreatedOnToolsVersion = 13.1; - }; }; }; buildConfigurationList = E3A3B11425904E7B001B4D0C /* Build configuration list for PBXProject "Color Picker" */; @@ -254,61 +255,40 @@ mainGroup = E3A3B11025904E7B001B4D0C; packageReferences = ( E3E14060259A0D97004FC89F /* XCRemoteSwiftPackageReference "Defaults" */, - E394DAA9263D73AD00F5B042 /* XCRemoteSwiftPackageReference "Regex" */, - E394DAAC263E95D900F5B042 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */, E394DAB0263E965500F5B042 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */, E3F4BC852788A5780075DC52 /* XCRemoteSwiftPackageReference "sentry-cocoa" */, + E315612E2988E515009B0A37 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */, ); productRefGroup = E3A3B11A25904E7B001B4D0C /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( E3A3B11825904E7B001B4D0C /* Color Picker */, - E3E7D79827218903009D71F4 /* IntentsExtension */, + E34BFA932988F3AF002AB421 /* Intents Extension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - E3A3B11725904E7B001B4D0C /* Resources */ = { + E34BFA922988F3AF002AB421 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E394C2F62642BE4C006B51CF /* InternetAccessPolicy.json in Resources */, - E3A3B12125904E7D001B4D0C /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - E3E7D79727218903009D71F4 /* Resources */ = { + E3A3B11725904E7B001B4D0C /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + E394C2F62642BE4C006B51CF /* InternetAccessPolicy.json in Resources */, + E3A3B12125904E7D001B4D0C /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - E394DAAF263E95E200F5B042 /* Copy “Launch at Login Helper” */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Copy “Launch at Login Helper”"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\"\n"; - showEnvVarsInLog = 0; - }; E3E9F9AE2642B8F800AE6450 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -332,6 +312,16 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + E34BFA902988F3AF002AB421 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E34BFAA42988F410002AB421 /* Utilities.swift in Sources */, + E34BFAA32988F3CA002AB421 /* Intents.swift in Sources */, + E34BFA972988F3AF002AB421 /* Main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E3A3B11525904E7B001B4D0C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -344,32 +334,75 @@ E38D4331263AAEA900701B82 /* Constants.swift in Sources */, E3DFA8C92662514800D2623E /* Events.swift in Sources */, E390D736263C6ACD005FCB34 /* ColorPanel.swift in Sources */, - E3E7D7A927218959009D71F4 /* Intents.intentdefinition in Sources */, E38D4335263AEE3700701B82 /* ColorPickerScreen.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - E3E7D79527218903009D71F4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - E3E7D7AC27218C03009D71F4 /* Utilities.swift in Sources */, - E3E7D7AA27218959009D71F4 /* Intents.intentdefinition in Sources */, - E3E7D79E27218903009D71F4 /* IntentHandler.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - E3E7D7A127218903009D71F4 /* PBXTargetDependency */ = { + E34BFA9D2988F3AF002AB421 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = E3E7D79827218903009D71F4 /* IntentsExtension */; - targetProxy = E3E7D7A027218903009D71F4 /* PBXContainerItemProxy */; + target = E34BFA932988F3AF002AB421 /* Intents Extension */; + targetProxy = E34BFA9C2988F3AF002AB421 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + E34BFAA02988F3AF002AB421 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = "Intents Extension/Intents_Extension.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = YG56YK5RN5; + ENABLE_HARDENED_RUNTIME = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Intents Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Intents Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@executable_path/../../../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.sindresorhus.Color-Picker.Intents-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_EXTENSION"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + E34BFAA12988F3AF002AB421 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = "Intents Extension/Intents_Extension.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = YG56YK5RN5; + ENABLE_HARDENED_RUNTIME = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Intents Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Intents Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@executable_path/../../../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.sindresorhus.Color-Picker.Intents-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; E3A3B12725904E7D001B4D0C /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = E3E7D7AB27218A50009D71F4 /* Config.xcconfig */; @@ -423,7 +456,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 12.4; + MACOSX_DEPLOYMENT_TARGET = 13.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -481,7 +514,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 12.4; + MACOSX_DEPLOYMENT_TARGET = 13.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; @@ -545,86 +578,32 @@ }; name = Release; }; - E3E7D7A427218903009D71F4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_ENTITLEMENTS = IntentsExtension/IntentsExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = YG56YK5RN5; - ENABLE_HARDENED_RUNTIME = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = IntentsExtension/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = IntentsExtension; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.sindresorhus.Color-Picker.IntentsExtension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_EXTENSION"; - SWIFT_EMIT_LOC_STRINGS = YES; - }; - name = Debug; - }; - E3E7D7A527218903009D71F4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_ENTITLEMENTS = IntentsExtension/IntentsExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = YG56YK5RN5; - ENABLE_HARDENED_RUNTIME = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = IntentsExtension/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = IntentsExtension; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.sindresorhus.Color-Picker.IntentsExtension"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP_EXTENSION; - SWIFT_EMIT_LOC_STRINGS = YES; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - E3A3B11425904E7B001B4D0C /* Build configuration list for PBXProject "Color Picker" */ = { + E34BFAA22988F3AF002AB421 /* Build configuration list for PBXNativeTarget "Intents Extension" */ = { isa = XCConfigurationList; buildConfigurations = ( - E3A3B12725904E7D001B4D0C /* Debug */, - E3A3B12825904E7D001B4D0C /* Release */, + E34BFAA02988F3AF002AB421 /* Debug */, + E34BFAA12988F3AF002AB421 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - E3A3B12925904E7D001B4D0C /* Build configuration list for PBXNativeTarget "Color Picker" */ = { + E3A3B11425904E7B001B4D0C /* Build configuration list for PBXProject "Color Picker" */ = { isa = XCConfigurationList; buildConfigurations = ( - E3A3B12A25904E7D001B4D0C /* Debug */, - E3A3B12B25904E7D001B4D0C /* Release */, + E3A3B12725904E7D001B4D0C /* Debug */, + E3A3B12825904E7D001B4D0C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - E3E7D7A627218903009D71F4 /* Build configuration list for PBXNativeTarget "IntentsExtension" */ = { + E3A3B12925904E7D001B4D0C /* Build configuration list for PBXNativeTarget "Color Picker" */ = { isa = XCConfigurationList; buildConfigurations = ( - E3E7D7A427218903009D71F4 /* Debug */, - E3E7D7A527218903009D71F4 /* Release */, + E3A3B12A25904E7D001B4D0C /* Debug */, + E3A3B12B25904E7D001B4D0C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -632,20 +611,12 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - E394DAA9263D73AD00F5B042 /* XCRemoteSwiftPackageReference "Regex" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/sindresorhus/Regex"; - requirement = { - kind = upToNextMinorVersion; - minimumVersion = 1.0.0; - }; - }; - E394DAAC263E95D900F5B042 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = { + E315612E2988E515009B0A37 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin"; + repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-Modern"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.0.0; + minimumVersion = 1.0.0; }; }; E394DAB0263E965500F5B042 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */ = { @@ -669,37 +640,27 @@ repositoryURL = "https://github.com/getsentry/sentry-cocoa"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 7.31.3; + minimumVersion = 8.3.3; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - E32CF5562793FACD002DDBC0 /* Defaults */ = { - isa = XCSwiftPackageProductDependency; - package = E3E14060259A0D97004FC89F /* XCRemoteSwiftPackageReference "Defaults" */; - productName = Defaults; - }; - E394DAAA263D73AD00F5B042 /* Regex */ = { + E315612F2988E515009B0A37 /* LaunchAtLogin */ = { isa = XCSwiftPackageProductDependency; - package = E394DAA9263D73AD00F5B042 /* XCRemoteSwiftPackageReference "Regex" */; - productName = Regex; + package = E315612E2988E515009B0A37 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */; + productName = LaunchAtLogin; }; - E394DAAD263E95D900F5B042 /* LaunchAtLogin */ = { + E34BFAA52988F4C7002AB421 /* Defaults */ = { isa = XCSwiftPackageProductDependency; - package = E394DAAC263E95D900F5B042 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */; - productName = LaunchAtLogin; + package = E3E14060259A0D97004FC89F /* XCRemoteSwiftPackageReference "Defaults" */; + productName = Defaults; }; E394DAB1263E965500F5B042 /* KeyboardShortcuts */ = { isa = XCSwiftPackageProductDependency; package = E394DAB0263E965500F5B042 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */; productName = KeyboardShortcuts; }; - E3E7D7AD27218C8A009D71F4 /* Regex */ = { - isa = XCSwiftPackageProductDependency; - package = E394DAA9263D73AD00F5B042 /* XCRemoteSwiftPackageReference "Regex" */; - productName = Regex; - }; E3F4BC862788A5780075DC52 /* Sentry */ = { isa = XCSwiftPackageProductDependency; package = E3F4BC852788A5780075DC52 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; diff --git a/Color Picker/App.swift b/Color Picker/App.swift index 6deeccd..147a6cc 100644 --- a/Color Picker/App.swift +++ b/Color Picker/App.swift @@ -1,15 +1,9 @@ import SwiftUI -import LaunchAtLogin - -// TODO: Remove the view menu /** NOTES: - The "com.apple.security.files.user-selected.read-only" entitlement is required by the "Open" menu in the "Color Palettes" pane. -TODO when targeting macOS 13: -- Upload non-App Store version. - TODO shortcut action ideas; - Convert color - Toggle color panel @@ -23,7 +17,7 @@ struct AppMain: App { @StateObject private var pasteboardObserver = NSPasteboard.SimpleObservable(.general, onlyWhileAppIsActive: true) init() { - migrate() + setUpConfig() } var body: some Scene { @@ -85,67 +79,25 @@ struct AppMain: App { } } - private func migrate() { - LaunchAtLogin.migrateIfNeeded() - - // TODO: Remove in 2023. - SSApp.runOnce(identifier: "migrateShownColorFormats") { - guard !SSApp.isFirstLaunch else { - return - } - - Defaults.migrate(.shownColorFormats, to: .v5) - Defaults.migrate(.colorFormatToCopyAfterPicking, to: .v5) - } - - // TODO: Remove in 2023. - SSApp.runOnce(identifier: "migrateToPreferredColorFormatSetting") { - guard !SSApp.isFirstLaunch else { - return - } - - if Defaults[.colorFormatToCopyAfterPicking] != .none { - Defaults[.copyColorAfterPicking] = true - } - - switch Defaults[.colorFormatToCopyAfterPicking] { - case .none: - break - case .hex: - Defaults[.preferredColorFormat] = .hex - case .hsl: - Defaults[.preferredColorFormat] = .hsl - case .rgb: - Defaults[.preferredColorFormat] = .rgb - case .lch: - Defaults[.preferredColorFormat] = .lch - } - } - - // Preserve the old behavior for existing users. - SSApp.runOnce(identifier: "setDefaultsForMenuBarItemClickActionSetting") { - guard !SSApp.isFirstLaunch else { - return - } - - Defaults[.menuBarItemClickAction] = .toggleWindow + private func setUpConfig() { + #if !DEBUG + SentrySDK.start { + $0.dsn = "https://e89cb93d693444ee8829f521ab75025a@o844094.ingest.sentry.io/6139060" + $0.enableSwizzling = false + $0.enableAppHangTracking = false // https://github.com/getsentry/sentry-cocoa/issues/2643 } + #endif } } @MainActor private final class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { - if #available(macOS 13, *) { - SSApp.swiftUIMainWindow?.close() - } + SSApp.swiftUIMainWindow?.close() } func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { - if #available(macOS 13, *) { - AppState.shared.handleAppReopen() - } - + AppState.shared.handleAppReopen() return false } } diff --git a/Color Picker/AppState.swift b/Color Picker/AppState.swift index b83cba1..3a944b6 100644 --- a/Color Picker/AppState.swift +++ b/Color Picker/AppState.swift @@ -129,16 +129,13 @@ final class AppState: ObservableObject { } } - init() { - setUpConfig() - + private init() { DispatchQueue.main.async { [self] in didLaunch() } } private func didLaunch() { - fixStuff() setUpEvents() handleMenuBarIcon() showWelcomeScreenIfNeeded() @@ -155,30 +152,8 @@ final class AppState: ObservableObject { #endif } - private func setUpConfig() { - #if !DEBUG - SentrySDK.start { - $0.dsn = "https://e89cb93d693444ee8829f521ab75025a@o844094.ingest.sentry.io/6139060" - $0.enableSwizzling = false - } - #endif - } - - private func fixStuff() { - if #available(macOS 13, *) { - SSApp.swiftUIMainWindow?.close() - } else { - // Make the invisible native SwitUI window not block access to the desktop. (macOS 12.0) - // https://github.com/feedback-assistant/reports/issues/253 - SSApp.swiftUIMainWindow?.ignoresMouseEvents = true - - // Make the invisible native SwiftUI window not show up in mission control when in menu bar mode. (macOS 11.6) - SSApp.swiftUIMainWindow?.collectionBehavior = .stationary - } - } - private func requestReview() { - SSApp.requestReviewAfterBeingCalledThisManyTimes([10, 100, 200, 1000]) + SSApp.requestReviewAfterBeingCalledThisManyTimes([8, 100, 200, 1000]) } private func addToRecentlyPickedColor(_ color: NSColor) { diff --git a/Color Picker/ColorPickerScreen.swift b/Color Picker/ColorPickerScreen.swift index fcd9fed..1d3b3aa 100644 --- a/Color Picker/ColorPickerScreen.swift +++ b/Color Picker/ColorPickerScreen.swift @@ -188,6 +188,7 @@ struct ColorPickerScreen: View { Text(colorPanel.color.accessibilityName) .font(.system(largerText ? .title3 : .body)) .textSelection(.enabled) + .accessibilityHidden(true) } } .padding(9) @@ -291,7 +292,7 @@ private struct BarView: View { actionButton Spacer() } - // Cannot do this as the `Menu` buttons don't respect it. (macOS 12.0.1) + // Cannot do this as the `Menu` buttons don't respect it. (macOS 13.2) // https://github.com/feedback-assistant/reports/issues/249 // .font(.title3) .background { @@ -318,7 +319,7 @@ private struct BarView: View { Divider() Defaults.Toggle("Stay on Top", key: .stayOnTop) Divider() - Button(OS.isMacOS13OrLater ? "Settings…" : "Preferences…") { + Button("Settings…") { SSApp.showSettingsWindow() } .keyboardShortcut(",") @@ -328,11 +329,8 @@ private struct BarView: View { .labelStyle(.iconOnly) // .padding(8) // Has no effect. (macOS 12.0.1) } - // TODO: Remove when targeting macOS 13 where it's fixed. - .buttonStyle(.automatic) // Without, it becomes disabled: https://github.com/feedback-assistant/reports/issues/250 (macOS 12.0.1) .padding(8) .contentShape(.rectangle) - .fixedSize() .opacity(0.6) // Try to match the other buttons. .menuIndicator(.hidden) } @@ -370,9 +368,6 @@ private struct RecentlyPickedColorsButton: View { recentlyPickedColors = [] } } - // TODO: Remove when targeting macOS 13 where it's fixed. - // Without, it becomes disabled. (macOS 12.4) - .buttonStyle(.automatic) } label: { Image(systemName: "clock.fill") .controlSize(.large) @@ -381,7 +376,6 @@ private struct RecentlyPickedColorsButton: View { } .menuIndicator(.hidden) .padding(8) - .fixedSize() .opacity(0.6) // Try to match the other buttons. .disabled(recentlyPickedColors.isEmpty) .help(recentlyPickedColors.isEmpty ? "No recently picked colors" : "Recently picked colors") diff --git a/Color Picker/Constants.swift b/Color Picker/Constants.swift index 5e2dac3..5f9dc6c 100644 --- a/Color Picker/Constants.swift +++ b/Color Picker/Constants.swift @@ -18,9 +18,6 @@ extension Defaults.Keys { static let largerText = Key("largerText", default: false) static let copyColorAfterPicking = Key("copyColorAfterPicking", default: false) static let showAccessibilityColorName = Key("showAccessibilityColorName", default: false) - - // Deprecated - static let colorFormatToCopyAfterPicking = Key("colorFormatToCopyAfterPicking", default: .none) } extension KeyboardShortcuts.Name { @@ -28,47 +25,6 @@ extension KeyboardShortcuts.Name { static let toggleWindow = Self("toggleWindow") } -// TODO: Remove in 2023. -enum CopyColorFormat: String, CaseIterable, Defaults.Serializable { - case none // swiftlint:disable:this discouraged_none_name - case hex - case hsl - case rgb - case lch - - var title: String { - switch self { - case .none: - return "None" - case .hex: - return "Hex" - case .hsl: - return "HSL" - case .rgb: - return "RGB" - case .lch: - return "LCH" - } - } -} - -// TODO: Remove in 2023. -enum CodableCopyColorFormat: String { - case none // swiftlint:disable:this discouraged_none_name - case hex - case hsl - case rgb - case lch -} - -extension CodableCopyColorFormat: Defaults.CodableType { - typealias NativeForm = CopyColorFormat -} - -extension CopyColorFormat: Defaults.NativeType { - typealias CodableForm = CodableCopyColorFormat -} - enum ColorFormat: String, CaseIterable, Defaults.Serializable { case hex case hsl @@ -93,22 +49,6 @@ extension ColorFormat: Identifiable { var id: Self { self } } -// TODO: Remove in 2023. -enum CodableColorFormat: String { - case hex - case hsl - case rgb - case lch -} - -extension CodableColorFormat: Defaults.CodableType { - typealias NativeForm = ColorFormat -} - -extension ColorFormat: Defaults.NativeType { - typealias CodableForm = CodableColorFormat -} - enum MenuBarItemClickAction: String, CaseIterable, Defaults.Serializable { case showMenu case showColorSampler diff --git a/Color Picker/Events.swift b/Color Picker/Events.swift index 6b8d0f5..4b1ed51 100644 --- a/Color Picker/Events.swift +++ b/Color Picker/Events.swift @@ -44,16 +44,6 @@ extension AppState { colorPanel.toggle() } - if #unavailable(macOS 13) { - // We use this instead of `applicationShouldHandleReopen` because of the macOS bug. - // https://github.com/feedback-assistant/reports/issues/246 - NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification) - .sink { [self] _ in - handleAppReopen() - } - .store(in: &cancellables) - } - // Workaround for the color picker window not becoming active after the settings window closes. (macOS 11.3) NotificationCenter.default.publisher(for: NSWindow.willCloseNotification) .sink { [self] _ in diff --git a/Color Picker/Info.plist b/Color Picker/Info.plist index e7a6482..3751d46 100644 --- a/Color Picker/Info.plist +++ b/Color Picker/Info.plist @@ -28,10 +28,5 @@ MDItemKeywords color,picker,system,pick,colour,colors,colours,sampler - NSUserActivityTypes - - GetRandomColorIntent - SampleColorIntent - diff --git a/Color Picker/Intents.intentdefinition b/Color Picker/Intents.intentdefinition deleted file mode 100644 index f5b81d3..0000000 --- a/Color Picker/Intents.intentdefinition +++ /dev/null @@ -1,331 +0,0 @@ - - - - - INEnums - - INIntentDefinitionModelVersion - 1.2 - INIntentDefinitionNamespace - cTK6qm - INIntentDefinitionSystemVersion - 22A400 - INIntentDefinitionToolsBuildVersion - 14B47b - INIntentDefinitionToolsVersion - 14.1 - INIntents - - - INIntentCategory - generic - INIntentConfigurable - - INIntentDescription - Lets you pick a color from the screen. Returns the color in Hex, HSL, RGB, and LCH format. - INIntentDescriptionID - cP7izh - INIntentIneligibleForSuggestions - - INIntentManagedParameterCombinations - - - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationUpdatesLinked - - - - INIntentName - SampleColor - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - INIntentResponseLastParameterTag - 2 - INIntentResponseOutput - color - INIntentResponseParameters - - - INIntentResponseParameterDisplayName - Color - INIntentResponseParameterDisplayNameID - XZNcv6 - INIntentResponseParameterDisplayPriority - 1 - INIntentResponseParameterName - color - INIntentResponseParameterObjectType - Color_ - INIntentResponseParameterObjectTypeNamespace - cTK6qm - INIntentResponseParameterTag - 2 - INIntentResponseParameterType - Object - - - - INIntentTitle - Sample Color from Screen - INIntentTitleID - Y4u1Rd - INIntentType - Custom - INIntentVerb - Do - - - INIntentCategory - information - INIntentConfigurable - - INIntentDescription - Gets a random color. Returns the color in Hex, HSL, RGB, and LCH format. - INIntentDescriptionID - wJpzOU - INIntentIneligibleForSuggestions - - INIntentManagedParameterCombinations - - - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationUpdatesLinked - - - - INIntentName - GetRandomColor - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - INIntentResponseLastParameterTag - 2 - INIntentResponseOutput - color - INIntentResponseParameters - - - INIntentResponseParameterDisplayName - Color - INIntentResponseParameterDisplayNameID - ZfEApj - INIntentResponseParameterDisplayPriority - 1 - INIntentResponseParameterName - color - INIntentResponseParameterObjectType - Color_ - INIntentResponseParameterObjectTypeNamespace - cTK6qm - INIntentResponseParameterTag - 2 - INIntentResponseParameterType - Object - - - - INIntentTitle - Get Random Color - INIntentTitleID - 3RA5pB - INIntentType - Custom - INIntentVerb - View - - - INTypes - - - INTypeDisplayName - Color - INTypeDisplayNameID - hDaAVh - INTypeLastPropertyTag - 110 - INTypeName - Color_ - INTypeProperties - - - INTypePropertyDefault - - INTypePropertyDisplayPriority - 1 - INTypePropertyName - identifier - INTypePropertyTag - 1 - INTypePropertyType - String - - - INTypePropertyDefault - - INTypePropertyDisplayPriority - 2 - INTypePropertyName - displayString - INTypePropertyTag - 2 - INTypePropertyType - String - - - INTypePropertyDefault - - INTypePropertyDisplayPriority - 3 - INTypePropertyName - pronunciationHint - INTypePropertyTag - 3 - INTypePropertyType - String - - - INTypePropertyDefault - - INTypePropertyDisplayPriority - 4 - INTypePropertyName - alternativeSpeakableMatches - INTypePropertySupportsMultipleValues - - INTypePropertyTag - 4 - INTypePropertyType - SpeakableString - - - INTypePropertyDisplayName - Hex - INTypePropertyDisplayNameID - 6YKpb0 - INTypePropertyDisplayPriority - 5 - INTypePropertyName - hex - INTypePropertyTag - 100 - INTypePropertyType - String - - - INTypePropertyDisplayName - Hex Number - INTypePropertyDisplayNameID - HRogva - INTypePropertyDisplayPriority - 6 - INTypePropertyName - hexNumber - INTypePropertyTag - 102 - INTypePropertyType - Integer - - - INTypePropertyDisplayName - HSL - INTypePropertyDisplayNameID - eUoGqL - INTypePropertyDisplayPriority - 7 - INTypePropertyName - hsl - INTypePropertyTag - 103 - INTypePropertyType - String - - - INTypePropertyDisplayName - RGB - INTypePropertyDisplayNameID - 4eJq9w - INTypePropertyDisplayPriority - 8 - INTypePropertyName - rgb - INTypePropertyTag - 104 - INTypePropertyType - String - - - INTypePropertyDisplayName - LCH - INTypePropertyDisplayNameID - VvLpmF - INTypePropertyDisplayPriority - 9 - INTypePropertyName - lch - INTypePropertyTag - 105 - INTypePropertyType - String - - - INTypePropertyDisplayName - HSL Legacy - INTypePropertyDisplayNameID - 9k0dTb - INTypePropertyDisplayPriority - 10 - INTypePropertyName - hslLegacy - INTypePropertyTag - 106 - INTypePropertyType - String - - - INTypePropertyDisplayName - RGB Legacy - INTypePropertyDisplayNameID - N6hItY - INTypePropertyDisplayPriority - 11 - INTypePropertyName - rgbLegacy - INTypePropertyTag - 107 - INTypePropertyType - String - - - - - - diff --git a/Color Picker/SettingsScreen.swift b/Color Picker/SettingsScreen.swift index 9ac38b7..d9d4349 100644 --- a/Color Picker/SettingsScreen.swift +++ b/Color Picker/SettingsScreen.swift @@ -14,7 +14,10 @@ struct SettingsScreen: View { AdvancedSettings() .settingsTabItem(.advanced) } - .frame(width: 420) + .formStyle(.grouped) + .frame(width: 460) + .frame(maxHeight: 480) + .fixedSize() .windowLevel(.floating + 1) // Ensure it's always above the color picker. } } @@ -23,99 +26,68 @@ private struct GeneralSettings: View { @Default(.showInMenuBar) private var showInMenuBar var body: some View { - VStack(alignment: .leading) { + Form { Section { - Defaults.Toggle("Stay on top", key: .stayOnTop) - .help("Make the color picker window stay on top of all other windows.") - .padding(.bottom, 8) - Defaults.Toggle("Copy color in preferred format after picking", key: .copyColorAfterPicking) - .padding(.bottom, 8) Defaults.Toggle("Show in menu bar instead of Dock", key: .showInMenuBar) .help("If you have “Keep in Dock” enabled when activating this setting, you should disable that since the Dock icon will no longer be functional.") - Group { + if showInMenuBar { LaunchAtLogin.Toggle() - .help(showInMenuBar ? "" : "There is really no point in launching the app at login if it is not in the menu bar. You can instead just put it in the Dock and launch it when needed.") + .help("There is really no point in launching the app at login if it is not in the menu bar. You can instead just put it in the Dock and launch it when needed.") HideMenuBarIconSetting() MenuBarItemClickActionSetting() } - .disabled(!showInMenuBar) - .padding(.leading, 20) - } footer: { + } + Section { + Defaults.Toggle("Stay on top", key: .stayOnTop) + .help("Make the color picker window stay on top of all other windows.") + Defaults.Toggle("Copy color in preferred format after picking", key: .copyColorAfterPicking) + } + Section {} footer: { Button("Feedback & Support") { SSApp.openSendFeedbackPage() } .buttonStyle(.link) - .padding(.top) - .offset(y: 20) + .controlSize(.small) } } - .padding() - .padding() - .padding(.vertical) } } private struct ColorSettings: View { var body: some View { - VStack(alignment: .leading) { - Form { - Defaults.Toggle("Uppercase Hex color", key: .uppercaseHexColor) - Defaults.Toggle("Prefix Hex color with #", key: .hashPrefixInHexColor) - Defaults.Toggle("Use legacy syntax for HSL and RGB", key: .legacyColorSyntax) - .help("Use the legacy “hsl(198, 28%, 50%)” syntax instead of the modern “hsl(198deg 28% 50%)” syntax. This setting is meant for users that need to support older browsers. All modern browsers support the modern syntax.") - } - .padding() - .padding(.horizontal) - Divider() + Form { Section { PreferredColorFormatSetting() - } - .padding() - .padding(.horizontal) - Divider() - VStack(alignment: .leading) { ShownColorFormatsSetting() } - .padding() - .padding(.horizontal) - Divider() Section { + Defaults.Toggle("Uppercase Hex color", key: .uppercaseHexColor) + Defaults.Toggle("Prefix Hex color with #", key: .hashPrefixInHexColor) + Defaults.Toggle("Use legacy syntax for HSL and RGB", key: .legacyColorSyntax) + .help("Use the legacy “hsl(198, 28%, 50%)” syntax instead of the modern “hsl(198deg 28% 50%)” syntax. This setting is meant for users that need to support older browsers. All modern browsers support the modern syntax.") + } + Section {} footer: { Link("What is LCH color?", destination: "https://lea.verou.me/2020/04/lch-colors-in-css-what-why-and-how/") .controlSize(.small) - .padding(.top) } - .frame(maxWidth: .infinity) } - .padding(.vertical) } } private struct ShortcutsSettings: View { @Default(.showInMenuBar) private var showInMenuBar - private let maxWidth = 100.0 var body: some View { Form { - KeyboardShortcuts.Recorder("Pick color:", name: .pickColor) - .padding(.bottom, 8) - KeyboardShortcuts.Recorder("Toggle window:", name: .toggleWindow) + KeyboardShortcuts.Recorder("Pick color", name: .pickColor) + KeyboardShortcuts.Recorder(for: .toggleWindow) { + Text("Toggle window") + if !showInMenuBar { + Text("Requires “Show in menu bar” to be enabled") + } + } .disabled(!showInMenuBar) - .opacity(showInMenuBar ? 1 : 0.5) - .overlay( - showInMenuBar - ? nil - : Text("Requires “Show in menu bar” to be enabled.") - .font(.system(size: 10)) - .foregroundColor(.secondary) - .offset(y: 20), - alignment: .bottom - ) - .padding(.bottom, showInMenuBar ? 0 : 20) } - .padding() - .padding() - .padding(.vertical) - .offset(x: -10) } } @@ -127,9 +99,6 @@ private struct AdvancedSettings: View { Defaults.Toggle("Use larger text in text fields", key: .largerText) Defaults.Toggle("Show accessibility color name", key: .showAccessibilityColorName) } - .padding() - .padding(.vertical) - .padding(.vertical) } } @@ -153,19 +122,11 @@ private struct MenuBarItemClickActionSetting: View { @Default(.menuBarItemClickAction) private var menuBarItemClickAction var body: some View { - VStack { - EnumPicker(enumBinding: $menuBarItemClickAction) { element, _ in - Text(element.title) - } label: { - Text("When clicking menu bar icon:") - .respectDisabled() - .fixedSize() - } - .fixedSize() + EnumPicker(selection: $menuBarItemClickAction) { + Text($0.title) + } label: { + Text("When clicking menu bar icon") Text(menuBarItemClickAction.tip) - .offset(x: 2) - .settingSubtitleTextStyle() - .frame(maxWidth: .infinity, alignment: .trailing) } } } @@ -174,20 +135,15 @@ private struct PreferredColorFormatSetting: View { @Default(.preferredColorFormat) private var preferredColorFormat var body: some View { - EnumPicker(enumBinding: $preferredColorFormat) { element, _ in - Text(element.title) - } label: { - Text("Preferred color format:") - .fixedSize() + EnumPicker("Preferred color format", selection: $preferredColorFormat) { + Text($0.title) } - .fixedSize() } } private struct ShownColorFormatsSetting: View { var body: some View { - Section("Shown color formats:") { - // TODO: Use a dropdown when SwiftUI supports multiple selections in `Picker`. + LabeledContent("Shown color formats") { Defaults.MultiCheckboxPicker( key: .shownColorFormats, data: ColorFormat.allCases diff --git a/Color Picker/Utilities.swift b/Color Picker/Utilities.swift index e22048b..aa26a20 100644 --- a/Color Picker/Utilities.swift +++ b/Color Picker/Utilities.swift @@ -3,7 +3,6 @@ import Combine import Carbon import StoreKit import Defaults -import Regex #if !APP_EXTENSION import Sentry @@ -188,7 +187,7 @@ enum SSApp { extension SSApp { @MainActor static var swiftUIMainWindow: NSWindow? { - NSApp.windows.first { $0.simpleClassName == (OS.isMacOS13OrLater ? "AppKitWindow" : "SwiftUIWindow") } + NSApp.windows.first { $0.simpleClassName == "AppKitWindow" } } } @@ -205,11 +204,7 @@ extension SSApp { NSApp.activate(ignoringOtherApps: true) } - if #available(macOS 13, *) { - NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) - } else { - NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) - } + NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) } } @@ -409,13 +404,18 @@ final class LocalEventMonitor: ObservableObject { @discardableResult func start() -> Self { - monitor = NSEvent.addLocalMonitorForEvents(matching: events) { [weak self] in + monitor = NSEvent.addLocalMonitorForEvents(matching: events) { [weak self] event in guard let self else { - return $0 + return event } - self.objectWillChange.send($0) - return self.callback?($0) ?? $0 + self.objectWillChange.send(event) + + if let callback = self.callback { + return callback(event) + } + + return event } as AnyObject return self @@ -668,7 +668,7 @@ extension Color { extension NSColor { - private static let cssHSLRegex = Regex(#"^\s*hsla?\((?\d+)(?:deg)?[\s,]*(?[\d.]+)%[\s,]*(?[\d.]+)%\);?\s*$"#) + private static let cssHSLRegex = /^\s*hsla?\((?\d+)(?:deg)?[\s,]*(?[\d.]+)%[\s,]*(?[\d.]+)%\);?\s*$/ // TODO: Should I move this to the `Colors.HSL` struct instead? // TODO: Support `alpha` in HSL (both comma and `/` separated): https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl() @@ -678,13 +678,10 @@ extension NSColor { */ convenience init?(cssHSLString: String) { guard - let match = Self.cssHSLRegex.firstMatch(in: cssHSLString), - let hueString = match.group(named: "hue")?.value, - let saturationString = match.group(named: "saturation")?.value, - let lightnessString = match.group(named: "lightness")?.value, - let hue = Double(hueString), - let saturation = Double(saturationString), - let lightness = Double(lightnessString), + let match = cssHSLString.wholeMatch(of: Self.cssHSLRegex)?.output, + let hue = Double(match.hue), + let saturation = Double(match.saturation), + let lightness = Double(match.lightness), (0...360).contains(hue), (0...100).contains(saturation), (0...100).contains(lightness) @@ -704,7 +701,7 @@ extension NSColor { extension NSColor { - private static let cssRGBRegex = Regex(#"^\s*rgba?\((?[\d.]+)[\s,]*(?[\d.]+)[\s,]*(?[\d.]+)\);?\s*$"#) + private static let cssRGBRegex = /^\s*rgba?\((?[\d.]+)[\s,]*(?[\d.]+)[\s,]*(?[\d.]+)\);?\s*$/ // TODO: Need to handle `rgb(10%, 10%, 10%)`. // TODO: Support `alpha` in RGB (both comma and `/` separated): https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl() @@ -715,13 +712,10 @@ extension NSColor { */ convenience init?(cssRGBString: String) { guard - let match = Self.cssRGBRegex.firstMatch(in: cssRGBString), - let redString = match.group(named: "red")?.value, - let greenString = match.group(named: "green")?.value, - let blueString = match.group(named: "blue")?.value, - let red = Double(redString), - let green = Double(greenString), - let blue = Double(blueString), + let match = cssRGBString.wholeMatch(of: Self.cssRGBRegex)?.output, + let red = Double(match.red), + let green = Double(match.green), + let blue = Double(match.blue), (0...255).contains(red), (0...255).contains(green), (0...255).contains(blue) @@ -741,7 +735,7 @@ extension NSColor { // https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lch() extension NSColor { - private static let cssLCHRegex = Regex(#"^\s*lch\((?[\d.]+)%\s+(?[\d.]+)\s+(?[\d.]+)(?:deg)?\s*(?\/\s+[\d.]+%?)?\)?;?$"#) + private static let cssLCHRegex = /^\s*lch\((?[\d.]+)%\s+(?[\d.]+)\s+(?[\d.]+)(?:deg)?\s*(?\/\s+[\d.]+%?)?\)?;?$/ // TODO: Support `alpha`, both percentage and float format. Right now we accept such colors, but ignore the alpha. // TODO: Write a lot of tests for the regex. @@ -750,13 +744,10 @@ extension NSColor { */ convenience init?(cssLCHString: String) { guard - let match = Self.cssLCHRegex.firstMatch(in: cssLCHString), - let lightnessString = match.group(named: "lightness")?.value, - let chromaString = match.group(named: "chroma")?.value, - let hueString = match.group(named: "hue")?.value, - let lightness = Double(lightnessString), - let chroma = Double(chromaString), - let hue = Double(hueString), + let match = cssLCHString.wholeMatch(of: Self.cssLCHRegex)?.output, + let lightness = Double(match.lightness), + let chroma = Double(match.chroma), + let hue = Double(match.hue), (0...100).contains(lightness), chroma >= 0, // Usually max 230, but theoretically unbounded. (0...360).contains(hue) @@ -1447,7 +1438,7 @@ extension NSMenu { @discardableResult func addSettingsItem() -> NSMenuItem { - addCallbackItem(OS.isMacOS13OrLater ? "Settings…" : "Preferences…", key: ",") { + addCallbackItem("Settings…", key: ",") { Task { await SSApp.showSettingsWindow() } @@ -1894,53 +1885,15 @@ extension RandomAccessCollection { } -/** -Create a `Picker` from an enum. - -- Note: The enum must conform to `CaseIterable`. - -``` -enum EventIndicatorsInCalendar: String, Codable, CaseIterable { - case none - case one - case maxThree - - var title: String { - switch self { - case .none: - return "None" - case .one: - return "Single Gray Dot" - case .maxThree: - return "Up To Three Colored Dots" - } - } -} - -struct ContentView: View { - @Default(.indicateEventsInCalendar) private var indicator - - var body: some View { - EnumPicker( - "Foo", - enumCase: $indicator - ) { element, isSelected in - Text(element.title) - } - } -} -``` -*/ struct EnumPicker: View where Enum: CaseIterable & Equatable, Enum.AllCases.Index: Hashable, Label: View, Content: View { - let enumBinding: Binding - @ViewBuilder let content: (Enum, Bool) -> Content + let selection: Binding + @ViewBuilder let content: (Enum) -> Content @ViewBuilder let label: () -> Label var body: some View { - Picker(selection: enumBinding.caseIndex) { + Picker(selection: selection.caseIndex) { // swiftlint:disable:this multiline_arguments ForEach(Array(Enum.allCases).indexed(), id: \.0) { index, element in - // TODO: Is `isSelected` really useful? If not, remove it. - content(element, element == enumBinding.wrappedValue) + content(element) .tag(index) } } label: { @@ -1952,10 +1905,10 @@ struct EnumPicker: View where Enum: CaseIterable & Equatab extension EnumPicker where Label == Text { init( _ title: some StringProtocol, - enumBinding: Binding, - @ViewBuilder content: @escaping (Enum, Bool) -> Content + selection: Binding, + @ViewBuilder content: @escaping (Enum) -> Content ) { - self.enumBinding = enumBinding + self.selection = selection self.content = content self.label = { Text(title) } } @@ -2103,7 +2056,7 @@ extension Colors { https://en.wikipedia.org/wiki/SRGB */ fileprivate static func sRGBToLinearSRGB(colorComponent: Double) -> Double { - colorComponent > 0.040_45 + colorComponent > 0.04045 ? pow((colorComponent + 0.055) / 1.055, 2.40) : (colorComponent / 12.92) } @@ -2114,7 +2067,7 @@ extension Colors { https://en.wikipedia.org/wiki/SRGB */ fileprivate static func linearSRGBToSRGB(colorComponent: Double) -> Double { - colorComponent > 0.003_130_8 + colorComponent > 0.0031308 ? (pow(colorComponent, 1.0 / 2.4) * 1.055 - 0.055) : (colorComponent * 12.92) } @@ -2131,9 +2084,9 @@ extension Colors { blue: Double ) -> (x: Double, y: Double, z: Double) { ( - x: (red * 0.412_456_4) + (green * 0.357_576_1) + (blue * 0.180_437_5), - y: (red * 0.212_672_9) + (green * 0.715_152_2) + (blue * 0.072_175_0), - z: (red * 0.019_333_9) + (green * 0.119_192_0) + (blue * 0.950_304_1) + x: (red * 0.4124564) + (green * 0.3575761) + (blue * 0.1804375), + y: (red * 0.2126729) + (green * 0.7151522) + (blue * 0.0721750), + z: (red * 0.0193339) + (green * 0.1191920) + (blue * 0.9503041) ) } @@ -2146,9 +2099,9 @@ extension Colors { z: Double ) -> (red: Double, green: Double, blue: Double) { ( - red: (x * 3.240_454_2) + (y * -1.537_138_5) + (z * -0.498_531_4), - green: (x * -0.969_266_0) + (y * 1.876_010_8) + (z * 0.041_556_0), - blue: (x * 0.055_643_4) + (y * -0.204_025_9) + (z * 1.057_225_2) + red: (x * 3.2404542) + (y * -1.5371385) + (z * -0.4985314), + green: (x * -0.9692660) + (y * 1.8760108) + (z * 0.0415560), + blue: (x * 0.0556434) + (y * -0.2040259) + (z * 1.0572252) ) } @@ -2163,9 +2116,9 @@ extension Colors { z: Double ) -> (x: Double, y: Double, z: Double) { ( - x: (x * 1.047_811_2) + (y * 0.022_886_6) + (z * -0.050_127_0), - y: (x * 0.029_542_4) + (y * 0.990_484_4) + (z * -0.017_049_1), - z: (x * -0.009_234_5) + (y * 0.015_043_6) + (z * 0.752_131_6) + x: (x * 1.0478112) + (y * 0.0228866) + (z * -0.0501270), + y: (x * 0.0295424) + (y * 0.9904844) + (z * -0.0170491), + z: (x * -0.0092345) + (y * 0.0150436) + (z * 0.7521316) ) } @@ -2180,9 +2133,9 @@ extension Colors { z: Double ) -> (x: Double, y: Double, z: Double) { ( - x: (x * 0.955_576_6) + (y * -0.023_039_3) + (z * 0.063_163_6), - y: (x * -0.028_289_5) + (y * 1.009_941_6) + (z * 0.021_007_7), - z: (x * 0.012_298_2) + (y * -0.020_483_0) + (z * 1.329_909_8) + x: (x * 0.9555766) + (y * -0.0230393) + (z * 0.0631636), + y: (x * -0.0282895) + (y * 1.0099416) + (z * 0.0210077), + z: (x * 0.0122982) + (y * -0.0204830) + (z * 1.3299098) ) } @@ -2202,9 +2155,9 @@ extension Colors { // swiftlint:enable identifier_name // Compute XYZ scaled relative to reference white. - let scaledX = x / 0.964_22 + let scaledX = x / 0.96422 let scaledY = y / 1.0 - let scaledZ = z / 0.825_21 + let scaledZ = z / 0.82521 func computeF(_ value: Double) -> Double { value > ε ? cbrt(value) : (κ * value + 16) / 116 @@ -2247,9 +2200,9 @@ extension Colors { // Scaled by reference white. return ( - x: x * 0.964_22, + x: x * 0.96422, y: y * 1.0, - z: z * 0.825_21 + z: z * 0.82521 ) } @@ -2456,12 +2409,9 @@ struct MultiCheckboxPicker: Vi @ViewBuilder var elementLabel: (Data.Element) -> ElementLabel var body: some View { - VStack(alignment: .leading) { - ForEach(data) { element in - Toggle(isOn: $selection.contains(element)) { - elementLabel(element) - } - .toggleStyle(.checkbox) + ForEach(data) { element in + Toggle(isOn: $selection.contains(element)) { + elementLabel(element) } } } @@ -2854,13 +2804,6 @@ extension View { } -extension Task { - public static func sleep(seconds: TimeInterval) async throws { - try await sleep(nanoseconds: UInt64(seconds * Double(NSEC_PER_SEC))) - } -} - - extension NSStatusItem { /** Show a one-time menu from the status item. @@ -2984,9 +2927,9 @@ extension OperatingSystem { /** - Note: Only use this when you cannot use an `if #available` check. For example, inline in function calls. */ - static let isMacOS14OrLater: Bool = { + static let isMacOS15OrLater: Bool = { #if os(macOS) - if #available(macOS 14, *) { + if #available(macOS 15, *) { return true } else { return false @@ -2999,9 +2942,9 @@ extension OperatingSystem { /** - Note: Only use this when you cannot use an `if #available` check. For example, inline in function calls. */ - static let isMacOS13OrLater: Bool = { + static let isMacOS14OrLater: Bool = { #if os(macOS) - if #available(macOS 13, *) { + if #available(macOS 14, *) { return true } else { return false diff --git a/Intents Extension/Info.plist b/Intents Extension/Info.plist new file mode 100644 index 0000000..8d15acb --- /dev/null +++ b/Intents Extension/Info.plist @@ -0,0 +1,11 @@ + + + + + EXAppExtensionAttributes + + EXExtensionPointIdentifier + com.apple.appintents-extension + + + diff --git a/Intents Extension/Intents.swift b/Intents Extension/Intents.swift new file mode 100644 index 0000000..c66f38a --- /dev/null +++ b/Intents Extension/Intents.swift @@ -0,0 +1,94 @@ +import AppKit +import AppIntents + +// Note: This is only in an extension as there is currently no way to detect that the app is running an in-app intent and prevent opening the color panel. + +struct GetRandomColorIntent: AppIntent, CustomIntentMigratedAppIntent { + static let intentClassName = "GetRandomColorIntent" + + static let title: LocalizedStringResource = "Get Random Color" + + static let description = IntentDescription( +""" +Returns a random color. + +The color formats Hex, HSL, RGB, and LCH are provided as individual properties. +""" + ) + + func perform() async throws -> some IntentResult & ReturnsValue { + .result(value: .init(.randomAvoidingBlackAndWhite())) + } +} + +struct SampleColorIntent: AppIntent, CustomIntentMigratedAppIntent { + static let intentClassName = "SampleColorIntent" + + static let title: LocalizedStringResource = "Sample Color from Screen" + + static let description = IntentDescription( +""" +Lets you pick a color from the screen. + +The color formats Hex, HSL, RGB, and LCH are provided as individual properties. +""" + ) + + func perform() async throws -> some IntentResult & ReturnsValue { + guard let color = await NSColorSampler().sample() else { + return .result(value: nil) + } + + return .result(value: .init(color)) + } +} + +struct Color_AppEntity: TransientAppEntity { + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Color") + + @Property(title: "Hex") + var hex: String + + @Property(title: "Hex Number") + var hexNumber: Int + + @Property(title: "HSL") + var hsl: String + + @Property(title: "RGB") + var rgb: String + + @Property(title: "LCH") + var lch: String + + @Property(title: "HSL Legacy") + var hslLegacy: String + + @Property(title: "RGB Legacy") + var rgbLegacy: String + + private var image: DisplayRepresentation.Image? + + var displayRepresentation: DisplayRepresentation { + .init( + title: "\(hex)", + subtitle: "\(rgb)", + image: image + ) + } +} + +extension Color_AppEntity { + init(_ nsColor: NSColor) { + let sRGBColor = nsColor.usingColorSpace(.sRGB)! + + self.hex = sRGBColor.format(.hex(hasPrefix: true)) + self.hexNumber = sRGBColor.hex + self.hsl = sRGBColor.format(.cssHSL) + self.rgb = sRGBColor.format(.cssRGB) + self.lch = nsColor.format(.cssLCH) + self.hslLegacy = sRGBColor.format(.cssHSLLegacy) + self.rgbLegacy = sRGBColor.format(.cssRGBLegacy) + self.image = .init(systemName: "square.fill", tintColor: sRGBColor) + } +} diff --git a/IntentsExtension/IntentsExtension.entitlements b/Intents Extension/Intents_Extension.entitlements similarity index 100% rename from IntentsExtension/IntentsExtension.entitlements rename to Intents Extension/Intents_Extension.entitlements diff --git a/Intents Extension/Main.swift b/Intents Extension/Main.swift new file mode 100644 index 0000000..bd30c7d --- /dev/null +++ b/Intents Extension/Main.swift @@ -0,0 +1,4 @@ +import AppIntents + +@main +struct Intents_ExtensionExtension: AppIntentsExtension {} diff --git a/IntentsExtension/Info.plist b/IntentsExtension/Info.plist deleted file mode 100644 index 200ad9f..0000000 --- a/IntentsExtension/Info.plist +++ /dev/null @@ -1,27 +0,0 @@ - - - - - ITSAppUsesNonExemptEncryption - - NSExtension - - NSExtensionAttributes - - IntentsRestrictedWhileLocked - - IntentsRestrictedWhileProtectedDataUnavailable - - IntentsSupported - - GetRandomColorIntent - SampleColorIntent - - - NSExtensionPointIdentifier - com.apple.intents-service - NSExtensionPrincipalClass - $(PRODUCT_MODULE_NAME).IntentHandler - - - diff --git a/IntentsExtension/IntentHandler.swift b/IntentsExtension/IntentHandler.swift deleted file mode 100644 index 33906fe..0000000 --- a/IntentsExtension/IntentHandler.swift +++ /dev/null @@ -1,61 +0,0 @@ -import Cocoa -import Intents - -extension Color_ { - fileprivate convenience init(_ nsColor: NSColor) { - let sRGBColor = nsColor.usingColorSpace(.sRGB)! - let thumbnail = NSImage.color(nsColor, size: CGSize(width: 1, height: 1)) - - self.init( - identifier: "color", - display: sRGBColor.hexString, - subtitle: sRGBColor.format(.cssRGB), - image: thumbnail.inImage - ) - - hex = sRGBColor.format(.hex()) - hexNumber = sRGBColor.hex as NSNumber - hsl = sRGBColor.format(.cssHSL) - rgb = sRGBColor.format(.cssRGB) - lch = nsColor.format(.cssLCH) - hslLegacy = sRGBColor.format(.cssHSLLegacy) - rgbLegacy = sRGBColor.format(.cssRGBLegacy) - } -} - -@MainActor -final class SampleColorIntentHandler: NSObject, SampleColorIntentHandling { - func handle(intent: SampleColorIntent) async -> SampleColorIntentResponse { - guard let color = await NSColorSampler().sample() else { - return .init(code: .failure, userActivity: nil) - } - - let response = SampleColorIntentResponse(code: .success, userActivity: nil) - response.color = Color_(color) - return response - } -} - -@MainActor -final class GetRandomColorIntentHandler: NSObject, GetRandomColorIntentHandling { - func handle(intent: GetRandomColorIntent) async -> GetRandomColorIntentResponse { - let response = GetRandomColorIntentResponse(code: .success, userActivity: nil) - response.color = Color_(.randomAvoidingBlackAndWhite()) - return response - } -} - -@MainActor -final class IntentHandler: INExtension { - override func handler(for intent: INIntent) -> Any? { - switch intent { - case is SampleColorIntent: - return SampleColorIntentHandler() - case is GetRandomColorIntent: - return GetRandomColorIntentHandler() - default: - assertionFailure("No handler for this intent") - return nil - } - } -}