diff --git a/Enchanted.xcodeproj/project.pbxproj b/Enchanted.xcodeproj/project.pbxproj index c861b7d..e072d99 100644 --- a/Enchanted.xcodeproj/project.pbxproj +++ b/Enchanted.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 3396E0762BD877D500213816 /* Menus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3396E0752BD877D500213816 /* Menus.swift */; }; FF0146CB2B3DA1DF00A2A9F6 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0146CA2B3DA1DF00A2A9F6 /* Settings.swift */; }; FF0146CD2B3DADCA00A2A9F6 /* HapticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0146CC2B3DADCA00A2A9F6 /* HapticsService.swift */; }; + FF0611EA2C04A11800443B33 /* Magnet in Frameworks */ = {isa = PBXBuildFile; platformFilters = (macos, ); productRef = FF0611E92C04A11800443B33 /* Magnet */; }; + FF0611F12C04A2FB00443B33 /* Splash in Frameworks */ = {isa = PBXBuildFile; productRef = FF0611F02C04A2FB00443B33 /* Splash */; }; + FF0611F32C04BEFE00443B33 /* AVSpeechSynthesisVoice+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0611F22C04BEFD00443B33 /* AVSpeechSynthesisVoice+Extension.swift */; }; FF1002302B2482BA0011A4DC /* ChatMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF10022F2B2482BA0011A4DC /* ChatMessageView.swift */; }; FF1002322B2483A20011A4DC /* Colours+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1002312B2483A20011A4DC /* Colours+Extension.swift */; }; FF1002392B24BBF20011A4DC /* Chat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1002382B24BBF20011A4DC /* Chat.swift */; }; @@ -34,7 +37,6 @@ FF1002732B276EC10011A4DC /* AppColorScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1002722B276EC10011A4DC /* AppColorScheme.swift */; }; FF1002752B278C170011A4DC /* AppStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1002742B278C170011A4DC /* AppStore.swift */; }; FF15EF6A2B826C0300D4A541 /* SimpleFloatingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF15EF692B826C0300D4A541 /* SimpleFloatingButton.swift */; }; - FF1BC3C52BA0753400A58043 /* Splash in Frameworks */ = {isa = PBXBuildFile; productRef = FF1BC3C42BA0753400A58043 /* Splash */; }; FF1BC3C72BA0757700A58043 /* SplashSyntaxHighlighter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1BC3C62BA0757700A58043 /* SplashSyntaxHighlighter+Extension.swift */; }; FF226A652BE2A0EC00CC91F1 /* SelectTextSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF226A642BE2A0EC00CC91F1 /* SelectTextSheet.swift */; }; FF24B30E2B66BE8500AB618F /* RunningBorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF24B30D2B66BE8500AB618F /* RunningBorder.swift */; }; @@ -75,7 +77,9 @@ FFB0327D2B312F3D0066A9DB /* RecordingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0327C2B312F3D0066A9DB /* RecordingView.swift */; }; FFB21A872B7BD0BA00D148A4 /* KeyboardReadable+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB21A862B7BD0BA00D148A4 /* KeyboardReadable+Extension.swift */; }; FFB56F432C0353CF0020AFFD /* ReadingAloudView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB56F422C0353CF0020AFFD /* ReadingAloudView.swift */; }; - FFB56F462C0383B80020AFFD /* ConversationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB56F452C0383B80020AFFD /* ConversationView.swift */; }; + FFB56F462C0383B80020AFFD /* VoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB56F452C0383B80020AFFD /* VoiceView.swift */; }; + FFBB2C7B2C03D8AD00509204 /* Voice.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBB2C7A2C03D8AD00509204 /* Voice.swift */; }; + FFBB2C7D2C047B6C00509204 /* Image+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBB2C7C2C047B6B00509204 /* Image+Extension.swift */; }; FFBBF4842B34881B008D611C /* SpeechRecogniser.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBBF4832B34881B008D611C /* SpeechRecogniser.swift */; }; FFBBF4882B34F9C8008D611C /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBBF4872B34F9C8008D611C /* View+Extension.swift */; }; FFBBF48A2B350283008D611C /* SelectedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBBF4892B350283008D611C /* SelectedImageView.swift */; }; @@ -98,7 +102,6 @@ FFEC9BE72B813A8D00AFBA63 /* RemovableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEC9BE62B813A8D00AFBA63 /* RemovableImage.swift */; }; FFF993592BDFBDCC004DCF19 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = FFF993582BDFBDCC004DCF19 /* MarkdownUI */; }; FFFD00C22B92607800392AE6 /* CompletionsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFD00C12B92607800392AE6 /* CompletionsStore.swift */; }; - FFFD00C62B94CA1300392AE6 /* Magnet in Frameworks */ = {isa = PBXBuildFile; platformFilters = (macos, ); productRef = FFFD00C52B94CA1300392AE6 /* Magnet */; }; FFFD00C92B94CB5E00392AE6 /* AsyncAlgorithms in Frameworks */ = {isa = PBXBuildFile; productRef = FFFD00C82B94CB5E00392AE6 /* AsyncAlgorithms */; }; /* End PBXBuildFile section */ @@ -106,6 +109,7 @@ 3396E0752BD877D500213816 /* Menus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menus.swift; sourceTree = ""; }; FF0146CA2B3DA1DF00A2A9F6 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; FF0146CC2B3DADCA00A2A9F6 /* HapticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticsService.swift; sourceTree = ""; }; + FF0611F22C04BEFD00443B33 /* AVSpeechSynthesisVoice+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVSpeechSynthesisVoice+Extension.swift"; sourceTree = ""; }; FF10022F2B2482BA0011A4DC /* ChatMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageView.swift; sourceTree = ""; }; FF1002312B2483A20011A4DC /* Colours+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Colours+Extension.swift"; sourceTree = ""; }; FF1002382B24BBF20011A4DC /* Chat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Chat.swift; sourceTree = ""; }; @@ -168,7 +172,9 @@ FFB0327C2B312F3D0066A9DB /* RecordingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingView.swift; sourceTree = ""; }; FFB21A862B7BD0BA00D148A4 /* KeyboardReadable+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyboardReadable+Extension.swift"; sourceTree = ""; }; FFB56F422C0353CF0020AFFD /* ReadingAloudView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingAloudView.swift; sourceTree = ""; }; - FFB56F452C0383B80020AFFD /* ConversationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationView.swift; sourceTree = ""; }; + FFB56F452C0383B80020AFFD /* VoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceView.swift; sourceTree = ""; }; + FFBB2C7A2C03D8AD00509204 /* Voice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Voice.swift; sourceTree = ""; }; + FFBB2C7C2C047B6B00509204 /* Image+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Image+Extension.swift"; sourceTree = ""; }; FFBBF4822B348345008D611C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; FFBBF4832B34881B008D611C /* SpeechRecogniser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpeechRecogniser.swift; sourceTree = ""; }; FFBBF4872B34F9C8008D611C /* View+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extension.swift"; sourceTree = ""; }; @@ -198,10 +204,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - FFFD00C62B94CA1300392AE6 /* Magnet in Frameworks */, - FF1BC3C52BA0753400A58043 /* Splash in Frameworks */, FFD5FAD52B8130CE0055AB51 /* OllamaKit in Frameworks */, FFD5FAD22B8130490055AB51 /* Vortex in Frameworks */, + FF0611F12C04A2FB00443B33 /* Splash in Frameworks */, + FF0611EA2C04A11800443B33 /* Magnet in Frameworks */, FF911DF12B98F08800915E94 /* WrappingHStack in Frameworks */, FF1002662B2653EE0011A4DC /* ActivityIndicatorView in Frameworks */, FFFD00C92B94CB5E00392AE6 /* AsyncAlgorithms in Frameworks */, @@ -216,18 +222,18 @@ FF10022C2B2481790011A4DC /* Components */ = { isa = PBXGroup; children = ( - FFD57E2E2BF2901A003FEFF1 /* ChatMessages */, FF10025F2B26499B0011A4DC /* ConversationStatusView.swift */, FF9300DB2B7823B0000859F4 /* EmptyConversaitonView.swift */, FF1002532B261D460011A4DC /* MessageListVIew.swift */, FF1002692B2731C60011A4DC /* ModelSelectorView.swift */, FF9C920B2BF0C088004C8275 /* OptionsMenuView.swift */, + FFB56F422C0353CF0020AFFD /* ReadingAloudView.swift */, FFEC9BE62B813A8D00AFBA63 /* RemovableImage.swift */, FF24B30D2B66BE8500AB618F /* RunningBorder.swift */, FFBBF4892B350283008D611C /* SelectedImageView.swift */, FF9300DD2B782A28000859F4 /* UnreachableAPIView.swift */, + FFD57E2E2BF2901A003FEFF1 /* ChatMessages */, FFB0327B2B312F310066A9DB /* Recorder */, - FFB56F422C0353CF0020AFFD /* ReadingAloudView.swift */, ); path = Components; sourceTree = ""; @@ -409,20 +415,21 @@ path = Recorder; sourceTree = ""; }; - FFB56F442C0383A60020AFFD /* Conversation */ = { + FFB56F442C0383A60020AFFD /* Voice */ = { isa = PBXGroup; children = ( - FFB56F452C0383B80020AFFD /* ConversationView.swift */, + FFB56F452C0383B80020AFFD /* VoiceView.swift */, + FFBB2C7A2C03D8AD00509204 /* Voice.swift */, ); - path = Conversation; + path = Voice; sourceTree = ""; }; FFD57E2E2BF2901A003FEFF1 /* ChatMessages */ = { isa = PBXGroup; children = ( FF10022F2B2482BA0011A4DC /* ChatMessageView.swift */, - FFD57E2F2BF29145003FEFF1 /* MarkdownColours.swift */, FFD57E312BF291B2003FEFF1 /* CodeBlockView.swift */, + FFD57E2F2BF29145003FEFF1 /* MarkdownColours.swift */, ); path = ChatMessages; sourceTree = ""; @@ -492,6 +499,8 @@ FFBBF4872B34F9C8008D611C /* View+Extension.swift */, FFB21A862B7BD0BA00D148A4 /* KeyboardReadable+Extension.swift */, FF1BC3C62BA0757700A58043 /* SplashSyntaxHighlighter+Extension.swift */, + FFBB2C7C2C047B6B00509204 /* Image+Extension.swift */, + FF0611F22C04BEFD00443B33 /* AVSpeechSynthesisVoice+Extension.swift */, ); path = Extensions; sourceTree = ""; @@ -539,7 +548,7 @@ FF38F8522B7AA9C400546B56 /* ApplicationEntry.swift */, FFEC32A82B24795A003E5C04 /* Chat */, FF15EF682B826BF400D4A541 /* Components */, - FFB56F442C0383A60020AFFD /* Conversation */, + FFB56F442C0383A60020AFFD /* Voice */, FF10026B2B2751630011A4DC /* Settings */, FF1002552B2624790011A4DC /* Sidebar */, ); @@ -575,12 +584,12 @@ FF1002652B2653EE0011A4DC /* ActivityIndicatorView */, FF464B122B8026DA008E5130 /* Vortex */, FFD5FAD42B8130CE0055AB51 /* OllamaKit */, - FFFD00C52B94CA1300392AE6 /* Magnet */, FFFD00C82B94CB5E00392AE6 /* AsyncAlgorithms */, FF911DF02B98F08800915E94 /* WrappingHStack */, - FF1BC3C42BA0753400A58043 /* Splash */, FF4A605C2BC3587800D7BD4F /* KeyboardShortcuts */, FFF993582BDFBDCC004DCF19 /* MarkdownUI */, + FF0611E92C04A11800443B33 /* Magnet */, + FF0611F02C04A2FB00443B33 /* Splash */, ); productName = Enchanted; productReference = FFEC328D2B24779A003E5C04 /* Enchanted.app */; @@ -614,12 +623,12 @@ FF1002642B2653EE0011A4DC /* XCRemoteSwiftPackageReference "ActivityIndicatorView" */, FF464B112B8026DA008E5130 /* XCRemoteSwiftPackageReference "Vortex" */, FFD5FAD32B8130CE0055AB51 /* XCRemoteSwiftPackageReference "OllamaKit" */, - FFFD00C42B94CA1300392AE6 /* XCRemoteSwiftPackageReference "Magnet" */, FFFD00C72B94CB5E00392AE6 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */, FF911DEF2B98F08800915E94 /* XCRemoteSwiftPackageReference "WrappingHStack" */, - FF1BC3C32BA0753400A58043 /* XCRemoteSwiftPackageReference "Splash" */, FF4A605B2BC3587800D7BD4F /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */, FFF993572BDFBDCC004DCF19 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */, + FF0611E82C04A11800443B33 /* XCRemoteSwiftPackageReference "Magnet" */, + FF0611EF2C04A2FB00443B33 /* XCRemoteSwiftPackageReference "Splash" */, ); productRefGroup = FFEC328E2B24779A003E5C04 /* Products */; projectDirPath = ""; @@ -665,13 +674,14 @@ FF66A51D2B76949A00FAAC1E /* Helpers.swift in Sources */, FF10026A2B2731C60011A4DC /* ModelSelectorView.swift in Sources */, FF38F84F2B7A7B6700546B56 /* MenuBarControlView_macOS.swift in Sources */, - FFB56F462C0383B80020AFFD /* ConversationView.swift in Sources */, + FFB56F462C0383B80020AFFD /* VoiceView.swift in Sources */, FFBBF48A2B350283008D611C /* SelectedImageView.swift in Sources */, FFEC9BDF2B8131B900AFBA63 /* HotKeys.swift in Sources */, FF2F03492B796A6500349855 /* HotkeyService.swift in Sources */, FF9300E02B783945000859F4 /* InputFields_macOS.swift in Sources */, FF38F8562B7AB01E00546B56 /* FloatingPanel.swift in Sources */, FF0146CB2B3DA1DF00A2A9F6 /* Settings.swift in Sources */, + FF0611F32C04BEFE00443B33 /* AVSpeechSynthesisVoice+Extension.swift in Sources */, FF38F8532B7AA9C400546B56 /* ApplicationEntry.swift in Sources */, FF6D82202B916CC3001183A8 /* CompletionsEditor.swift in Sources */, FF38F85C2B7ABC2C00546B56 /* PromptPanel.swift in Sources */, @@ -680,6 +690,7 @@ FF2F03442B79631800349855 /* Button+Extension.swift in Sources */, FF1002682B2668790011A4DC /* Date+Extension.swift in Sources */, FF6D821E2B916053001183A8 /* UpsertCompletionView.swift in Sources */, + FFBB2C7B2C03D8AD00509204 /* Voice.swift in Sources */, FF9300D42B778829000859F4 /* ChatView_macOS.swift in Sources */, FF38F8512B7A919C00546B56 /* MenuBarControl_macOS.swift in Sources */, FF38F85A2B7AB28300546B56 /* PromptPanelView.swift in Sources */, @@ -688,6 +699,7 @@ FF10025C2B2625BE0011A4DC /* SidebarView.swift in Sources */, FF1002392B24BBF20011A4DC /* Chat.swift in Sources */, FFB0327D2B312F3D0066A9DB /* RecordingView.swift in Sources */, + FFBB2C7D2C047B6C00509204 /* Image+Extension.swift in Sources */, FF6D82192B912804001183A8 /* CompletionButtonView.swift in Sources */, FF10024A2B25BE740011A4DC /* LanguageModelSD.swift in Sources */, FF6B7B132B3EE7AC00E8FEA3 /* Throttler.swift in Sources */, @@ -861,7 +873,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 31; + CURRENT_PROJECT_VERSION = 32; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Enchanted/Preview Content\""; DEVELOPMENT_TEAM = JDDZ55DT74; @@ -887,18 +899,18 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.6.7; + MARKETING_VERSION = 1.6.8; PRODUCT_BUNDLE_IDENTIFIER = subj.Enchanted; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Debug; }; @@ -911,7 +923,7 @@ CODE_SIGN_ENTITLEMENTS = Enchanted/Enchanted.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 31; + CURRENT_PROJECT_VERSION = 32; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Enchanted/Preview Content\""; DEVELOPMENT_TEAM = JDDZ55DT74; @@ -937,18 +949,18 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.6.7; + MARKETING_VERSION = 1.6.8; PRODUCT_BUNDLE_IDENTIFIER = subj.Enchanted; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Release; }; @@ -976,20 +988,28 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - FF1002642B2653EE0011A4DC /* XCRemoteSwiftPackageReference "ActivityIndicatorView" */ = { + FF0611E82C04A11800443B33 /* XCRemoteSwiftPackageReference "Magnet" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/exyte/ActivityIndicatorView.git"; + repositoryURL = "https://github.com/AugustDev/Magnet"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.1.1; + branch = master; + kind = branch; + }; + }; + FF0611EF2C04A2FB00443B33 /* XCRemoteSwiftPackageReference "Splash" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/AugustDev/Splash"; + requirement = { + branch = master; + kind = branch; }; }; - FF1BC3C32BA0753400A58043 /* XCRemoteSwiftPackageReference "Splash" */ = { + FF1002642B2653EE0011A4DC /* XCRemoteSwiftPackageReference "ActivityIndicatorView" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/JohnSundell/Splash"; + repositoryURL = "https://github.com/exyte/ActivityIndicatorView.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.16.0; + minimumVersion = 1.1.1; }; }; FF464B112B8026DA008E5130 /* XCRemoteSwiftPackageReference "Vortex" */ = { @@ -1032,14 +1052,6 @@ kind = branch; }; }; - FFFD00C42B94CA1300392AE6 /* XCRemoteSwiftPackageReference "Magnet" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/AugustDev/Magnet"; - requirement = { - branch = master; - kind = branch; - }; - }; FFFD00C72B94CB5E00392AE6 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-async-algorithms.git"; @@ -1051,16 +1063,21 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - FF1002652B2653EE0011A4DC /* ActivityIndicatorView */ = { + FF0611E92C04A11800443B33 /* Magnet */ = { isa = XCSwiftPackageProductDependency; - package = FF1002642B2653EE0011A4DC /* XCRemoteSwiftPackageReference "ActivityIndicatorView" */; - productName = ActivityIndicatorView; + package = FF0611E82C04A11800443B33 /* XCRemoteSwiftPackageReference "Magnet" */; + productName = Magnet; }; - FF1BC3C42BA0753400A58043 /* Splash */ = { + FF0611F02C04A2FB00443B33 /* Splash */ = { isa = XCSwiftPackageProductDependency; - package = FF1BC3C32BA0753400A58043 /* XCRemoteSwiftPackageReference "Splash" */; + package = FF0611EF2C04A2FB00443B33 /* XCRemoteSwiftPackageReference "Splash" */; productName = Splash; }; + FF1002652B2653EE0011A4DC /* ActivityIndicatorView */ = { + isa = XCSwiftPackageProductDependency; + package = FF1002642B2653EE0011A4DC /* XCRemoteSwiftPackageReference "ActivityIndicatorView" */; + productName = ActivityIndicatorView; + }; FF464B122B8026DA008E5130 /* Vortex */ = { isa = XCSwiftPackageProductDependency; package = FF464B112B8026DA008E5130 /* XCRemoteSwiftPackageReference "Vortex" */; @@ -1086,11 +1103,6 @@ package = FFF993572BDFBDCC004DCF19 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */; productName = MarkdownUI; }; - FFFD00C52B94CA1300392AE6 /* Magnet */ = { - isa = XCSwiftPackageProductDependency; - package = FFFD00C42B94CA1300392AE6 /* XCRemoteSwiftPackageReference "Magnet" */; - productName = Magnet; - }; FFFD00C82B94CB5E00392AE6 /* AsyncAlgorithms */ = { isa = XCSwiftPackageProductDependency; package = FFFD00C72B94CB5E00392AE6 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */; diff --git a/Enchanted.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Enchanted.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index bdfdd64..9240687 100644 --- a/Enchanted.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Enchanted.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "833fa47aa42a8385a50e6f20c5125e66acefe26c9777986dddce89968d1daceb", + "originHash" : "2ecdf9d081540d56a3d35c3ac234eabac50668e7cc364d29a44a05e51449bba1", "pins" : [ { "identity" : "activityindicatorview", @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Alamofire/Alamofire.git", "state" : { - "revision" : "723fa5a6c65812aec4a0d7cc432ee198883b6e00", - "version" : "5.9.0" + "revision" : "f455c2975872ccd2d9c81594c658af65716e9b9a", + "version" : "5.9.1" } }, { @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sindresorhus/KeyboardShortcuts", "state" : { - "revision" : "09e4a10ed6b65b3a40fe07b6bf0c84809313efc4", - "version" : "2.0.0" + "revision" : "2e5f15581fefb821d4b366e57d817be8bf12aa58", + "version" : "2.0.1" } }, { @@ -67,10 +67,10 @@ { "identity" : "splash", "kind" : "remoteSourceControl", - "location" : "https://github.com/JohnSundell/Splash", + "location" : "https://github.com/AugustDev/Splash", "state" : { - "revision" : "7f4df436eb78fe64fe2c32c58006e9949fa28ad8", - "version" : "0.16.0" + "branch" : "master", + "revision" : "c31eba0866102be9be29391dac641ecb46795702" } }, { diff --git a/Enchanted.xcodeproj/xcshareddata/xcschemes/Enchanted.xcscheme b/Enchanted.xcodeproj/xcshareddata/xcschemes/Enchanted.xcscheme index 5b57e54..072d855 100644 --- a/Enchanted.xcodeproj/xcshareddata/xcschemes/Enchanted.xcscheme +++ b/Enchanted.xcodeproj/xcshareddata/xcschemes/Enchanted.xcscheme @@ -55,7 +55,7 @@ isEnabled = "NO"> diff --git a/Enchanted.xcodeproj/xcuserdata/wpc.xcuserdatad/xcschemes/xcschememanagement.plist b/Enchanted.xcodeproj/xcuserdata/wpc.xcuserdatad/xcschemes/xcschememanagement.plist index 726da57..bbf6a1c 100644 --- a/Enchanted.xcodeproj/xcuserdata/wpc.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Enchanted.xcodeproj/xcuserdata/wpc.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ Enchanted.xcscheme_^#shared#^_ orderHint - 0 + 1 SuppressBuildableAutocreation diff --git a/Enchanted/Assets.xcassets/AppIcon.appiconset/ios.png b/Enchanted/Assets.xcassets/AppIcon.appiconset/ios.png deleted file mode 100644 index 7bd6c86..0000000 Binary files a/Enchanted/Assets.xcassets/AppIcon.appiconset/ios.png and /dev/null differ diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..0bf52c8 --- /dev/null +++ b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "vision-back.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/vision-back.png b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/vision-back.png new file mode 100644 index 0000000..02a44e3 Binary files /dev/null and b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/vision-back.png differ diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Contents.json new file mode 100644 index 0000000..950af4d --- /dev/null +++ b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.solidimagestacklayer" + }, + { + "filename" : "Middle.solidimagestacklayer" + }, + { + "filename" : "Back.solidimagestacklayer" + } + ] +} diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..ea7dab4 --- /dev/null +++ b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "vision-front.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/vision-front.png b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/vision-front.png new file mode 100644 index 0000000..350a9f0 Binary files /dev/null and b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/vision-front.png differ diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..ea7dab4 --- /dev/null +++ b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "vision-front.png", + "idiom" : "vision", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/vision-front.png b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/vision-front.png new file mode 100644 index 0000000..350a9f0 Binary files /dev/null and b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/vision-front.png differ diff --git a/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Enchanted/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Enchanted/Extensions/AVSpeechSynthesisVoice+Extension.swift b/Enchanted/Extensions/AVSpeechSynthesisVoice+Extension.swift new file mode 100644 index 0000000..0a031bf --- /dev/null +++ b/Enchanted/Extensions/AVSpeechSynthesisVoice+Extension.swift @@ -0,0 +1,29 @@ +// +// AVSpeechSynthesisVoice+Extension.swift +// Enchanted +// +// Created by Augustinas Malinauskas on 27/05/2024. +// + +import Foundation +import AVFoundation + +extension AVSpeechSynthesisVoice { + var prettyName: String { + let name = self.name + if name.lowercased().contains("default") || name.lowercased().contains("premium") || name.lowercased().contains("enhanced") { + return name + } + + let qualityString = { + switch self.quality.rawValue { + case 1: return "Default" + case 2: return "Enhanced" + case 3: return "Premium" + default: return "Unknown" + } + }() + + return "\(name) (\(qualityString))" + } +} diff --git a/Enchanted/Extensions/Image+Extension.swift b/Enchanted/Extensions/Image+Extension.swift new file mode 100644 index 0000000..ccb3c74 --- /dev/null +++ b/Enchanted/Extensions/Image+Extension.swift @@ -0,0 +1,24 @@ +// +// Image+Extension.swift +// Enchanted +// +// Created by Augustinas Malinauskas on 27/05/2024. +// + +import SwiftUI + +#if os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) +extension Image { + init?(data: Data) { + guard let uiImage = UIImage(data: data) else { return nil } + self.init(uiImage: uiImage) + } +} +#elseif os(macOS) +extension Image { + init?(data: Data) { + guard let nsImage = NSImage(data: data) else { return nil } + self.init(nsImage: nsImage) + } +} +#endif diff --git a/Enchanted/Extensions/KeyboardReadable+Extension.swift b/Enchanted/Extensions/KeyboardReadable+Extension.swift index 85de92a..2b4dc21 100644 --- a/Enchanted/Extensions/KeyboardReadable+Extension.swift +++ b/Enchanted/Extensions/KeyboardReadable+Extension.swift @@ -28,7 +28,7 @@ extension KeyboardReadable { .eraseToAnyPublisher() } } -#elseif os(macOS) +#elseif os(macOS) || os(visionOS) /// Mock protocol protocol KeyboardReadable {} #endif diff --git a/Enchanted/Extensions/SplashSyntaxHighlighter+Extension.swift b/Enchanted/Extensions/SplashSyntaxHighlighter+Extension.swift index 66b67b6..5ac7704 100644 --- a/Enchanted/Extensions/SplashSyntaxHighlighter+Extension.swift +++ b/Enchanted/Extensions/SplashSyntaxHighlighter+Extension.swift @@ -6,8 +6,9 @@ // import MarkdownUI -import Splash import SwiftUI +import Splash + struct SplashCodeSyntaxHighlighter: CodeSyntaxHighlighter { private let syntaxHighlighter: SyntaxHighlighter diff --git a/Enchanted/Extensions/UIImage+Extension.swift b/Enchanted/Extensions/UIImage+Extension.swift index cd59628..386cd0a 100644 --- a/Enchanted/Extensions/UIImage+Extension.swift +++ b/Enchanted/Extensions/UIImage+Extension.swift @@ -7,7 +7,7 @@ import SwiftUI -#if os(iOS) +#if os(iOS) || os(visionOS) extension UIImage { func convertImageToBase64String() -> String { return self.jpegData(compressionQuality: 1)?.base64EncodedString() ?? "" diff --git a/Enchanted/Extensions/View+Extension.swift b/Enchanted/Extensions/View+Extension.swift index fe99a95..84c2d1c 100644 --- a/Enchanted/Extensions/View+Extension.swift +++ b/Enchanted/Extensions/View+Extension.swift @@ -44,7 +44,7 @@ extension View { renderer.scale = displayScale -#if os(iOS) +#if os(iOS) || os(visionOS) let image = renderer.uiImage #elseif os(macOS) let image = renderer.nsImage @@ -54,6 +54,22 @@ extension View { } } +extension View { + /// Applies the given transform if the given condition evaluates to `true`. + /// - Parameters: + /// - condition: The condition to evaluate. + /// - transform: The transform to apply to the source `View`. + /// - Returns: Either the original `View` or the modified `View` if the condition is `true`. + /// https://www.avanderlee.com/swiftui/conditional-view-modifier/ + @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View { + if condition { + transform(self) + } else { + self + } + } +} + struct GradientForegroundStyle: ViewModifier { func body(content: Content) -> some View { content.foregroundStyle( diff --git a/Enchanted/Services/Clipboard.swift b/Enchanted/Services/Clipboard.swift index 42b71ef..70f3b05 100644 --- a/Enchanted/Services/Clipboard.swift +++ b/Enchanted/Services/Clipboard.swift @@ -14,7 +14,7 @@ import UIKit #endif -class Clipboard { +final class Clipboard: Sendable { static let shared = Clipboard() func setString(_ message: String) { @@ -42,7 +42,7 @@ class Clipboard { } func getText() -> String? { -#if os(iOS) +#if os(iOS) || os(visionOS) return UIPasteboard.general.string #elseif os(macOS) return NSPasteboard.general.string(forType: .string) diff --git a/Enchanted/Services/HapticsService.swift b/Enchanted/Services/HapticsService.swift index 29b88e9..41cc13e 100644 --- a/Enchanted/Services/HapticsService.swift +++ b/Enchanted/Services/HapticsService.swift @@ -35,7 +35,7 @@ class Haptics: @unchecked Sendable { play(.medium) } } -#elseif os(macOS) +#else class Haptics { static let shared = Haptics() func lightTap() {} diff --git a/Enchanted/Services/SpeechService.swift b/Enchanted/Services/SpeechService.swift index 98bfd09..9d5aff7 100644 --- a/Enchanted/Services/SpeechService.swift +++ b/Enchanted/Services/SpeechService.swift @@ -38,7 +38,7 @@ class SpeechSynthesizerDelegate: NSObject, AVSpeechSynthesizerDelegate { override init() { super.init() synthesizer.delegate = delegate - voices = listAllVoices() + fetchVoices() } func getVoiceIdentifier() -> String? { @@ -103,10 +103,19 @@ class SpeechSynthesizerDelegate: NSObject, AVSpeechSynthesizerDelegate { } - func listAllVoices() -> [AVSpeechSynthesisVoice] { + func fetchVoices() { let voices = AVSpeechSynthesisVoice.speechVoices().sorted { (firstVoice: AVSpeechSynthesisVoice, secondVoice: AVSpeechSynthesisVoice) -> Bool in return firstVoice.quality.rawValue > secondVoice.quality.rawValue } - return voices + + /// prevent state refresh if there are no new elements + let diff = self.voices.elementsEqual(voices, by: { $0.identifier == $1.identifier }) + if diff { + return + } + + DispatchQueue.main.async { + self.voices = voices + } } } diff --git a/Enchanted/Stores/AppStore.swift b/Enchanted/Stores/AppStore.swift index 45cd2a1..9780f94 100644 --- a/Enchanted/Stores/AppStore.swift +++ b/Enchanted/Stores/AppStore.swift @@ -9,6 +9,11 @@ import Foundation import Combine import SwiftUI +enum AppState { + case chat + case voice +} + @Observable final class AppStore { static let shared = AppStore() @@ -19,6 +24,7 @@ final class AppStore { @MainActor var isReachable: Bool = true @MainActor var notifications: [NotificationMessage] = [] @MainActor var menuBarIcon: String? = nil + var appState: AppState = .chat init() { if let storedIntervalString = UserDefaults.standard.string(forKey: "pingInterval") { diff --git a/Enchanted/UI/Helpers.swift b/Enchanted/UI/Helpers.swift index fbf38cf..0254668 100644 --- a/Enchanted/UI/Helpers.swift +++ b/Enchanted/UI/Helpers.swift @@ -7,8 +7,10 @@ import SwiftUI -#if os(iOS) +#if os(iOS) || os(visionOS) typealias PlatformImage = UIImage -#elseif os(macOS) +#else typealias PlatformImage = NSImage #endif + +//Image(nsImage: nsImage) diff --git a/Enchanted/UI/Shared/ApplicationEntry.swift b/Enchanted/UI/Shared/ApplicationEntry.swift index ab6f6c0..34c6f32 100644 --- a/Enchanted/UI/Shared/ApplicationEntry.swift +++ b/Enchanted/UI/Shared/ApplicationEntry.swift @@ -14,32 +14,39 @@ struct ApplicationEntry: View { @State private var conversationStore = ConversationStore.shared @State private var completionsStore = CompletionsStore.shared @State private var appStore = AppStore.shared - + var body: some View { - Chat(languageModelStore: languageModelStore, conversationStore: conversationStore, appStore: appStore) - .task { - - if let bundleIdentifier = Bundle.main.bundleIdentifier { - print("Bundle Identifier: \(bundleIdentifier)") - } else { - print("Bundle Identifier not found.") - } + VStack { + switch appStore.appState { + case .chat: + Chat(languageModelStore: languageModelStore, conversationStore: conversationStore, appStore: appStore) + case .voice: + Voice(languageModelStore: languageModelStore, conversationStore: conversationStore, appStore: appStore) + } + } + .task { + + if let bundleIdentifier = Bundle.main.bundleIdentifier { + print("Bundle Identifier: \(bundleIdentifier)") + } else { + print("Bundle Identifier not found.") + } + + Task.detached { + async let loadModels: () = languageModelStore.loadModels() + async let loadConversations: () = conversationStore.loadConversations() + async let loadCompletions: () = completionsStore.load() - Task.detached { - async let loadModels: () = languageModelStore.loadModels() - async let loadConversations: () = conversationStore.loadConversations() - async let loadCompletions: () = completionsStore.load() - - do { - _ = try await loadModels - _ = try await loadConversations - _ = try await loadCompletions - } catch { - print("Unexpected error: \(error).") - } + do { + _ = try await loadModels + _ = try await loadConversations + _ = try await loadCompletions + } catch { + print("Unexpected error: \(error).") } } - .preferredColorScheme(colorScheme.toiOSFormat) + } + .preferredColorScheme(colorScheme.toiOSFormat) } } diff --git a/Enchanted/UI/Shared/Chat/Chat.swift b/Enchanted/UI/Shared/Chat/Chat.swift index 0cbe0dc..bc9b864 100644 --- a/Enchanted/UI/Shared/Chat/Chat.swift +++ b/Enchanted/UI/Shared/Chat/Chat.swift @@ -124,7 +124,7 @@ struct Chat: View, Sendable { var body: some View { Group { -#if os(macOS) +#if os(macOS) || os(visionOS) ChatView( selectedConversation: conversationStore.selectedConversation, conversations: conversationStore.conversations, diff --git a/Enchanted/UI/Shared/Chat/Components/ChatMessages/ChatMessageView.swift b/Enchanted/UI/Shared/Chat/Components/ChatMessages/ChatMessageView.swift index 93b3f4b..010b9d0 100644 --- a/Enchanted/UI/Shared/Chat/Components/ChatMessages/ChatMessageView.swift +++ b/Enchanted/UI/Shared/Chat/Components/ChatMessages/ChatMessageView.swift @@ -7,8 +7,8 @@ import SwiftUI import MarkdownUI -import Splash import ActivityIndicatorView +import Splash struct ChatMessageView: View { @Environment(\.colorScheme) var colorScheme @@ -25,8 +25,8 @@ struct ChatMessageView: View { return message.role == "user" ? userInitialsNotEmpty.uppercased() : "AI" } - var image: PlatformImage? { - message.image != nil ? PlatformImage(data: message.image!) : nil + var image: Image? { + message.image != nil ? Image(data: message.image!) : nil } private var codeHighlightColorScheme: Splash.Theme { @@ -39,71 +39,52 @@ struct ChatMessageView: View { } var body: some View { - ZStack(alignment: .topTrailing) { - VStack { - HStack(alignment: .top, spacing: 12) { + VStack(alignment: .trailing, spacing: 0) { + HStack(alignment: .firstTextBaseline) { + Group { if message.role == "user" { - ZStack { - Circle() - .foregroundColor(.green) - - Text(roleName) - .font(.system(size: 11)) - .foregroundStyle(.background) - - } - .frame(width: 24, height: 24) - + Spacer() } else { - Image("logo-nobg") - .resizable() - .scaledToFit() - .frame(width: 24, height: 24) - } - - VStack(alignment: .leading) { - HStack { - Text(message.role.capitalized) - .font(.system(size: 14)) - .fontWeight(.medium) - .padding(.bottom, 2) - .frame(height: 27) - + if showLoader { ActivityIndicatorView(isVisible: .constant(true), type: .rotatingDots(count: 5)) - .frame(width: 20, height: 20) - .rotationEffect(.degrees(-90)) - .showIf(showLoader) - } - - Markdown(message.content) -#if os(macOS) - .textSelection(.enabled) -#endif - .markdownCodeSyntaxHighlighter(.splash(theme: codeHighlightColorScheme)) - .markdownTheme(MarkdownColours.enchantedTheme) - - if let uiImage = image { -#if os(iOS) - Image(uiImage: uiImage) - .resizable() - .scaledToFit() - .frame(width: 100) - .clipShape(RoundedRectangle(cornerRadius: 5)) -#elseif os(macOS) - Image(nsImage: uiImage) + .frame(width: 24, height: 24) + .rotationEffect(.degrees(90)) + } else { + Image("logo-nobg") .resizable() .scaledToFit() - .frame(width: 100) - .clipShape(RoundedRectangle(cornerRadius: 5)) -#endif - + .frame(width: 24, height: 24) } } + } + .offset(CGSize(width: 0, height: 6)) + + VStack(alignment: .leading) { + Markdown(message.content) +#if os(macOS) + .textSelection(.enabled) +#endif + .markdownCodeSyntaxHighlighter(.splash(theme: codeHighlightColorScheme)) + .markdownTheme(MarkdownColours.enchantedTheme) + if let uiImage = image { + uiImage + .resizable() + .scaledToFit() + .frame(width: 100) + .clipShape(RoundedRectangle(cornerRadius: 5)) + + } + } + .if(message.role == "user", transform: { v in + v.padding() + .background(RoundedRectangle(cornerRadius: 25).fill(.regularMaterial)) + }) + + if message.role != "user" { Spacer() } } - #if os(macOS) HStack(spacing: 0) { /// Copy button @@ -113,7 +94,6 @@ struct ChatMessageView: View { } .buttonStyle(GrowingButton()) .clipShape(RoundedRectangle(cornerRadius: 10)) - .showIf(mouseHover) /// Play button Button(action: { @@ -131,7 +111,6 @@ struct ChatMessageView: View { } .buttonStyle(GrowingButton()) .clipShape(RoundedRectangle(cornerRadius: 10)) - .showIf(mouseHover) .showIf(!isSpeaking) /// Stop button @@ -147,7 +126,6 @@ struct ChatMessageView: View { } .buttonStyle(GrowingButton()) .clipShape(RoundedRectangle(cornerRadius: 10)) - .showIf(mouseHover) .showIf(isSpeaking) /// Edit button @@ -157,9 +135,9 @@ struct ChatMessageView: View { } .buttonStyle(GrowingButton()) .clipShape(RoundedRectangle(cornerRadius: 10)) - .showIf(mouseHover) .showIf(message.role == "user") } + .opacity(mouseHover ? 1 : 0.0001) #endif } @@ -178,7 +156,10 @@ struct ChatMessageView: View { ChatMessageView(message: MessageSD.sample[0], userInitials: "AM", editMessage: .constant(nil)) .previewLayout(.sizeThatFits) - ChatMessageView(message: MessageSD(content: "```python \nprint(5+5)\n```", role: "ai"), userInitials: "AM", editMessage: .constant(nil)) + ChatMessageView(message: MessageSD.sample[1], userInitials: "AM", editMessage: .constant(nil)) + .previewLayout(.sizeThatFits) + + ChatMessageView(message: MessageSD(content: "```python \nprint(5+5)\n```", role: "ai"), showLoader: true, userInitials: "AM", editMessage: .constant(nil)) .previewLayout(.sizeThatFits) } } diff --git a/Enchanted/UI/Shared/Chat/Components/EmptyConversaitonView.swift b/Enchanted/UI/Shared/Chat/Components/EmptyConversaitonView.swift index 5fc77d3..95906ab 100644 --- a/Enchanted/UI/Shared/Chat/Components/EmptyConversaitonView.swift +++ b/Enchanted/UI/Shared/Chat/Components/EmptyConversaitonView.swift @@ -39,8 +39,11 @@ struct EmptyConversaitonView: View, KeyboardReadable { ) Text("How can I help you today?") - .font(.system(size: 25)) + .font(.title2) + .fontWeight(.light) + #if !os(visionOS) .foregroundStyle(Color(.systemGray)) + #endif LazyVGrid(columns: columns, alignment: .leading, spacing: 15) { ForEach(0.. String { - switch quality { - case 1: return "Default" - case 2: return "Enhanced" - case 3: return "Premium" - default: return "Unknown" - } - - } - var body: some View { VStack { ZStack { @@ -73,11 +63,13 @@ struct SettingsView: View { Form { Section(header: Text("Ollama").font(.headline)) { + TextField("Ollama server URI", text: $ollamaUri, onCommit: checkServer) .textContentType(.URL) .disableAutocorrection(true) .textFieldStyle(RoundedBorderTextFieldStyle()) -#if os(iOS) +#if !os(macOS) + .padding(.top, 8) .keyboardType(.URL) .autocapitalization(.none) #endif @@ -140,8 +132,8 @@ struct SettingsView: View { } Picker(selection: $voiceIdentifier) { - ForEach(voices, id:\.self) { voice in - Text("\(voice.name) (\(voiceQualityPrettify(voice.quality.rawValue)))").tag(voice.identifier) + ForEach(voices, id:\.self.identifier) { voice in + Text(voice.prettyName).tag(voice.identifier) } } label: { Label("Voice", systemImage: "waveform") diff --git a/Enchanted/UI/Shared/Voice/Voice.swift b/Enchanted/UI/Shared/Voice/Voice.swift new file mode 100644 index 0000000..d9ff194 --- /dev/null +++ b/Enchanted/UI/Shared/Voice/Voice.swift @@ -0,0 +1,18 @@ +// +// Voice.swift +// Enchanted +// +// Created by Augustinas Malinauskas on 26/05/2024. +// + +import SwiftUI + +struct Voice: View { + @State var languageModelStore: LanguageModelStore + @State var conversationStore: ConversationStore + @State var appStore: AppStore + + var body: some View { + VoiceView() + } +} diff --git a/Enchanted/UI/Shared/Voice/VoiceView.swift b/Enchanted/UI/Shared/Voice/VoiceView.swift new file mode 100644 index 0000000..ff694fd --- /dev/null +++ b/Enchanted/UI/Shared/Voice/VoiceView.swift @@ -0,0 +1,18 @@ +// +// ConversationView.swift +// Enchanted +// +// Created by Augustinas Malinauskas on 26/05/2024. +// + +import SwiftUI + +struct VoiceView: View { + var body: some View { + Text("hello") + } +} + +#Preview { + VoiceView() +} diff --git a/Enchanted/UI/iOS/Components/SelectTextSheet.swift b/Enchanted/UI/iOS/Components/SelectTextSheet.swift index 83e3cef..953ee36 100644 --- a/Enchanted/UI/iOS/Components/SelectTextSheet.swift +++ b/Enchanted/UI/iOS/Components/SelectTextSheet.swift @@ -5,7 +5,7 @@ // Created by Augustinas Malinauskas on 01/05/2024. // -#if os(iOS) +#if os(iOS) || os(visionOS) import SwiftUI import UIKit @@ -23,7 +23,7 @@ struct SelectTextSheet: View { HStack { Spacer() - Button(action: {}) { + Button(action: {presentationMode.wrappedValue.dismiss()}) { Image(systemName: "x.circle.fill") .padding(7) } @@ -40,6 +40,9 @@ struct SelectTextSheet: View { textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument) } } + #if os(visionOS) + .frame(width: 600, height: 600) + #endif } .textSelection(.enabled) diff --git a/Enchanted/UI/macOS/Chat/ChatView_macOS.swift b/Enchanted/UI/macOS/Chat/ChatView_macOS.swift index 9513586..1446225 100644 --- a/Enchanted/UI/macOS/Chat/ChatView_macOS.swift +++ b/Enchanted/UI/macOS/Chat/ChatView_macOS.swift @@ -5,7 +5,7 @@ // Created by Augustinas Malinauskas on 10/02/2024. // -#if os(macOS) +#if os(macOS) || os(visionOS) import SwiftUI struct ChatView: View { @@ -31,10 +31,8 @@ struct ChatView: View { @State private var message = "" @State private var editMessage: MessageSD? - @FocusState private var isFocusedInput: Bool - - @StateObject var speechRecognizer = SpeechRecognizer() @State var isRecording = false + @FocusState private var isFocusedInput: Bool var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { @@ -45,13 +43,29 @@ struct ChatView: View { onConversationDelete: onConversationDelete, onDeleteDailyConversations: onDeleteDailyConversations ) + .toolbar { +#if os(visionOS) + ToolbarItemGroup(placement:.navigationBarTrailing) { + Button(action: { + withAnimation(.easeIn(duration: 0.3)) { + columnVisibility = .detailOnly + } + }) { + Image(systemName: "sidebar.left") + } + .buttonStyle(PlainButtonStyle()) + .showIf(columnVisibility != .detailOnly) + } + +#endif + } } detail: { VStack(alignment: .center) { if selectedConversation != nil { MessageListView( messages: messages, conversationState: conversationState, - userInitials: userInitials, + userInitials: userInitials, editMessage: $editMessage ) } else { @@ -71,22 +85,46 @@ struct ChatView: View { conversationState: conversationState, onStopGenerateTap: onStopGenerateTap, selectedModel: selectedModel, - onSendMessageTap: onSendMessageTap, + onSendMessageTap: onSendMessageTap, editMessage: $editMessage ) .padding() .frame(maxWidth: 800) } + .toolbar { + #if os(visionOS) + ToolbarItemGroup(placement: .topBarLeading) { + Button(action: { + withAnimation { + columnVisibility = .automatic + } + }) { + Image(systemName: "sidebar.left") + } + .buttonStyle(PlainButtonStyle()) + .showIf(columnVisibility == .detailOnly) + + Text("Enchanted") + } + #else + ToolbarItem(placement: .navigation) { + Text("Enchanted") + } + #endif + + + ToolbarItemGroup(placement: .automatic) { + ToolbarView( + modelsList: modelsList, + selectedModel: selectedModel, + onSelectModel: onSelectModel, + onNewConversationTap: onNewConversationTap, + copyChat: copyChat + ) + } + } } - .toolbar { - ToolbarView( - modelsList: modelsList, - selectedModel: selectedModel, - onSelectModel: onSelectModel, - onNewConversationTap: onNewConversationTap, - copyChat: copyChat - ) - } + .navigationTitle("") .onChange(of: editMessage, initial: false) { _, newMessage in if let newMessage = newMessage { message = newMessage.content diff --git a/Enchanted/UI/macOS/Chat/Components/InputFields_macOS.swift b/Enchanted/UI/macOS/Chat/Components/InputFields_macOS.swift index 6988954..4cf72e2 100644 --- a/Enchanted/UI/macOS/Chat/Components/InputFields_macOS.swift +++ b/Enchanted/UI/macOS/Chat/Components/InputFields_macOS.swift @@ -5,7 +5,7 @@ // Created by Augustinas Malinauskas on 10/02/2024. // -#if os(macOS) +#if os(macOS) || os(visionOS) import SwiftUI struct InputFieldsView: View { @@ -44,6 +44,7 @@ struct InputFieldsView: View { selectedImage = image } +#if os(macOS) var hotkeys: [HotkeyCombination] { [ HotkeyCombination(keyBase: [.command], key: .kVK_ANSI_V) { @@ -54,6 +55,7 @@ struct InputFieldsView: View { } ] } +#endif var body: some View { HStack(spacing: 20) { @@ -71,6 +73,7 @@ struct InputFieldsView: View { .frame(minHeight: 40) .font(.system(size: 14)) .textFieldStyle(.plain) +#if os(macOS) .onSubmit { if NSApp.currentEvent?.modifierFlags.contains(.shift) == true { message += "\n" @@ -78,9 +81,12 @@ struct InputFieldsView: View { sendMessage() } } +#endif /// TextField bypasses drop area .allowsHitTesting(!fileDropActive) +#if os(macOS) .addCustomHotkeys(hotkeys) +#endif RecordingView(isRecording: $isRecording.animation()) { transcription in withAnimation(.easeIn(duration: 0.3)) { @@ -96,9 +102,8 @@ struct InputFieldsView: View { switch result { case .success(let url): guard url.startAccessingSecurityScopedResource() else { return } - if let imageData = try? Data(contentsOf: url), - let nsImage = NSImage(data: imageData) { - selectedImage = Image(nsImage: nsImage) + if let imageData = try? Data(contentsOf: url) { + selectedImage = Image(data: imageData) } url.stopAccessingSecurityScopedResource() case .failure(let error): @@ -134,9 +139,7 @@ struct InputFieldsView: View { guard let provider = providers.first else { return false } _ = provider.loadDataRepresentation(for: .image) { data, error in if error == nil, let data { - if let nsImage = NSImage(data: data) { - selectedImage = Image(nsImage: nsImage) - } + selectedImage = Image(data: data) } } diff --git a/Enchanted/UI/macOS/Chat/Components/ToolbarView_macOS.swift b/Enchanted/UI/macOS/Chat/Components/ToolbarView_macOS.swift index 4736586..043842b 100644 --- a/Enchanted/UI/macOS/Chat/Components/ToolbarView_macOS.swift +++ b/Enchanted/UI/macOS/Chat/Components/ToolbarView_macOS.swift @@ -5,7 +5,7 @@ // Created by Augustinas Malinauskas on 10/02/2024. // -#if os(macOS) +#if os(macOS) || os(visionOS) import SwiftUI struct ToolbarView: View { @@ -32,8 +32,9 @@ struct ToolbarView: View { .resizable() .scaledToFit() .frame(height: 20) - .foregroundColor(Color.labelCustom) + .padding(5) } + .buttonStyle(PlainButtonStyle()) .keyboardShortcut(KeyEquivalent("n"), modifiers: .command) } }