From 9d932d463c673cbbc497ac4ab211393956e264f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E8=BF=9E=E8=BE=B0?= Date: Mon, 31 Jul 2023 10:55:07 +0800 Subject: [PATCH] plugin --- .../OSXChatGPT.xcodeproj/project.pbxproj | 85 +++++- .../xcshareddata/swiftpm/Package.resolved | 9 + .../xcschemes/OSXChatGPT.xcscheme | 12 + .../DataProvider/ChatGPTManager.swift | 98 +++++++ .../DataProvider/PluginViewModel.swift | 180 +++++++++++++ .../DataProvider/ServerManager.swift | 12 +- .../OSXChatGPT/DataProvider/ViewModel.swift | 34 +++ .../OSXChatGPT/Interface/OSXChatGPTApp.swift | 47 +++- .../Models/Conversation+CoreDataClass.swift | 2 +- .../Conversation+CoreDataProperties.swift | 38 ++- .../PluginAPIInstall+CoreDataClass.swift | 23 ++ .../PluginAPIInstall+CoreDataProperties.swift | 28 ++ .../Models/PluginManifest+CoreDataClass.swift | 15 ++ .../PluginManifest+CoreDataProperties.swift | 35 +++ .../OSXChatGPT/Models/PluginManifest.swift | 44 ---- .../PluginManifestAPI+CoreDataClass.swift | 15 ++ ...PluginManifestAPI+CoreDataProperties.swift | 27 ++ .../PluginManifestAuth+CoreDataClass.swift | 15 ++ ...luginManifestAuth+CoreDataProperties.swift | 25 ++ .../OSXChatGPT/NetWork/HTTPClient.swift | 80 +++++- OSXChatGPT/OSXChatGPT/OSXChatGPT.entitlements | 2 + .../OSXChatGPT.xcdatamodel/contents | 30 +++ .../WindowView/ChatRoomToolBar.swift | 28 +- .../WindowView/PluginContentView.swift | 249 ++++++++++++++++++ .../OSXChatGPT/WindowView/PluginPopView.swift | 172 ++++++++++++ .../OSXChatGPT/WindowView/SessionsView.swift | 31 ++- .../OSXChatGPT/WindowView/SettingsView.swift | 21 ++ .../WindowView/UserInitializeView.swift | 28 +- OSXChatGPT/OSXChatGPT/WindowView/View.swift | 2 +- 29 files changed, 1291 insertions(+), 96 deletions(-) create mode 100644 OSXChatGPT/OSXChatGPT/DataProvider/PluginViewModel.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataClass.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataProperties.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataClass.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataProperties.swift delete mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginManifest.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataClass.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataProperties.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataClass.swift create mode 100644 OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataProperties.swift create mode 100644 OSXChatGPT/OSXChatGPT/WindowView/PluginContentView.swift create mode 100644 OSXChatGPT/OSXChatGPT/WindowView/PluginPopView.swift create mode 100644 OSXChatGPT/OSXChatGPT/WindowView/SettingsView.swift diff --git a/OSXChatGPT/OSXChatGPT.xcodeproj/project.pbxproj b/OSXChatGPT/OSXChatGPT.xcodeproj/project.pbxproj index ff7e6fa..41a1f59 100644 --- a/OSXChatGPT/OSXChatGPT.xcodeproj/project.pbxproj +++ b/OSXChatGPT/OSXChatGPT.xcodeproj/project.pbxproj @@ -67,11 +67,11 @@ CB28A52829C1569900F0286A /* ThinkingAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB28A52729C1569900F0286A /* ThinkingAnimationView.swift */; }; CB298CE52A0A97390022EE6B /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB298CE42A0A97390022EE6B /* ExportView.swift */; }; CB2A943229F828E500D3A048 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = CB2A943129F828E500D3A048 /* SwiftSoup */; }; - CB2A943929F830E500D3A048 /* Conversation+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943729F830E500D3A048 /* Conversation+CoreDataClass.swift */; }; - CB2A943A29F830E500D3A048 /* Conversation+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943829F830E500D3A048 /* Conversation+CoreDataProperties.swift */; }; CB2A944029F973F800D3A048 /* GoogleSearchPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943F29F973F800D3A048 /* GoogleSearchPopView.swift */; }; CB2A944229F993A200D3A048 /* GoogleSearchSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A944129F993A200D3A048 /* GoogleSearchSettingView.swift */; }; CB2AADE42A41AFC100747BEB /* SimpleToast in Frameworks */ = {isa = PBXBuildFile; productRef = CB2AADE32A41AFC100747BEB /* SimpleToast */; }; + CB2AADE62A41E15600747BEB /* PluginContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2AADE52A41E15600747BEB /* PluginContentView.swift */; }; + CB2AADE82A42786F00747BEB /* PluginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2AADE72A42786F00747BEB /* PluginViewModel.swift */; }; CB2D438929F0183A007742AE /* ChatGPT+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2D438729F0183A007742AE /* ChatGPT+CoreDataClass.swift */; }; CB2D438A29F0183A007742AE /* ChatGPT+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2D438829F0183A007742AE /* ChatGPT+CoreDataProperties.swift */; }; CB2F971F29CE12B6004EBD96 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = CB2F971E29CE12B6004EBD96 /* MarkdownUI */; }; @@ -82,11 +82,23 @@ CB4D1FC429F195E60010D063 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CB4D1FC629F195E60010D063 /* Localizable.strings */; }; CB53A3BE29D48C8F00A5B8FC /* Prompt+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB53A3BC29D48C8F00A5B8FC /* Prompt+CoreDataClass.swift */; }; CB53A3BF29D48C8F00A5B8FC /* Prompt+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB53A3BD29D48C8F00A5B8FC /* Prompt+CoreDataProperties.swift */; }; - CB55E0EA2A3AAEA2006EBE2D /* PluginManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB55E0E92A3AAEA2006EBE2D /* PluginManifest.swift */; }; CBD5AB6429E6DE9A007B6625 /* ProjectSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD5AB6329E6DE9A007B6625 /* ProjectSettingManager.swift */; }; CBD5AB6629E6EFE3007B6625 /* MarkdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD5AB6529E6EFE3007B6625 /* MarkdownView.swift */; }; CBD5AB6929E707A1007B6625 /* MarkdownTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD5AB6829E707A1007B6625 /* MarkdownTheme.swift */; }; CBD5AB6B29E707F0007B6625 /* MarkdownTokenType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD5AB6A29E707F0007B6625 /* MarkdownTokenType.swift */; }; + CBFBD75A2A4697450090454B /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = CBFBD7592A4697450090454B /* Yams */; }; + CBFBD7632A4931AD0090454B /* PluginManifestAuth+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD75B2A4931AD0090454B /* PluginManifestAuth+CoreDataClass.swift */; }; + CBFBD7642A4931AD0090454B /* PluginManifestAuth+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD75C2A4931AD0090454B /* PluginManifestAuth+CoreDataProperties.swift */; }; + CBFBD7652A4931AD0090454B /* PluginManifestAPI+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD75D2A4931AD0090454B /* PluginManifestAPI+CoreDataClass.swift */; }; + CBFBD7662A4931AD0090454B /* PluginManifestAPI+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD75E2A4931AD0090454B /* PluginManifestAPI+CoreDataProperties.swift */; }; + CBFBD7702A528D050090454B /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD76F2A528D050090454B /* SettingsView.swift */; }; + CBFBD7752A57C5400090454B /* PluginAPIInstall+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD7712A57C5400090454B /* PluginAPIInstall+CoreDataClass.swift */; }; + CBFBD7762A57C5400090454B /* PluginAPIInstall+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD7722A57C5400090454B /* PluginAPIInstall+CoreDataProperties.swift */; }; + CBFBD7772A57C5400090454B /* PluginManifest+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD7732A57C5400090454B /* PluginManifest+CoreDataClass.swift */; }; + CBFBD7782A57C5400090454B /* PluginManifest+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD7742A57C5400090454B /* PluginManifest+CoreDataProperties.swift */; }; + CBFBD77A2A5D0DE70090454B /* PluginPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD7792A5D0DE70090454B /* PluginPopView.swift */; }; + CBFBD77D2A5E67040090454B /* Conversation+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD77B2A5E67040090454B /* Conversation+CoreDataClass.swift */; }; + CBFBD77E2A5E67040090454B /* Conversation+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFBD77C2A5E67040090454B /* Conversation+CoreDataProperties.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -150,10 +162,10 @@ CB28A52129C07BE500F0286A /* KeyboardMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardMonitor.swift; sourceTree = ""; }; CB28A52729C1569900F0286A /* ThinkingAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThinkingAnimationView.swift; sourceTree = ""; }; CB298CE42A0A97390022EE6B /* ExportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportView.swift; sourceTree = ""; }; - CB2A943729F830E500D3A048 /* Conversation+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataClass.swift"; sourceTree = ""; }; - CB2A943829F830E500D3A048 /* Conversation+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataProperties.swift"; sourceTree = ""; }; CB2A943F29F973F800D3A048 /* GoogleSearchPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSearchPopView.swift; sourceTree = ""; }; CB2A944129F993A200D3A048 /* GoogleSearchSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSearchSettingView.swift; sourceTree = ""; }; + CB2AADE52A41E15600747BEB /* PluginContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginContentView.swift; sourceTree = ""; }; + CB2AADE72A42786F00747BEB /* PluginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginViewModel.swift; sourceTree = ""; }; CB2D438729F0183A007742AE /* ChatGPT+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatGPT+CoreDataClass.swift"; sourceTree = ""; }; CB2D438829F0183A007742AE /* ChatGPT+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatGPT+CoreDataProperties.swift"; sourceTree = ""; }; CB2F972129CED6AE004EBD96 /* ChatRoomInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomInputView.swift; sourceTree = ""; }; @@ -163,12 +175,23 @@ CB4D1FC729F195EA0010D063 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; CB53A3BC29D48C8F00A5B8FC /* Prompt+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Prompt+CoreDataClass.swift"; sourceTree = ""; }; CB53A3BD29D48C8F00A5B8FC /* Prompt+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Prompt+CoreDataProperties.swift"; sourceTree = ""; }; - CB55E0E92A3AAEA2006EBE2D /* PluginManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManifest.swift; sourceTree = ""; }; CBC4B0FE29B8BF9600650296 /* OSXChatGPT.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = OSXChatGPT.xcdatamodel; sourceTree = ""; }; CBD5AB6329E6DE9A007B6625 /* ProjectSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSettingManager.swift; sourceTree = ""; }; CBD5AB6529E6EFE3007B6625 /* MarkdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownView.swift; sourceTree = ""; }; CBD5AB6829E707A1007B6625 /* MarkdownTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTheme.swift; sourceTree = ""; }; CBD5AB6A29E707F0007B6625 /* MarkdownTokenType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTokenType.swift; sourceTree = ""; }; + CBFBD75B2A4931AD0090454B /* PluginManifestAuth+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAuth+CoreDataClass.swift"; sourceTree = ""; }; + CBFBD75C2A4931AD0090454B /* PluginManifestAuth+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAuth+CoreDataProperties.swift"; sourceTree = ""; }; + CBFBD75D2A4931AD0090454B /* PluginManifestAPI+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAPI+CoreDataClass.swift"; sourceTree = ""; }; + CBFBD75E2A4931AD0090454B /* PluginManifestAPI+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAPI+CoreDataProperties.swift"; sourceTree = ""; }; + CBFBD76F2A528D050090454B /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + CBFBD7712A57C5400090454B /* PluginAPIInstall+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginAPIInstall+CoreDataClass.swift"; sourceTree = ""; }; + CBFBD7722A57C5400090454B /* PluginAPIInstall+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginAPIInstall+CoreDataProperties.swift"; sourceTree = ""; }; + CBFBD7732A57C5400090454B /* PluginManifest+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifest+CoreDataClass.swift"; sourceTree = ""; }; + CBFBD7742A57C5400090454B /* PluginManifest+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifest+CoreDataProperties.swift"; sourceTree = ""; }; + CBFBD7792A5D0DE70090454B /* PluginPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginPopView.swift; sourceTree = ""; }; + CBFBD77B2A5E67040090454B /* Conversation+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataClass.swift"; sourceTree = ""; }; + CBFBD77C2A5E67040090454B /* Conversation+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataProperties.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -181,6 +204,7 @@ CB2F971F29CE12B6004EBD96 /* MarkdownUI in Frameworks */, CB0F5A5929D057FF005B71D2 /* Splash in Frameworks */, CB2AADE42A41AFC100747BEB /* SimpleToast in Frameworks */, + CBFBD75A2A4697450090454B /* Yams in Frameworks */, CB2A943229F828E500D3A048 /* SwiftSoup in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -210,6 +234,8 @@ children = ( 182B436329BC5C8700F06778 /* MainContentView.swift */, 182B436029BC5C8700F06778 /* SessionsView.swift */, + CB2AADE52A41E15600747BEB /* PluginContentView.swift */, + CBFBD7792A5D0DE70090454B /* PluginPopView.swift */, 182B436229BC5C8700F06778 /* ChatRoomView.swift */, CB27655B29D1C12C00897E0E /* MarkdownContentView.swift */, CB2F972729CEFB65004EBD96 /* ChatRoomToolBar.swift */, @@ -225,6 +251,7 @@ 182B437A29BC5FBE00F06778 /* EnterAPIView.swift */, 188FB46629C1FA9700E3C18F /* EidtSessionRemarkView.swift */, CB28A52729C1569900F0286A /* ThinkingAnimationView.swift */, + CBFBD76F2A528D050090454B /* SettingsView.swift */, ); path = WindowView; sourceTree = ""; @@ -243,6 +270,7 @@ 182B437129BC5D1B00F06778 /* ChatGPTManager.swift */, 182B436F29BC5D1B00F06778 /* CoreDataManager.swift */, 182B437029BC5D1B00F06778 /* ViewModel.swift */, + CB2AADE72A42786F00747BEB /* PluginViewModel.swift */, CB373A9A29F56CFF00B8D9BE /* Localization.swift */, CBD5AB6329E6DE9A007B6625 /* ProjectSettingManager.swift */, CB28A52129C07BE500F0286A /* KeyboardMonitor.swift */, @@ -368,14 +396,22 @@ CBC4B11429B8CB1B00650296 /* Models */ = { isa = PBXGroup; children = ( + CBFBD7712A57C5400090454B /* PluginAPIInstall+CoreDataClass.swift */, + CBFBD7722A57C5400090454B /* PluginAPIInstall+CoreDataProperties.swift */, + CBFBD7732A57C5400090454B /* PluginManifest+CoreDataClass.swift */, + CBFBD7742A57C5400090454B /* PluginManifest+CoreDataProperties.swift */, + CBFBD75B2A4931AD0090454B /* PluginManifestAuth+CoreDataClass.swift */, + CBFBD75C2A4931AD0090454B /* PluginManifestAuth+CoreDataProperties.swift */, + CBFBD75D2A4931AD0090454B /* PluginManifestAPI+CoreDataClass.swift */, + CBFBD75E2A4931AD0090454B /* PluginManifestAPI+CoreDataProperties.swift */, CB26A2DB29FC1DCF001EF861 /* GoogleSearch+CoreDataClass.swift */, CB26A2DC29FC1DCF001EF861 /* GoogleSearch+CoreDataProperties.swift */, CB26A2DD29FC1DCF001EF861 /* GoogleSearchResult+CoreDataClass.swift */, CB26A2DE29FC1DCF001EF861 /* GoogleSearchResult+CoreDataProperties.swift */, - CB2A943729F830E500D3A048 /* Conversation+CoreDataClass.swift */, - CB2A943829F830E500D3A048 /* Conversation+CoreDataProperties.swift */, CB2D438729F0183A007742AE /* ChatGPT+CoreDataClass.swift */, CB2D438829F0183A007742AE /* ChatGPT+CoreDataProperties.swift */, + CBFBD77B2A5E67040090454B /* Conversation+CoreDataClass.swift */, + CBFBD77C2A5E67040090454B /* Conversation+CoreDataProperties.swift */, CB1F015D29EB9D05009CF942 /* Message+CoreDataClass.swift */, CB1F015E29EB9D05009CF942 /* Message+CoreDataProperties.swift */, CB1F015729EAFBF5009CF942 /* MessageText+CoreDataClass.swift */, @@ -383,7 +419,6 @@ CB53A3BC29D48C8F00A5B8FC /* Prompt+CoreDataClass.swift */, CB53A3BD29D48C8F00A5B8FC /* Prompt+CoreDataProperties.swift */, 182B43A329BF730300F06778 /* NSColor.swift */, - CB55E0E92A3AAEA2006EBE2D /* PluginManifest.swift */, ); path = Models; sourceTree = ""; @@ -431,6 +466,7 @@ CB0F5A5A29D057FF005B71D2 /* SplashMarkdown */, CB2A943129F828E500D3A048 /* SwiftSoup */, CB2AADE32A41AFC100747BEB /* SimpleToast */, + CBFBD7592A4697450090454B /* Yams */, ); productName = OSXChatGPT; productReference = CB1DCABE29B4F09D00B1D4E1 /* OSXChatGPT.app */; @@ -466,6 +502,7 @@ CB0F5A5729D057FF005B71D2 /* XCRemoteSwiftPackageReference "Splash" */, CB2A943029F828E500D3A048 /* XCRemoteSwiftPackageReference "SwiftSoup" */, CB2AADE22A41AFC100747BEB /* XCRemoteSwiftPackageReference "SimpleToast" */, + CBFBD7582A4697450090454B /* XCRemoteSwiftPackageReference "Yams" */, ); productRefGroup = CB1DCABF29B4F09D00B1D4E1 /* Products */; projectDirPath = ""; @@ -497,7 +534,6 @@ files = ( CB1F012F29E999EF009CF942 /* Segment.swift in Sources */, CB1F015C29EAFBF5009CF942 /* MessageText+CoreDataProperties.swift in Sources */, - CB2A943929F830E500D3A048 /* Conversation+CoreDataClass.swift in Sources */, CB373A9B29F56CFF00B8D9BE /* Localization.swift in Sources */, CB1F015129E9BC8C009CF942 /* Tokenizer.swift in Sources */, CB2449FA29D7FE38006EE829 /* ServerManager.swift in Sources */, @@ -509,30 +545,37 @@ CB2A944229F993A200D3A048 /* GoogleSearchSettingView.swift in Sources */, 182B43A429BF730300F06778 /* NSColor.swift in Sources */, CB1F012D29E99982009CF942 /* Grammar.swift in Sources */, + CBFBD77E2A5E67040090454B /* Conversation+CoreDataProperties.swift in Sources */, + CBFBD7632A4931AD0090454B /* PluginManifestAuth+CoreDataClass.swift in Sources */, CB27656629D1DA9800897E0E /* AIPromptView.swift in Sources */, CB26A2E229FC1DCF001EF861 /* GoogleSearchResult+CoreDataProperties.swift in Sources */, CB1F014929E99B5E009CF942 /* Sequence+Occurrences.swift in Sources */, 182B436B29BC5CBA00F06778 /* AppDelegate.swift in Sources */, + CBFBD7782A57C5400090454B /* PluginManifest+CoreDataProperties.swift in Sources */, CB1F015B29EAFBF5009CF942 /* MessageText+CoreDataClass.swift in Sources */, 182B437329BC5D1B00F06778 /* CoreDataManager.swift in Sources */, CB2A944029F973F800D3A048 /* GoogleSearchPopView.swift in Sources */, 188FB46729C1FA9700E3C18F /* EidtSessionRemarkView.swift in Sources */, CB2D438929F0183A007742AE /* ChatGPT+CoreDataClass.swift in Sources */, + CBFBD77A2A5D0DE70090454B /* PluginPopView.swift in Sources */, + CBFBD7702A528D050090454B /* SettingsView.swift in Sources */, CB1F014229E99B5E009CF942 /* CharacterSet+Contains.swift in Sources */, CB1F014629E99B5E009CF942 /* String+IsNumber.swift in Sources */, - CB2A943A29F830E500D3A048 /* Conversation+CoreDataProperties.swift in Sources */, 182B436629BC5C8700F06778 /* View.swift in Sources */, - CB55E0EA2A3AAEA2006EBE2D /* PluginManifest.swift in Sources */, + CBFBD7752A57C5400090454B /* PluginAPIInstall+CoreDataClass.swift in Sources */, CB1F012929E995BA009CF942 /* MarkdownTextBuilder.swift in Sources */, CB1F015F29EB9D05009CF942 /* Message+CoreDataClass.swift in Sources */, 182B437B29BC5FBE00F06778 /* EnterAPIView.swift in Sources */, + CB2AADE62A41E15600747BEB /* PluginContentView.swift in Sources */, CB0F5A5F29D059C4005B71D2 /* TextOutputFormat.swift in Sources */, 182B437929BC5D6200F06778 /* OSXChatGPTApp.swift in Sources */, CBD5AB6929E707A1007B6625 /* MarkdownTheme.swift in Sources */, + CBFBD7762A57C5400090454B /* PluginAPIInstall+CoreDataProperties.swift in Sources */, CB28A52229C07BE500F0286A /* KeyboardMonitor.swift in Sources */, CB1F014A29E99B5E009CF942 /* Sequence+AnyOf.swift in Sources */, 182B437429BC5D1B00F06778 /* ViewModel.swift in Sources */, CB1F016029EB9D05009CF942 /* Message+CoreDataProperties.swift in Sources */, + CBFBD7662A4931AD0090454B /* PluginManifestAPI+CoreDataProperties.swift in Sources */, CB1F014329E99B5E009CF942 /* Substring+HasSuffix.swift in Sources */, CB53A3BE29D48C8F00A5B8FC /* Prompt+CoreDataClass.swift in Sources */, 182B436729BC5C8700F06778 /* ChatRoomView.swift in Sources */, @@ -541,6 +584,7 @@ CB1F014729E99B5E009CF942 /* String+HTMLEntities.swift in Sources */, CB0F5A6029D059C4005B71D2 /* SplashCodeSyntaxHighlighter.swift in Sources */, CB1F014D29E9A4CC009CF942 /* MessageTextModel.swift in Sources */, + CBFBD7652A4931AD0090454B /* PluginManifestAPI+CoreDataClass.swift in Sources */, 182B436529BC5C8700F06778 /* SessionsView.swift in Sources */, CB1F014429E99B5E009CF942 /* String+PrefixChecking.swift in Sources */, CB27657529D33D7A00897E0E /* AIPromptInputView.swift in Sources */, @@ -548,15 +592,19 @@ 182B437229BC5D1B00F06778 /* HTTPClient.swift in Sources */, CB26A2DF29FC1DCF001EF861 /* GoogleSearch+CoreDataClass.swift in Sources */, CB53A3BF29D48C8F00A5B8FC /* Prompt+CoreDataProperties.swift in Sources */, + CBFBD7642A4931AD0090454B /* PluginManifestAuth+CoreDataProperties.swift in Sources */, CB1F012729E9832F009CF942 /* MarkdownTextAttributesReader.swift in Sources */, CB1F012C29E99982009CF942 /* SwiftGrammar.swift in Sources */, CBD5AB6429E6DE9A007B6625 /* ProjectSettingManager.swift in Sources */, 182B436829BC5C8700F06778 /* MainContentView.swift in Sources */, CB28A52829C1569900F0286A /* ThinkingAnimationView.swift in Sources */, + CB2AADE82A42786F00747BEB /* PluginViewModel.swift in Sources */, CBD5AB6629E6EFE3007B6625 /* MarkdownView.swift in Sources */, CBD5AB6B29E707F0007B6625 /* MarkdownTokenType.swift in Sources */, + CBFBD77D2A5E67040090454B /* Conversation+CoreDataClass.swift in Sources */, CB26A2E129FC1DCF001EF861 /* GoogleSearchResult+CoreDataClass.swift in Sources */, 182B437529BC5D1B00F06778 /* ChatGPTManager.swift in Sources */, + CBFBD7772A57C5400090454B /* PluginManifest+CoreDataClass.swift in Sources */, CB2D438A29F0183A007742AE /* ChatGPT+CoreDataProperties.swift in Sources */, CB2F972229CED6AE004EBD96 /* ChatRoomInputView.swift in Sources */, CB1F014529E99B5E009CF942 /* String+Removing.swift in Sources */, @@ -818,6 +866,14 @@ minimumVersion = 2.0.0; }; }; + CBFBD7582A4697450090454B /* XCRemoteSwiftPackageReference "Yams" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/jpsim/Yams.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -851,6 +907,11 @@ package = CB2F971D29CE12B6004EBD96 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */; productName = MarkdownUI; }; + CBFBD7592A4697450090454B /* Yams */ = { + isa = XCSwiftPackageProductDependency; + package = CBFBD7582A4697450090454B /* XCRemoteSwiftPackageReference "Yams" */; + productName = Yams; + }; /* End XCSwiftPackageProductDependency section */ /* Begin XCVersionGroup section */ diff --git a/OSXChatGPT/OSXChatGPT.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/OSXChatGPT/OSXChatGPT.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f4e7b49..433ad8a 100644 --- a/OSXChatGPT/OSXChatGPT.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/OSXChatGPT/OSXChatGPT.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -44,6 +44,15 @@ "revision" : "f707b8680cddb96dc1855632340a572ef37bbb98", "version" : "2.5.3" } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3", + "version" : "5.0.6" + } } ], "version" : 2 diff --git a/OSXChatGPT/OSXChatGPT.xcodeproj/xcshareddata/xcschemes/OSXChatGPT.xcscheme b/OSXChatGPT/OSXChatGPT.xcodeproj/xcshareddata/xcschemes/OSXChatGPT.xcscheme index c2a2ec0..92217c0 100644 --- a/OSXChatGPT/OSXChatGPT.xcodeproj/xcshareddata/xcschemes/OSXChatGPT.xcscheme +++ b/OSXChatGPT/OSXChatGPT.xcodeproj/xcshareddata/xcschemes/OSXChatGPT.xcscheme @@ -50,6 +50,18 @@ ReferencedContainer = "container:OSXChatGPT.xcodeproj"> + + + + + + Void) { diff --git a/OSXChatGPT/OSXChatGPT/DataProvider/PluginViewModel.swift b/OSXChatGPT/OSXChatGPT/DataProvider/PluginViewModel.swift new file mode 100644 index 0000000..ab30a94 --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/DataProvider/PluginViewModel.swift @@ -0,0 +1,180 @@ +// +// PluginViewModel.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/6/21. +// + +import Foundation +import Yams +import CoreData + + +class PluginViewModel: ObservableObject { + + @Published var pluginList: [PluginManifest] = [] + + + init() { + fetchAllPlugin() + } + /// 获取所有插件 + func fetchAllPlugin() { + var aa: [PluginManifest] = CoreDataManager.shared.fetch("PluginManifest", sorting: nil) + let remove = aa.filter { $0.name_for_human == nil } + if remove.count > 0 { + CoreDataManager.shared.delete(objects: remove) + aa.removeAll(where: { $0.name_for_human == nil }) + CoreDataManager.shared.saveData() + } + pluginList = aa + } + + static func fetchAllInstallPlugins(_ select:[PluginAPIInstall]) -> [PluginSelectViewModel] { + + let request: NSFetchRequest = PluginManifest.fetchRequest() + request.predicate = NSPredicate(format: "install != NULL") + let results: [PluginManifest] = CoreDataManager.shared.fetch(request: request) + var temp: [PluginSelectViewModel] = [] + results.forEach { manifest in + if let insta = manifest.install { + let model = PluginSelectViewModel(plugin: insta) + if select.contains(where: { $0.name_for_human == insta.name_for_human}) { + model.isMySelect = true + } + temp.append(model) + } + } + + return temp + } + + /// 安装插件 + func InstallPlugin(manifest: PluginManifest, progress:@escaping ((Progress) -> Void), complete:@escaping ((String?) -> Void)) { + guard let api = manifest.api?.url else { + complete("api URL Error") + return + } + HTTPClient.shared.downloadData(urlStr: api, progress: progress) { resUrl, err in + if let url = resUrl { + do { + let data = try Data(contentsOf: url) + if let content = String(data: data, encoding: .utf8), + let json = try Yams.load(yaml: content) as? [String: Any] { + self.installPluginToCoreData(manifest: manifest, jsonString: content, json: json) + complete(nil) + print(json) + }else { + complete("Install Error") + } + } catch { + print("读取文件失败: \(error)") + complete("Install Error") + } + }else { + complete("Install Error") + } +// print("downloadData reUrl:\(resUrl) \n err:\(err)") + } + } + func uninstall(manifest: PluginManifest) -> String? { + if let plugin = fetchManifest(name_for_human: manifest.name_for_human) { + if let install = plugin.install { + CoreDataManager.shared.delete(object: install) + } + plugin.install = nil + if let index = pluginList.firstIndex(where: { $0.name_for_human == plugin.name_for_human }) { + pluginList[index] = plugin + } + CoreDataManager.shared.saveData() + return nil + }else { + return "uninstall Error" + } + } + private func installPluginToCoreData(manifest: PluginManifest, jsonString: String, json: [String: Any]) { + let install = PluginAPIInstall(context: CoreDataManager.shared.container.viewContext) + if let plugin = fetchManifest(name_for_human: manifest.name_for_human) { + install.schema_version = manifest.schema_version + install.apiJsonString = jsonString; + install.logo_url = manifest.logo_url + install.name_for_human = manifest.name_for_human + install.description_for_human = manifest.description_for_human + install.apiJson = json + plugin.install = install + if let index = pluginList.firstIndex(where: { $0.name_for_human == plugin.name_for_human }) { + DispatchQueue.main.async { + self.pluginList[index] = plugin + } + } + CoreDataManager.shared.saveData() + } + + } + func reloadAllManifestes() { + HTTPClient.getManifests { datas, err in + if let er = err { + print("error:\(er)") + return + } + if let jsons = datas as? [[String:Any]] { + var temp : [PluginManifest] = [] + jsons.forEach { json in + let name_for_human = json["name_for_human"] as? String + var plugin = self.fetchPlugin(name_for_human: name_for_human ?? "") + if plugin?.id == nil { + plugin?.id = UUID() + } + if plugin == nil { + plugin = PluginManifest(context: CoreDataManager.shared.container.viewContext) + plugin?.id = UUID() + plugin?.api = PluginManifestAPI(context: CoreDataManager.shared.container.viewContext) + plugin?.auth = PluginManifestAuth(context: CoreDataManager.shared.container.viewContext) + + } + plugin?.setValue(json["schema_version"], forKey: "schema_version") + plugin?.setValue(json["name_for_human"], forKey: "name_for_human") + plugin?.setValue(json["name_for_model"], forKey: "name_for_model") + plugin?.setValue(json["description_for_human"], forKey: "description_for_human") + plugin?.setValue(json["description_for_model"], forKey: "description_for_model") + plugin?.setValue(json["logo_url"], forKey: "logo_url") + plugin?.setValue(json["contact_email"], forKey: "contact_email") + plugin?.setValue(json["legal_info_url"], forKey: "legal_info_url") + plugin?.api?.setValue((json["api"] as? [String: Any])?["type"], forKey: "type") + plugin?.api?.setValue((json["api"] as? [String: Any])?["url"], forKey: "url") + plugin?.api?.setValue((json["api"] as? [String: Any])?["has_user_authentication"], forKey: "has_user_authentication") + plugin?.auth?.setValue((json["auth"] as? [String: Any])?["type"], forKey: "type") +// print(plugin) + if let plag = plugin { + temp.append(plag) + } + } + CoreDataManager.shared.saveData() + DispatchQueue.main.async { + self.pluginList = temp + } + } + + } + } + + +} + +extension PluginViewModel { + + private func fetchPlugin(name_for_human: String) -> PluginManifest? { + let request: NSFetchRequest = PluginManifest.fetchRequest() + request.predicate = NSPredicate(format: "name_for_human == %@", name_for_human) + let results: [PluginManifest] = CoreDataManager.shared.fetch(request: request) + return results.first + } + private func fetchManifest(name_for_human: String?) -> PluginManifest? { + guard let name = name_for_human, !name.isEmpty else { return nil } + let request: NSFetchRequest = PluginManifest.fetchRequest() + request.predicate = NSPredicate(format: "name_for_human == %@", name) + let results: [PluginManifest] = CoreDataManager.shared.fetch(request: request) + return results.first + + } +} diff --git a/OSXChatGPT/OSXChatGPT/DataProvider/ServerManager.swift b/OSXChatGPT/OSXChatGPT/DataProvider/ServerManager.swift index 51b6a50..3e7b769 100644 --- a/OSXChatGPT/OSXChatGPT/DataProvider/ServerManager.swift +++ b/OSXChatGPT/OSXChatGPT/DataProvider/ServerManager.swift @@ -10,6 +10,7 @@ import Foundation let getTokenUrl = "https://raw.githubusercontent.com/CoderLineChan/data/main/data/token.json" let githubUrl = "https://api.github.com/repos/CoderLineChan/OSXChatGptDataUpload/contents/data" let githubGetUrl = "https://api.github.com/repos/CoderLineChan/OSXChatGptData/contents/data/data.json" +let githubPluginGetUrl = "https://api.github.com/repos/CoderLineChan/OSXChatGptData/contents/data/manifests.json" class ServerManager { static let shared = ServerManager() @@ -35,17 +36,18 @@ class ServerManager { let da = NSData(base64Encoded: getDataBase64, options: NSData.Base64DecodingOptions.init(rawValue: 0)), let dataString = String(data: da as Data, encoding: .utf8) { self.getDataToken = dataString -// print("获取getDataToken成功") +// print("获取getDataToken成功:\(dataString)") + }else { -// print("获取getDataToken失败") + print("获取getDataToken失败") } if let uploadDataBase64 = json["uploadData"] as? String, let da = NSData(base64Encoded: uploadDataBase64, options: NSData.Base64DecodingOptions.init(rawValue: 0)), let dataString = String(data: da as Data, encoding: .utf8) { self.uploadDataToken = dataString -// print("获取uploadDataToken成功") +// print("获取uploadDataToken成功:\(dataString)") }else { -// print("获取uploadDataToken失败") + print("获取uploadDataToken失败") } self.loading = false if self.uploadDataToken.isEmpty || self.getDataToken.isEmpty { @@ -54,7 +56,7 @@ class ServerManager { } } }else { -// print("获取Token失败") + print("获取Token失败") self.loading = false DispatchQueue.global().asyncAfter(deadline: .now() + 3) { self.checkToken() diff --git a/OSXChatGPT/OSXChatGPT/DataProvider/ViewModel.swift b/OSXChatGPT/OSXChatGPT/DataProvider/ViewModel.swift index 3297c53..4afa2b7 100644 --- a/OSXChatGPT/OSXChatGPT/DataProvider/ViewModel.swift +++ b/OSXChatGPT/OSXChatGPT/DataProvider/ViewModel.swift @@ -14,14 +14,18 @@ import SwiftSoup @MainActor class ViewModel: ObservableObject { + @Published var showDynamicBackground: Bool = ProjectSettingManager.shared.showDynamicBackground//动态背景 @Published var conversations: [Conversation] = []//所有会话 @Published var messages: [Message] = []//当前会话的消息 @Published var showUserInitialize: Bool = false//显示设置页 @Published var showEditRemark: Bool = false//显示编辑备注 @Published var showAIPrompt: Bool = false //显示自定义提示 + @Published var showPluginView: Bool = false //显示插件 var editConversation: Conversation?//编辑备注的会话 @Published var createNewChat: Bool = false//创建新会话 + @Published var currentPlugins: [PluginAPIInstall] = []//当前会话的插件 + @Published var isDisabledPlugin: Bool = true @Published var changeMsgText: String = "" @State var scrollID = UUID() var currentConversation: Conversation?//当前会话 @@ -650,6 +654,36 @@ extension ViewModel { } } +// MARK: - plugin +extension ViewModel { + func refreshCurrentPlugins() { + if let plugins = currentConversation?.plugin?.array as? [PluginAPIInstall] { + currentPlugins = plugins + }else { + currentPlugins = [] + } + + if let con = currentConversation, con.lastMessage != nil { + isDisabledPlugin = true + }else { + isDisabledPlugin = false + } + } + func updateCurrentPlugins(plugins: [PluginAPIInstall]) { + if let plugin = currentConversation?.plugin { + currentConversation?.removeFromPlugin(plugin) + } + var arr: [[String: Any]] = [] + plugins.forEach { install in + currentConversation?.addToPlugin(install) + if let json = install.apiJson { + arr.append(json) + } + } + ChatGPTManager.shared.createFunctions(jsonArray: arr) + CoreDataManager.shared.saveData() + } +} extension ViewModel { func getChatRoomView(conversation: Conversation?) -> ChatRoomView { if conversation == nil || conversation!.sesstionId == "" { diff --git a/OSXChatGPT/OSXChatGPT/Interface/OSXChatGPTApp.swift b/OSXChatGPT/OSXChatGPT/Interface/OSXChatGPTApp.swift index 31be589..88a13b6 100644 --- a/OSXChatGPT/OSXChatGPT/Interface/OSXChatGPTApp.swift +++ b/OSXChatGPT/OSXChatGPT/Interface/OSXChatGPTApp.swift @@ -11,7 +11,7 @@ import Colorful struct OSXChatGPTApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject var viewModel = ViewModel() - + @State private var showSettings = false var body: some Scene { WindowGroup { ZStack { @@ -20,8 +20,16 @@ struct OSXChatGPTApp: App { .ignoresSafeArea() } MainContentView().environmentObject(viewModel).edgesIgnoringSafeArea(.top).frame(minWidth: 900, maxWidth: .infinity, minHeight: 600, maxHeight: .infinity) + .onAppear { + if showSettings { + openSettingsWindow() + } + } } + } + + .windowToolbarStyle(.unified) .commands { SidebarCommands() } .commands { CommandGroup(replacing: CommandGroupPlacement.newItem) {} } @@ -36,6 +44,43 @@ struct OSXChatGPTApp: App { } } } +// .commands { +// CommandGroup(replacing: CommandGroupPlacement.appInfo) { +//// Button("Custom Shortcut") { +//// // 在这里添加自定义快捷键触发时的操作 +//// print("Custom Shortcut Triggered") +//// NSApplication.shared.hide(nil) +//// } +//// .keyboardShortcut(KeyEquivalent("N"), modifiers: .command) +// Button(Localization.Setting.localized) { +//// showSettings.toggle() +// openSettingsWindow() +// print("Localization.Setting.localized") +// +// } +// .keyboardShortcut(KeyEquivalent(","), modifiers: .command) +// } +// } + + + + } + + func openSettingsWindow() { + let settingsWindow = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), + styleMask: [.titled, .closable, .miniaturizable, .resizable], + backing: .buffered, + defer: false + ) + + let settingsWindowController = NSWindowController(window: settingsWindow) + settingsWindowController.contentViewController = NSHostingController(rootView: SettingsView()) + + settingsWindow.makeKeyAndOrderFront(nil) + NSApp.activate(ignoringOtherApps: true) } } + + diff --git a/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataClass.swift b/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataClass.swift index 7e085f7..28c18fd 100644 --- a/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataClass.swift +++ b/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataClass.swift @@ -2,7 +2,7 @@ // Conversation+CoreDataClass.swift // OSXChatGPT // -// Created by CoderChan on 2023/4/26. +// Created by CoderChan on 2023/7/12. // // diff --git a/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataProperties.swift b/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataProperties.swift index e82062b..de5ef87 100644 --- a/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataProperties.swift +++ b/OSXChatGPT/OSXChatGPT/Models/Conversation+CoreDataProperties.swift @@ -2,7 +2,7 @@ // Conversation+CoreDataProperties.swift // OSXChatGPT // -// Created by CoderChan on 2023/4/26. +// Created by CoderChan on 2023/7/12. // // @@ -24,6 +24,42 @@ extension Conversation { @NSManaged public var lastMessage: Message? @NSManaged public var prompt: Prompt? @NSManaged public var search: GoogleSearch? + @NSManaged public var plugin: NSOrderedSet? + +} + +// MARK: Generated accessors for plugin +extension Conversation { + + @objc(insertObject:inPluginAtIndex:) + @NSManaged public func insertIntoPlugin(_ value: PluginAPIInstall, at idx: Int) + + @objc(removeObjectFromPluginAtIndex:) + @NSManaged public func removeFromPlugin(at idx: Int) + + @objc(insertPlugin:atIndexes:) + @NSManaged public func insertIntoPlugin(_ values: [PluginAPIInstall], at indexes: NSIndexSet) + + @objc(removePluginAtIndexes:) + @NSManaged public func removeFromPlugin(at indexes: NSIndexSet) + + @objc(replaceObjectInPluginAtIndex:withObject:) + @NSManaged public func replacePlugin(at idx: Int, with value: PluginAPIInstall) + + @objc(replacePluginAtIndexes:withPlugin:) + @NSManaged public func replacePlugin(at indexes: NSIndexSet, with values: [PluginAPIInstall]) + + @objc(addPluginObject:) + @NSManaged public func addToPlugin(_ value: PluginAPIInstall) + + @objc(removePluginObject:) + @NSManaged public func removeFromPlugin(_ value: PluginAPIInstall) + + @objc(addPlugin:) + @NSManaged public func addToPlugin(_ values: NSOrderedSet) + + @objc(removePlugin:) + @NSManaged public func removeFromPlugin(_ values: NSOrderedSet) } diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataClass.swift b/OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataClass.swift new file mode 100644 index 0000000..0883dd1 --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataClass.swift @@ -0,0 +1,23 @@ +// +// PluginAPIInstall+CoreDataClass.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/7/7. +// +// + +import Foundation +import CoreData +import Yams + + +public class PluginAPIInstall: NSManagedObject { + + lazy var apiJson: [String: Any]? = { + if let content = apiJsonString { + let json = try? Yams.load(yaml: content) as? [String: Any] + return json + } + return nil + }() +} diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataProperties.swift b/OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataProperties.swift new file mode 100644 index 0000000..2a493ee --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginAPIInstall+CoreDataProperties.swift @@ -0,0 +1,28 @@ +// +// PluginAPIInstall+CoreDataProperties.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/7/7. +// +// + +import Foundation +import CoreData + + +extension PluginAPIInstall { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "PluginAPIInstall") + } + + @NSManaged public var apiJsonString: String? + @NSManaged public var schema_version: String? + @NSManaged public var logo_url: String? + @NSManaged public var name_for_human: String? + @NSManaged public var description_for_human: String? +} + +extension PluginAPIInstall : Identifiable { + +} diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataClass.swift b/OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataClass.swift new file mode 100644 index 0000000..077706f --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataClass.swift @@ -0,0 +1,15 @@ +// +// PluginManifest+CoreDataClass.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/7/7. +// +// + +import Foundation +import CoreData + + +public class PluginManifest: NSManagedObject { + +} diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataProperties.swift b/OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataProperties.swift new file mode 100644 index 0000000..f081712 --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginManifest+CoreDataProperties.swift @@ -0,0 +1,35 @@ +// +// PluginManifest+CoreDataProperties.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/7/7. +// +// + +import Foundation +import CoreData + + +extension PluginManifest { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "PluginManifest") + } + @NSManaged public var id: UUID? + @NSManaged public var schema_version: String? + @NSManaged public var name_for_human: String? + @NSManaged public var name_for_model: String? + @NSManaged public var description_for_human: String? + @NSManaged public var description_for_model: String? + @NSManaged public var logo_url: String? + @NSManaged public var contact_email: String? + @NSManaged public var legal_info_url: String? + @NSManaged public var api: PluginManifestAPI? + @NSManaged public var auth: PluginManifestAuth? + @NSManaged public var install: PluginAPIInstall? + +} + +extension PluginManifest : Identifiable { + +} diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginManifest.swift b/OSXChatGPT/OSXChatGPT/Models/PluginManifest.swift deleted file mode 100644 index 3a31b3e..0000000 --- a/OSXChatGPT/OSXChatGPT/Models/PluginManifest.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// PluginManifest.swift -// OSXChatGPT -// -// Created by CoderChan on 2023/6/15. -// - -import Foundation - - -struct PluginManifest { - var schema_version: String = "" - var name_for_human: String = "" - var name_for_model: String = "" - var description_for_human: String = "" - var description_for_model: String = "" - var auth: PluginManifestAuth = PluginManifestAuth() - var api: PluginManifestAPI = PluginManifestAPI() - var logo_url: String = "" - var contact_email: String = "" - var legal_info_url: String = "" -} - - -struct PluginManifestAuth { - var type: String = "" -} - -struct PluginManifestAPI { - var type: String = "" - var url: String = "" - var has_user_authentication: Bool = false -} - -// "auth": { -// "type": "oauth", -// "authorization_content_type": "application/x-www-form-urlencoded", -// "client_url": "https://auth.buildbetter.app/realms/buildbetter/protocol/openid-connect/auth", -// "authorization_url": "https://auth.buildbetter.app/realms/buildbetter/protocol/openid-connect/token", -// "scope": "email profile", -// "verification_tokens": { -// "openai": "28e7c6a759b94f4cac07cd4693433997" -// } -// }, diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataClass.swift b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataClass.swift new file mode 100644 index 0000000..e69a36f --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataClass.swift @@ -0,0 +1,15 @@ +// +// PluginManifestAPI+CoreDataClass.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/6/26. +// +// + +import Foundation +import CoreData + + +public class PluginManifestAPI: NSManagedObject { + +} diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataProperties.swift b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataProperties.swift new file mode 100644 index 0000000..16e7fc5 --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAPI+CoreDataProperties.swift @@ -0,0 +1,27 @@ +// +// PluginManifestAPI+CoreDataProperties.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/6/26. +// +// + +import Foundation +import CoreData + + +extension PluginManifestAPI { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "PluginManifestAPI") + } + + @NSManaged public var type: String? + @NSManaged public var url: String? + @NSManaged public var has_user_authentication: Bool + +} + +extension PluginManifestAPI : Identifiable { + +} diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataClass.swift b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataClass.swift new file mode 100644 index 0000000..ddc3eda --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataClass.swift @@ -0,0 +1,15 @@ +// +// PluginManifestAuth+CoreDataClass.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/6/26. +// +// + +import Foundation +import CoreData + + +public class PluginManifestAuth: NSManagedObject { + +} diff --git a/OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataProperties.swift b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataProperties.swift new file mode 100644 index 0000000..4a0555d --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/Models/PluginManifestAuth+CoreDataProperties.swift @@ -0,0 +1,25 @@ +// +// PluginManifestAuth+CoreDataProperties.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/6/26. +// +// + +import Foundation +import CoreData + + +extension PluginManifestAuth { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "PluginManifestAuth") + } + + @NSManaged public var type: String? + +} + +extension PluginManifestAuth : Identifiable { + +} diff --git a/OSXChatGPT/OSXChatGPT/NetWork/HTTPClient.swift b/OSXChatGPT/OSXChatGPT/NetWork/HTTPClient.swift index ec2d096..3498efd 100644 --- a/OSXChatGPT/OSXChatGPT/NetWork/HTTPClient.swift +++ b/OSXChatGPT/OSXChatGPT/NetWork/HTTPClient.swift @@ -52,22 +52,32 @@ struct GoogleSearchItem: Decodable { } -class HTTPClient { +class HTTPClient: NSObject { static let shared = HTTPClient() fileprivate var urlSession: URLSession! fileprivate var sessionConfiguration: URLSessionConfiguration! private var isCancelStreamRequest: Bool = false private var currentTask: URLSessionTask? + private var downloadProgress: ((Progress) -> Void)? + private var downloadComplete: ((URL?, Error?) -> Void)? private let jsonDecoder: JSONDecoder = { let jsonDecoder = JSONDecoder() jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase return jsonDecoder }() - init() { + override init() { sessionConfiguration = URLSessionConfiguration.default urlSession = URLSession(configuration: sessionConfiguration) } - + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "fractionCompleted", let progress = object as? Progress { + DispatchQueue.main.async { + self.updateProgress(progress) + } + } + } + func setAdditionalHeaders(_ headers: Dictionary) { sessionConfiguration.httpAdditionalHeaders = headers } @@ -398,6 +408,70 @@ extension HTTPClient { task.resume() } } +// MARK: - 下载 +extension HTTPClient { + func downloadData(urlStr: String, progress:@escaping ((Progress) -> Void), complete:@escaping ((URL?, Error?) -> Void)) { + guard let url = URL(string: urlStr) else { + complete(nil, nil) + return + } + downloadProgress = progress + let task = urlSession.downloadTask(with: URLRequest(url: url)) { reUrl, rep, err in + complete(reUrl, err) + } + task.resume() + task.progress.addObserver(self, forKeyPath: "fractionCompleted", options: .new, context: nil) + } + + func updateProgress(_ progress: Progress) { + downloadProgress?(progress) + } + +} + +// MARK: - plugin manifests +extension HTTPClient { + static func getManifests(callback:@escaping (_ datas: [Any], _ err: String?) -> Void) { + let url = URL(string: githubPluginGetUrl)! + var request = URLRequest(url: url) + let authorizationValue = "Bearer \(ServerManager.shared.getDataToken)" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue(authorizationValue, forHTTPHeaderField: "Authorization") + request.httpMethod = "GET" + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + if let error = error { + print("error: \(error.localizedDescription)") + callback([], error.localizedDescription) + return + } + var code = 0 + if let response = response as? HTTPURLResponse { + code = response.statusCode + } + if let data = data, + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { + if let base64Str = json["content"] as? String { + let base64String = base64Str.replacingOccurrences(of: "\n", with: "") + if let da = NSData(base64Encoded: base64String, options: NSData.Base64DecodingOptions.init(rawValue: 0)), + let dataString = String(data: da as Data, encoding: .utf8), + let jsonData = dataString.data(using: .utf8), + let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [Any] { + DispatchQueue.main.async { + callback(jsonObject, nil) + } + }else { + callback([], "data error code:\(code)") + } + }else { + //获取不到数据,需要更新token + callback([], "data error code:\(code)") + + } + } + } + task.resume() + } +} struct HTTPResponse1: Decodable { let json: HTTPResponse2 diff --git a/OSXChatGPT/OSXChatGPT/OSXChatGPT.entitlements b/OSXChatGPT/OSXChatGPT/OSXChatGPT.entitlements index a046386..1f46b92 100644 --- a/OSXChatGPT/OSXChatGPT/OSXChatGPT.entitlements +++ b/OSXChatGPT/OSXChatGPT/OSXChatGPT.entitlements @@ -4,6 +4,8 @@ com.apple.security.app-sandbox + com.apple.security.cs.disable-library-validation + com.apple.security.files.user-selected.read-write com.apple.security.network.client diff --git a/OSXChatGPT/OSXChatGPT/OSXChatGPT.xcdatamodeld/OSXChatGPT.xcdatamodel/contents b/OSXChatGPT/OSXChatGPT/OSXChatGPT.xcdatamodeld/OSXChatGPT.xcdatamodel/contents index c61ae12..87fdf74 100644 --- a/OSXChatGPT/OSXChatGPT/OSXChatGPT.xcdatamodeld/OSXChatGPT.xcdatamodel/contents +++ b/OSXChatGPT/OSXChatGPT/OSXChatGPT.xcdatamodeld/OSXChatGPT.xcdatamodel/contents @@ -13,6 +13,7 @@ + @@ -48,6 +49,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OSXChatGPT/OSXChatGPT/WindowView/ChatRoomToolBar.swift b/OSXChatGPT/OSXChatGPT/WindowView/ChatRoomToolBar.swift index 95d41c6..18e3eab 100644 --- a/OSXChatGPT/OSXChatGPT/WindowView/ChatRoomToolBar.swift +++ b/OSXChatGPT/OSXChatGPT/WindowView/ChatRoomToolBar.swift @@ -18,6 +18,7 @@ struct ChatRoomToolBar: View { @State private var model: String = "" @State private var context: String = "" @State private var isOn: Bool = false + @State var showPluginView: Bool = false //显示插件 @EnvironmentObject var viewModel: ViewModel @State private var isAnswerTypeTrue = ChatGPTManager.shared.answerType.valueBool @@ -80,21 +81,24 @@ struct ChatRoomToolBar: View { viewModel.updateConversation(sesstionId: viewModel.currentConversation?.sesstionId ?? "", message: nil) } - Button { - showSearchView.toggle() - } label: { - Text("谷歌搜索") - } +// Button { +// showSearchView.toggle() +// } label: { +// Text("谷歌搜索") +// } +// .popover(isPresented: $showSearchSettingView) { +// GoogleSearchSettingView().environmentObject(viewModel) +// } +// .popover(isPresented: $showSearchView) { +// GoogleSearchPopView(showSearchView: $showSearchView, showSearchSettingView: $showSearchSettingView).environmentObject(viewModel) +// } + PluginToolView(showPluginView: $showPluginView, isDisabledPlugin: $viewModel.isDisabledPlugin) + .sheet(isPresented: $showPluginView, content: { + PluginPopView(plugins: PluginViewModel.fetchAllInstallPlugins(viewModel.currentConversation?.plugin?.array as? [PluginAPIInstall] ?? [])).environmentObject(viewModel) + }) - .popover(isPresented: $showSearchView) { - GoogleSearchPopView(showSearchView: $showSearchView, showSearchSettingView: $showSearchSettingView).environmentObject(viewModel) - } - - .popover(isPresented: $showSearchSettingView) { - GoogleSearchSettingView().environmentObject(viewModel) - } Spacer() if viewModel.showStopAnswerBtn { diff --git a/OSXChatGPT/OSXChatGPT/WindowView/PluginContentView.swift b/OSXChatGPT/OSXChatGPT/WindowView/PluginContentView.swift new file mode 100644 index 0000000..8bd1534 --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/WindowView/PluginContentView.swift @@ -0,0 +1,249 @@ +// +// PluginContentView.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/6/20. +// + +import SwiftUI +import SimpleToast + +struct PluginContentView: View { + @EnvironmentObject var viewModel: ViewModel + @StateObject var pluginViewModel = PluginViewModel() + @State private var toastOptions: SimpleToastOptions = SimpleToastOptions(alignment: .center, hideAfter: 2.5, backdrop: nil, animation: nil, modifierType: .fade, dismissOnTap: true) + @State private var isShowToast: Bool = false + @State private var errorToast: String = "" + @State private var isShowing: Bool = false + var body: some View { + VStack { + GeometryReader { geometry in + Spacer() + HStack { + Spacer() + Button { + pluginViewModel.reloadAllManifestes() + } label: { + Text("更新插件") + } + .padding() + + } + .frame(width: geometry.size.width, height: geometry.size.height) + .background(Color.white) + + }.frame(height: 40) + .padding(.top, 1) + + ScrollView { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 180, maximum: 230), spacing: 16)], alignment: .leading, spacing: 16) { + ForEach(pluginViewModel.pluginList) { item in + ZStack { + RoundedRectangle(cornerRadius: 10) + .fill(.white) + .frame(height: 165) + PluginItemView(manifest: item, pluginViewModel: pluginViewModel, showToast: { err in + self.isShowToast = false + self.toastOptions = self.createNewToastOptions() + self.errorToast = err + self.isShowToast = true + }).environmentObject(viewModel) + } + } + } + } + .padding() + Spacer() + } + .simpleToast(isPresented: $isShowToast, options: self.toastOptions, content: { + VStack { + Text(self.errorToast) + .padding() + .foregroundColor(.white) + .background(.black.opacity(0.9)) + .cornerRadius(5) + } + .padding() + + }) + + .onAppear { + + } + + } + private func createNewToastOptions() -> SimpleToastOptions { + return SimpleToastOptions(alignment: .center, hideAfter: 2.5, backdrop: nil, animation: nil, modifierType: .fade, dismissOnTap: true) + } +} + +struct PluginItemView: View { + let manifest: PluginManifest + let pluginViewModel: PluginViewModel + var showToast: (String) -> () + @EnvironmentObject var viewModel: ViewModel + + @State private var isShowLoading: Bool = false + @State private var downloadProgress: Double = 0.0 + var body: some View { + GeometryReader { geometry in + ZStack { + VStack(alignment: .leading) { + HStack { + AsyncImage(url: URL(string: manifest.logo_url ?? "")) { image in + image.resizable() + } placeholder: { + ProgressView() + } + .frame(width: 50, height: 50) + .cornerRadius(5) + VStack(alignment: .leading) { + Text(manifest.name_for_human ?? manifest.name_for_model ?? "NULL") + .textSelection(.enabled) + HStack { + Button { + if manifest.install == nil { + installPlugin(manifest: manifest) + }else if manifest.install?.schema_version != manifest.schema_version { + installPlugin(manifest: manifest) + } else { + uninstall(manifest: manifest) + } + } label: { + HStack { + if manifest.install == nil { + Text("安装") + .foregroundColor(.white) + if isShowLoading { + PluginDownloadProgressView(circleBackdropColor: .white.opacity(0.5), circleForegroundColor: .white, progress: $downloadProgress) + .frame(width: 15, height: 15) + }else { + Image(systemName: "arrow.down.circle.fill") + .foregroundColor(.white) + .frame(width: 15, height: 15) + } + }else if manifest.install?.schema_version != manifest.schema_version { + Text("更新") + .foregroundColor(.white) + Image(systemName: "arrow.clockwise.circle.fill") + .foregroundColor(.white) + .frame(width: 15, height: 15) + } else { + Text("卸载") + .foregroundColor(.white) + Image(systemName: "xmark.bin.circle.fill") + .foregroundColor(.white) + .frame(width: 15, height: 15) + } + } + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + .frame(width: 80, height: 30) + .background((manifest.install == nil || manifest.install?.schema_version != manifest.schema_version) ? .blue : .red) + }.buttonStyle(PlainButtonStyle()) + .frame(width: 80, height: 30) + .cornerRadius(5) + } + } + Spacer() + } + .padding(.leading, 16) + .padding(.trailing, 10) + .padding(.top, 10) + Spacer() + } + VStack { + Text(manifest.description_for_human ?? manifest.description_for_model ?? "") + .padding(EdgeInsets(top: 70, leading: 12, bottom: 5, trailing: 12)) + .textSelection(.enabled) + } + } + .frame(width: geometry.size.width, height: geometry.size.height) + } + } + + func installPlugin(manifest: PluginManifest) { + if isShowLoading == true { + return + } + downloadProgress = 0 + isShowLoading = true + pluginViewModel.InstallPlugin(manifest: manifest) { pro in + self.downloadProgress = Double(pro.completedUnitCount / pro.totalUnitCount) + print("Progress:\(pro.totalUnitCount), \(pro.completedUnitCount)") + } complete: { err in + if let error = err { + self.installPluginError(err: error) + }else { + //成功 + showToast("安装成功") + } + } + } + func installPluginError(err: String) { + showToast(err) + downloadProgress = 0 + isShowLoading = false + } + func test() { + if downloadProgress >= 1 { + withAnimation { + isShowLoading = false + } + return + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.downloadProgress += 0.1 + self.test() + } + } + func uninstall(manifest: PluginManifest) { + if let err = pluginViewModel.uninstall(manifest: manifest) { + showToast(err) + }else { + showToast("卸载成功") + downloadProgress = 0 + isShowLoading = false + } + } +} + +struct PluginContentView_Previews: PreviewProvider { + static var previews: some View { + PluginContentView() + } +} + +struct PluginDownloadProgressView: View { + let lineWidth: CGFloat = 2 + let circleBackdropColor: Color + let circleForegroundColor: Color + @Binding var progress: Double + var body: some View { + GeometryReader { geometry in + ZStack { + Circle() // Inactive + .stroke(lineWidth: lineWidth) + .frame(width: geometry.size.width, height: geometry.size.height) + .foregroundColor(circleBackdropColor) + + Rectangle() + .frame(width: geometry.size.width * 0.4, height: geometry.size.width * 0.4) + .foregroundColor(circleForegroundColor) + .cornerRadius(2) +// ProgressView() +// .frame(width: geometry.size.width * 0.5, height: geometry.size.height * 0.5) +// .foregroundColor(circleForegroundColor) + // .cornerRadius(2) + + Circle() + .trim(from: 0, to: progress) + .stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round)) + .frame(width: geometry.size.width, height: geometry.size.height) + .foregroundColor(circleForegroundColor) + .rotationEffect(.degrees(-90)) + .animation(.linear) + } + + } + } +} diff --git a/OSXChatGPT/OSXChatGPT/WindowView/PluginPopView.swift b/OSXChatGPT/OSXChatGPT/WindowView/PluginPopView.swift new file mode 100644 index 0000000..e3432ca --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/WindowView/PluginPopView.swift @@ -0,0 +1,172 @@ +// +// PluginPopView.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/7/11. +// + +import SwiftUI +import SimpleToast + +class PluginSelectViewModel: ObservableObject, Identifiable { + @Published var plugin: PluginAPIInstall + @Published var isMySelect: Bool + init(plugin: PluginAPIInstall) { + self.plugin = plugin + self.isMySelect = false + } + class func createDatas(_ plugins: [PluginAPIInstall]) -> [PluginSelectViewModel] { + var arr : [PluginSelectViewModel] = [] + plugins.forEach { plu in + arr.append(PluginSelectViewModel(plugin: plu)) + } + return arr + } +} + + +struct PluginToolView: View { + @EnvironmentObject var viewModel: ViewModel + @Binding var showPluginView: Bool + @Binding var isDisabledPlugin: Bool + var body: some View { + Button { + showPluginView.toggle() + } label: { + GeometryReader { geometry in + if viewModel.currentPlugins.count > 0 { + HStack(alignment: .center, spacing: 5) { + ForEach(viewModel.currentPlugins) { plugin in + AsyncImage(url: URL(string: plugin.logo_url ?? "")) { image in + image.resizable() + } placeholder: { + ProgressView() + } + .frame(width: 16, height: 16) + .cornerRadius(2) + } + } + .frame(width: geometry.size.width, height: geometry.size.height) + .opacity(isDisabledPlugin ? 0.6 : 1) + }else { + Text("No Plugin") + .frame(width: geometry.size.width, height: geometry.size.height) + .opacity(isDisabledPlugin ? 0.6 : 1) + } + } + } + + .frame(width: 75, height: 22) + .disabled(isDisabledPlugin) + .onAppear{ + viewModel.refreshCurrentPlugins() + } + } +} + + +struct PluginPopView: View { + @EnvironmentObject var viewModel: ViewModel + @Environment(\.presentationMode) var presentationMode + @State var plugins: [PluginSelectViewModel] + @State private var toastOptions: SimpleToastOptions = SimpleToastOptions(alignment: .center, hideAfter: 2.5, backdrop: nil, animation: nil, modifierType: .fade, dismissOnTap: true) + @State private var isShowToast: Bool = false + var body: some View { + VStack(alignment: .center) { + HStack(alignment: .center) { + Button { + self.presentationMode.wrappedValue.dismiss() + } label: { + Text("取消") + }.padding() + Spacer() + + Button { + var select: [PluginAPIInstall] = [] + plugins.forEach { viewM in + if viewM.isMySelect { + select.append(viewM.plugin) + } + } + viewModel.updateCurrentPlugins(plugins: select) + viewModel.refreshCurrentPlugins() + self.presentationMode.wrappedValue.dismiss() + } label: { + Text("确定") + }.padding() + + } + .padding(.top, 16) + .frame(height: 35) + .frame(maxWidth: .infinity) + .background(.white) + + List { + ForEach(plugins, id: \.plugin.id) { plugin in + Toggle(isOn: Binding(get: { plugins[getIndex(for: plugin)].isMySelect }, set: { newValue in + if newValue == true { + if plugins.filter( {$0.isMySelect == true }).count >= 3 { + self.isShowToast = true + return + } + } + plugin.isMySelect = newValue; + plugins[getIndex(for: plugin)] = plugin + })) { + HStack { + AsyncImage(url: URL(string: plugin.plugin.logo_url ?? "")) { image in + image.resizable() + } placeholder: { + ProgressView() + } + .frame(width: 35, height: 35) + .cornerRadius(5) + VStack(alignment: .center) { + HStack { + Text(plugin.plugin.name_for_human ?? "") + .font(Font.system(size: 14)) + Spacer() + } + .padding(.bottom, 1) + Text(plugin.plugin.description_for_human ?? "") + .font(Font.system(size: 12)) + } + .frame(maxWidth: .infinity, minHeight: 45, maxHeight: 45, alignment: .leading) + + } + } + .padding(5) + .background(Color.black.opacity(0.1)) + .cornerRadius(6) + } + } + .padding(.top, 1) + .frame(width: 380, height: 500) + + .onAppear { + + } + } + .simpleToast(isPresented: $isShowToast, options: self.toastOptions, content: { + VStack { + Text("最多选三个插件") + .padding() + .foregroundColor(.white) + .background(.black.opacity(0.9)) + .cornerRadius(5) + } + .padding() + + }) + } + + func getIndex(for plugin: PluginSelectViewModel) -> Int { + plugins.firstIndex(where: { $0.plugin.id == plugin.plugin.id })! + } +} + +//struct PluginPopView_Previews: PreviewProvider { +// static var previews: some View { +// PluginPopView() +// } +//} diff --git a/OSXChatGPT/OSXChatGPT/WindowView/SessionsView.swift b/OSXChatGPT/OSXChatGPT/WindowView/SessionsView.swift index 2a161e3..7c97604 100644 --- a/OSXChatGPT/OSXChatGPT/WindowView/SessionsView.swift +++ b/OSXChatGPT/OSXChatGPT/WindowView/SessionsView.swift @@ -25,7 +25,7 @@ struct SessionsView: View { Button(action: { viewModel.showUserInitialize = false viewModel.showAIPrompt = false - + viewModel.showPluginView = false // 点击 New Chat 按钮的操作 viewModel.currentConversation = viewModel.addNewConversation() viewModel.createNewChat = true @@ -36,6 +36,7 @@ struct SessionsView: View { Text(Localization.newChat.localized) } .padding(10) + .padding(.leading, 0) .foregroundColor(.white) .background(Color.blue.opacity(0.8)) .cornerRadius(5) @@ -52,6 +53,7 @@ struct SessionsView: View { } viewModel.createNewChat = false viewModel.showAIPrompt = false + viewModel.showPluginView = false viewModel.showUserInitialize = true KeyboardMonitor.shared.stopKeyMonitor() KeyboardMonitor.shared.stopMonitorPasteboard() @@ -74,6 +76,7 @@ struct SessionsView: View { } viewModel.createNewChat = false viewModel.showUserInitialize = false + viewModel.showPluginView = false viewModel.showAIPrompt = true KeyboardMonitor.shared.stopKeyMonitor() KeyboardMonitor.shared.stopMonitorPasteboard() @@ -85,11 +88,35 @@ struct SessionsView: View { .cornerRadius(5) } .buttonStyle(PlainButtonStyle()) - .padding(.trailing, 10) + .padding(.trailing, 0) }.buttonStyle(PlainButtonStyle()) + NavigationLink(destination: PluginContentView().environmentObject(viewModel), isActive: $viewModel.showPluginView) { + Button(action: { + // 点击右边按钮的操作 + if viewModel.currentConversation != nil { + viewModel.currentConversation = nil//先取消会话 + } + viewModel.createNewChat = false + viewModel.showUserInitialize = false + viewModel.showAIPrompt = false + viewModel.showPluginView = true + KeyboardMonitor.shared.stopKeyMonitor() + KeyboardMonitor.shared.stopMonitorPasteboard() + }) { + Image(systemName: "powerplug.fill") + .padding(10) + .foregroundColor(.white) + .background(Color.gray) + .cornerRadius(5) + } + .buttonStyle(PlainButtonStyle()) + .padding(.trailing, 10) + + }.buttonStyle(PlainButtonStyle()) + }) .frame(height: 20) .sheet(isPresented: $viewModel.showEditRemark) { diff --git a/OSXChatGPT/OSXChatGPT/WindowView/SettingsView.swift b/OSXChatGPT/OSXChatGPT/WindowView/SettingsView.swift new file mode 100644 index 0000000..1e49de4 --- /dev/null +++ b/OSXChatGPT/OSXChatGPT/WindowView/SettingsView.swift @@ -0,0 +1,21 @@ +// +// SettingsView.swift +// OSXChatGPT +// +// Created by CoderChan on 2023/7/3. +// + +import SwiftUI + +struct SettingsView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + .background(.red) + } +} + +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView() + } +} diff --git a/OSXChatGPT/OSXChatGPT/WindowView/UserInitializeView.swift b/OSXChatGPT/OSXChatGPT/WindowView/UserInitializeView.swift index 3d3bd00..9bd1498 100644 --- a/OSXChatGPT/OSXChatGPT/WindowView/UserInitializeView.swift +++ b/OSXChatGPT/OSXChatGPT/WindowView/UserInitializeView.swift @@ -101,20 +101,20 @@ struct UserInitializeView: View { .buttonStyle(PlainButtonStyle()) // 隐藏按钮的默认样式 - Button(action: { - showGoogleSetting.toggle() - }) { - HStack(spacing: 8) { - Text("谷歌搜索配置") - } - .foregroundColor(.white) - .padding(.horizontal, 16) - .padding(.vertical, 8) - .background(Color.green) - .cornerRadius(10) - .padding(.leading, 16) - } - .buttonStyle(PlainButtonStyle()) // 隐藏按钮的默认样式 +// Button(action: { +// showGoogleSetting.toggle() +// }) { +// HStack(spacing: 8) { +// Text("谷歌搜索配置") +// } +// .foregroundColor(.white) +// .padding(.horizontal, 16) +// .padding(.vertical, 8) +// .background(Color.green) +// .cornerRadius(10) +// .padding(.leading, 16) +// } +// .buttonStyle(PlainButtonStyle()) // 隐藏按钮的默认样式 } if apiKey.count > 0 { diff --git a/OSXChatGPT/OSXChatGPT/WindowView/View.swift b/OSXChatGPT/OSXChatGPT/WindowView/View.swift index 6f94708..94a1e5f 100644 --- a/OSXChatGPT/OSXChatGPT/WindowView/View.swift +++ b/OSXChatGPT/OSXChatGPT/WindowView/View.swift @@ -10,7 +10,7 @@ import SwiftUI extension View { func leftSessionContentSize() -> some View { frame( - minWidth: 250, idealWidth: 250, maxWidth: .infinity, + minWidth: 270, idealWidth: 270, maxWidth: .infinity, minHeight: 300, idealHeight: 350, maxHeight: .infinity, alignment: .leading )