This commit is contained in:
陈连辰
2023-07-31 10:55:07 +08:00
parent b97576f102
commit 9d932d463c
29 changed files with 1291 additions and 96 deletions

View File

@@ -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 = "<group>"; };
CB28A52729C1569900F0286A /* ThinkingAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThinkingAnimationView.swift; sourceTree = "<group>"; };
CB298CE42A0A97390022EE6B /* ExportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportView.swift; sourceTree = "<group>"; };
CB2A943729F830E500D3A048 /* Conversation+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataClass.swift"; sourceTree = "<group>"; };
CB2A943829F830E500D3A048 /* Conversation+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataProperties.swift"; sourceTree = "<group>"; };
CB2A943F29F973F800D3A048 /* GoogleSearchPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSearchPopView.swift; sourceTree = "<group>"; };
CB2A944129F993A200D3A048 /* GoogleSearchSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSearchSettingView.swift; sourceTree = "<group>"; };
CB2AADE52A41E15600747BEB /* PluginContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginContentView.swift; sourceTree = "<group>"; };
CB2AADE72A42786F00747BEB /* PluginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginViewModel.swift; sourceTree = "<group>"; };
CB2D438729F0183A007742AE /* ChatGPT+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatGPT+CoreDataClass.swift"; sourceTree = "<group>"; };
CB2D438829F0183A007742AE /* ChatGPT+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatGPT+CoreDataProperties.swift"; sourceTree = "<group>"; };
CB2F972129CED6AE004EBD96 /* ChatRoomInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatRoomInputView.swift; sourceTree = "<group>"; };
@@ -163,12 +175,23 @@
CB4D1FC729F195EA0010D063 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
CB53A3BC29D48C8F00A5B8FC /* Prompt+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Prompt+CoreDataClass.swift"; sourceTree = "<group>"; };
CB53A3BD29D48C8F00A5B8FC /* Prompt+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Prompt+CoreDataProperties.swift"; sourceTree = "<group>"; };
CB55E0E92A3AAEA2006EBE2D /* PluginManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManifest.swift; sourceTree = "<group>"; };
CBC4B0FE29B8BF9600650296 /* OSXChatGPT.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = OSXChatGPT.xcdatamodel; sourceTree = "<group>"; };
CBD5AB6329E6DE9A007B6625 /* ProjectSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSettingManager.swift; sourceTree = "<group>"; };
CBD5AB6529E6EFE3007B6625 /* MarkdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownView.swift; sourceTree = "<group>"; };
CBD5AB6829E707A1007B6625 /* MarkdownTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTheme.swift; sourceTree = "<group>"; };
CBD5AB6A29E707F0007B6625 /* MarkdownTokenType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTokenType.swift; sourceTree = "<group>"; };
CBFBD75B2A4931AD0090454B /* PluginManifestAuth+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAuth+CoreDataClass.swift"; sourceTree = "<group>"; };
CBFBD75C2A4931AD0090454B /* PluginManifestAuth+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAuth+CoreDataProperties.swift"; sourceTree = "<group>"; };
CBFBD75D2A4931AD0090454B /* PluginManifestAPI+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAPI+CoreDataClass.swift"; sourceTree = "<group>"; };
CBFBD75E2A4931AD0090454B /* PluginManifestAPI+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifestAPI+CoreDataProperties.swift"; sourceTree = "<group>"; };
CBFBD76F2A528D050090454B /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
CBFBD7712A57C5400090454B /* PluginAPIInstall+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginAPIInstall+CoreDataClass.swift"; sourceTree = "<group>"; };
CBFBD7722A57C5400090454B /* PluginAPIInstall+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginAPIInstall+CoreDataProperties.swift"; sourceTree = "<group>"; };
CBFBD7732A57C5400090454B /* PluginManifest+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifest+CoreDataClass.swift"; sourceTree = "<group>"; };
CBFBD7742A57C5400090454B /* PluginManifest+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginManifest+CoreDataProperties.swift"; sourceTree = "<group>"; };
CBFBD7792A5D0DE70090454B /* PluginPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginPopView.swift; sourceTree = "<group>"; };
CBFBD77B2A5E67040090454B /* Conversation+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataClass.swift"; sourceTree = "<group>"; };
CBFBD77C2A5E67040090454B /* Conversation+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataProperties.swift"; sourceTree = "<group>"; };
/* 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 = "<group>";
@@ -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 = "<group>";
@@ -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 */

View File

@@ -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

View File

@@ -50,6 +50,18 @@
ReferencedContainer = "container:OSXChatGPT.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "PYTHON_LIBRARY"
value = "/Library/Frameworks/Python.framework/Versions/3.11/Python"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "PYTHON_LOADER_LOAD"
value = "TRUE"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@@ -489,6 +489,104 @@ extension ChatGPTManager {
}
// MARK: - plugin
extension ChatGPTManager {
func createFunctions(jsonArray: [[String: Any]]) {
var functions: [[String: Any]] = []
jsonArray.forEach { json in
var function: [String: Any] = [:]
if let components = json["components"] as? [String: Any] {
let schemas = components["schemas"] as? [String: Any]
if let keys = schemas?.keys.forEach({ key in
}) {
print("")
}
print(schemas)
}
}
}
}
//
//visitWebPageError:
// type: object
// properties:
// code:
// type: string
// description: error code
// message:
// type: string
// description: error message
// detail:
// type: string
// description: error detail
//functions = [
// {
// "name": "get_current_weather",
// "description": "Get the current weather in a given location",
// "parameters": {
// "type": "object",
// "properties": {
// "location": {
// "type": "string",
// "description": "The city and state, e.g. San Francisco, CA",
// },
// "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
// },
// "required": ["location"],
// },
// }
// ]
//- key : "visitWebPageError"
// value : 2 elements
// 0 : 2 elements
// key : AnyHashable("properties")
// - value : "properties"
// value : 3 elements
// 0 : 2 elements
// key : AnyHashable("message")
// - value : "message"
// value : 2 elements
// 0 : 2 elements
// key : AnyHashable("type")
// - value : "type"
// - value : "string"
// 1 : 2 elements
// key : AnyHashable("description")
// - value : "description"
// - value : "error message"
// 1 : 2 elements
// key : AnyHashable("code")
// - value : "code"
// value : 2 elements
// 0 : 2 elements
// key : AnyHashable("type")
// - value : "type"
// - value : "string"
// 1 : 2 elements
// key : AnyHashable("description")
// - value : "description"
// - value : "error code"
// 2 : 2 elements
// key : AnyHashable("detail")
// - value : "detail"
// value : 2 elements
// 0 : 2 elements
// key : AnyHashable("type")
// - value : "type"
// - value : "string"
// 1 : 2 elements
// key : AnyHashable("description")
// - value : "description"
// - value : "error detail"
// 1 : 2 elements
// key : AnyHashable("type")
// - value : "type"
// - value : "object"
// MARK: - search
extension ChatGPTManager {
func search(search: GoogleSearch?, callback:@escaping (_ searchResult: GoogleSearchResponse?, _ err: String?) -> Void) {

View File

@@ -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> = 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> = 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> = PluginManifest.fetchRequest()
request.predicate = NSPredicate(format: "name_for_human == %@", name)
let results: [PluginManifest] = CoreDataManager.shared.fetch(request: request)
return results.first
}
}

View File

@@ -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()

View File

@@ -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 == "" {

View File

@@ -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)
}
}

View File

@@ -2,7 +2,7 @@
// Conversation+CoreDataClass.swift
// OSXChatGPT
//
// Created by CoderChan on 2023/4/26.
// Created by CoderChan on 2023/7/12.
//
//

View File

@@ -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)
}

View File

@@ -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
}()
}

View File

@@ -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<PluginAPIInstall> {
return NSFetchRequest<PluginAPIInstall>(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 {
}

View File

@@ -0,0 +1,15 @@
//
// PluginManifest+CoreDataClass.swift
// OSXChatGPT
//
// Created by CoderChan on 2023/7/7.
//
//
import Foundation
import CoreData
public class PluginManifest: NSManagedObject {
}

View File

@@ -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<PluginManifest> {
return NSFetchRequest<PluginManifest>(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 {
}

View File

@@ -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"
// }
// },

View File

@@ -0,0 +1,15 @@
//
// PluginManifestAPI+CoreDataClass.swift
// OSXChatGPT
//
// Created by CoderChan on 2023/6/26.
//
//
import Foundation
import CoreData
public class PluginManifestAPI: NSManagedObject {
}

View File

@@ -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<PluginManifestAPI> {
return NSFetchRequest<PluginManifestAPI>(entityName: "PluginManifestAPI")
}
@NSManaged public var type: String?
@NSManaged public var url: String?
@NSManaged public var has_user_authentication: Bool
}
extension PluginManifestAPI : Identifiable {
}

View File

@@ -0,0 +1,15 @@
//
// PluginManifestAuth+CoreDataClass.swift
// OSXChatGPT
//
// Created by CoderChan on 2023/6/26.
//
//
import Foundation
import CoreData
public class PluginManifestAuth: NSManagedObject {
}

View File

@@ -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<PluginManifestAuth> {
return NSFetchRequest<PluginManifestAuth>(entityName: "PluginManifestAuth")
}
@NSManaged public var type: String?
}
extension PluginManifestAuth : Identifiable {
}

View File

@@ -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<String, AnyObject>) {
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

View File

@@ -4,6 +4,8 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>

View File

@@ -13,6 +13,7 @@
<attribute name="updateData" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="chatGPT" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ChatGPT"/>
<relationship name="lastMessage" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Message"/>
<relationship name="plugin" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PluginAPIInstall"/>
<relationship name="prompt" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Prompt" inverseName="sesstion" inverseEntity="Prompt"/>
<relationship name="search" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GoogleSearch"/>
</entity>
@@ -48,6 +49,35 @@
<attribute name="text" optional="YES" attributeType="String"/>
<attribute name="type" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
</entity>
<entity name="PluginAPIInstall" representedClassName=".PluginAPIInstall" syncable="YES">
<attribute name="apiJsonString" attributeType="String"/>
<attribute name="description_for_human" optional="YES" attributeType="String"/>
<attribute name="logo_url" optional="YES" attributeType="String"/>
<attribute name="name_for_human" optional="YES" attributeType="String"/>
<attribute name="schema_version" optional="YES" attributeType="String"/>
</entity>
<entity name="PluginManifest" representedClassName=".PluginManifest" syncable="YES">
<attribute name="contact_email" optional="YES" attributeType="String"/>
<attribute name="description_for_human" optional="YES" attributeType="String"/>
<attribute name="description_for_model" optional="YES" attributeType="String"/>
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
<attribute name="legal_info_url" optional="YES" attributeType="String"/>
<attribute name="logo_url" optional="YES" attributeType="String"/>
<attribute name="name_for_human" optional="YES" attributeType="String"/>
<attribute name="name_for_model" optional="YES" attributeType="String"/>
<attribute name="schema_version" optional="YES" attributeType="String"/>
<relationship name="api" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PluginManifestAPI"/>
<relationship name="auth" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PluginManifestAuth"/>
<relationship name="install" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PluginAPIInstall"/>
</entity>
<entity name="PluginManifestAPI" representedClassName=".PluginManifestAPI" syncable="YES">
<attribute name="has_user_authentication" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="type" optional="YES" attributeType="String"/>
<attribute name="url" optional="YES" attributeType="String"/>
</entity>
<entity name="PluginManifestAuth" representedClassName=".PluginManifestAuth" syncable="YES">
<attribute name="type" optional="YES" attributeType="String"/>
</entity>
<entity name="Prompt" representedClassName=".Prompt" syncable="YES">
<attribute name="author" optional="YES" attributeType="String"/>
<attribute name="cloudId" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>

View File

@@ -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 {

View File

@@ -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)
}
}
}
}

View File

@@ -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()
// }
//}

View File

@@ -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) {

View File

@@ -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()
}
}

View File

@@ -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 {

View File

@@ -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
)