diff --git a/README.MD b/README.MD index d89f2ce..c038cd0 100644 --- a/README.MD +++ b/README.MD @@ -17,6 +17,45 @@ ## news ---------------- +06-10-2023 + +LogicSage 1.2.1 (4) Could it be the one? + +Hello LogicSage Enthusiasts, + +LogicSage 1.2.1 brings many enhancements to the app experience. + +Please give a try at using my app called LogicSage. It’s a mobile AI workspace that allows you to use GPTs to help you write code that you incorporate in your Xcode or Swift playgrounds projects. Or you can use it a a GPT chatting app, your choice. +What will you ask LogicSage today? LogicSage is the Mobile AI workspace for iPadOS (and iOS) :). Join me as we spark creativity. + +We are excited to announce the release of LogicSage 1.2.1, an upgrade that brings you a plethora of enhancements to your favourite mobile AI workspace. LogicSage, an open-source AI workspace, is now more user-friendly than ever. + +Whether you're a developer looking to sharpen your programming skills or a curious mind eager to delve into AI, LogicSage is your perfect companion. It lets you interact with GPTs, view and edit code, and even submit pull requests. You can also use it as a GPT chat app – the choice is yours! + +Here's a sneak peek at what you can do with LogicSage: + + View and examine app source code or any git repositories, with fully color-customizable viewing file windows. + Chat with GPTs directly via views in the app or through a command-line interface complete with a toolbar and command bar. + Integrate with text-to-speech software in both the client and server. + Create apps automatically in Xcode with Term window and LogicSage for Mac. + Enjoy interaction without a Mac + +And the best part? All you need is an AI API Key, which can be entered in the Settings menu. + +So, what are you waiting for? Unlock the power of the future with LogicSage and change the way you interact with technology. Try it out today on iPadOS or iOS. + +We're constantly working on enhancing your LogicSage experience. Stay tuned for Google and Link cmd support coming soon! + +Your feedback is invaluable to us. If you have any suggestions, ideas, or queries, feel free to drop a mail at chrisbdillard@gmail.com. + +Happy coding! + +Regards, +Chris Dillard, +Creator, LogicSage + +---------------- + 06-06-2023 LogicSage 1.2.1-2 in development!!!! diff --git a/SwiftSageServer/Package.resolved b/SwiftSageServer/Package.resolved index 10703da..a090f81 100644 --- a/SwiftSageServer/Package.resolved +++ b/SwiftSageServer/Package.resolved @@ -168,7 +168,7 @@ "location" : "https://github.com/vapor/vapor.git", "state" : { "branch" : "main", - "revision" : "65877a5db28cd0e97bed0cc667be58d3c7e575a6" + "revision" : "38cb397c6a0cc66031bb454a8614e6f988a7bbdb" } }, { diff --git a/SwiftSageServer/Sources/App/configure.swift b/SwiftSageServer/Sources/App/configure.swift index 7ff6360..b928e43 100644 --- a/SwiftSageServer/Sources/App/configure.swift +++ b/SwiftSageServer/Sources/App/configure.swift @@ -5,9 +5,9 @@ import var Darwin.stdout // SwiftSageServer -// Run with `swift run` in the SwiftSageServer directory. +// Run with `./run.sh` in the LogicSage directory. -let debugging = true +let debugging = false let specs = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "▇", "▆", "▅", "▄", "▃", "▂", "▁"] + ["░", "▒", "▓", "█","░", "▒","▓", "█","░", "▒","░", "▒", "▓", "█","░"] + ["."] diff --git a/SwiftSageiOS/Sourceful/SwiftUI/SourceCodeTextEditor.swift b/SwiftSageiOS/Sourceful/SwiftUI/SourceCodeTextEditor.swift index 8e85b27..0895711 100644 --- a/SwiftSageiOS/Sourceful/SwiftUI/SourceCodeTextEditor.swift +++ b/SwiftSageiOS/Sourceful/SwiftUI/SourceCodeTextEditor.swift @@ -97,7 +97,6 @@ public struct SourceCodeTextEditor: _ViewRepresentable { self._isMoveGestureActive = isMoveGestureActive self._isResizeGestureActive = isResizeGestureActive - } public func makeCoordinator() -> Coordinator { @@ -122,34 +121,33 @@ public struct SourceCodeTextEditor: _ViewRepresentable { _ = view.becomeFirstResponder() } - // TODO: Incorporate this performance fixing code. - // // Only auto scroll when not gesturing - // guard !isResizeGestureActive && !isMoveGestureActive else { return } - - let overrideText = custom.overrideText() - // TODO: Make sure -- Is it strictly needed????? + // Is it strictly needed????? // DispatchQueue.main.async { - view.textView.isEditable = isEditing + view.textView.isEditable = isEditing - view.contentTextView.isEditable = isEditing + view.contentTextView.isEditable = isEditing - view.isEditing = isEditing - view.textView.isSelectable = isEditing - view.contentTextView.isSelectable = isEditing - if let overrideText = overrideText { + view.isEditing = isEditing + view.textView.isSelectable = isEditing + view.contentTextView.isSelectable = isEditing + if let overrideText = overrideText { - context.coordinator.wrappedView.text = overrideText - - if isLocktoBottom { - let location = view.textView.text.count - 1 - let bottom = NSMakeRange(location, 1) - view.textView.scrollRangeToVisible(bottom) + context.coordinator.wrappedView.text = overrideText +// +// // Only auto scroll when not gesturing +// guard !isResizeGestureActive && !isMoveGestureActive else { return } + + if isLocktoBottom { + let location = view.textView.text.count - 1 + let bottom = NSMakeRange(location, 1) + view.textView.scrollRangeToVisible(bottom) + } } - } + // } } #endif diff --git a/SwiftSageiOS/SwiftSageiOS.xcodeproj/project.pbxproj b/SwiftSageiOS/SwiftSageiOS.xcodeproj/project.pbxproj index 99a989e..0506611 100644 --- a/SwiftSageiOS/SwiftSageiOS.xcodeproj/project.pbxproj +++ b/SwiftSageiOS/SwiftSageiOS.xcodeproj/project.pbxproj @@ -150,6 +150,8 @@ B7C446332A2A917000B03FF5 /* SageMultiView+onDrag.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7C446322A2A917000B03FF5 /* SageMultiView+onDrag.swift */; }; B7C446352A2A91E600B03FF5 /* ResizingHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7C446342A2A91E600B03FF5 /* ResizingHandle.swift */; }; B7D7AD022A26ACFF003C9AFE /* Settings+ColorGen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7D7AD012A26ACFF003C9AFE /* Settings+ColorGen.swift */; }; + B7DB97DF2A32E1C600304B80 /* SettingsViewModel+Websocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7DB97DE2A32E1C600304B80 /* SettingsViewModel+Websocket.swift */; }; + B7DB97E12A32EABB00304B80 /* SettingsViewModel+Convo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7DB97E02A32EABB00304B80 /* SettingsViewModel+Convo.swift */; }; B7EF2A2B2A0C2D620048CFFC /* View+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF2A2A2A0C2D620048CFFC /* View+Keyboard.swift */; }; B7F3A11629F74C9E0006C837 /* NetServiceBrowserWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F3A11529F74C9E0006C837 /* NetServiceBrowserWrapper.swift */; }; B7F3A11929F75EAB0006C837 /* CommandButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F3A11829F75EAB0006C837 /* CommandButton.swift */; }; @@ -325,6 +327,8 @@ B7C446322A2A917000B03FF5 /* SageMultiView+onDrag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SageMultiView+onDrag.swift"; sourceTree = ""; }; B7C446342A2A91E600B03FF5 /* ResizingHandle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizingHandle.swift; sourceTree = ""; }; B7D7AD012A26ACFF003C9AFE /* Settings+ColorGen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Settings+ColorGen.swift"; sourceTree = ""; }; + B7DB97DE2A32E1C600304B80 /* SettingsViewModel+Websocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsViewModel+Websocket.swift"; sourceTree = ""; }; + B7DB97E02A32EABB00304B80 /* SettingsViewModel+Convo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsViewModel+Convo.swift"; sourceTree = ""; }; B7EF2A2A2A0C2D620048CFFC /* View+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Keyboard.swift"; sourceTree = ""; }; B7F3A11529F74C9E0006C837 /* NetServiceBrowserWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetServiceBrowserWrapper.swift; sourceTree = ""; }; B7F3A11829F75EAB0006C837 /* CommandButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandButton.swift; sourceTree = ""; }; @@ -757,6 +761,8 @@ B78EAF5E2A116D76003095A2 /* Github */, B737F40A29F837CE00E57577 /* SettingsVIew.swift */, B737F40C29F8395B00E57577 /* SettingsViewModel.swift */, + B7DB97E02A32EABB00304B80 /* SettingsViewModel+Convo.swift */, + B7DB97DE2A32E1C600304B80 /* SettingsViewModel+Websocket.swift */, B7D7AD012A26ACFF003C9AFE /* Settings+ColorGen.swift */, ); path = Settings; @@ -1039,6 +1045,7 @@ B7378BFD2A1B192600A1D44C /* HapticMan.swift in Sources */, B7191F2929F9F09A00078D8D /* TextViewWrapperView.swift in Sources */, B78DA96C2A0312F1002343AB /* HelpPopup.swift in Sources */, + B7DB97E12A32EABB00304B80 /* SettingsViewModel+Convo.swift in Sources */, B7544E302A191BCE00961F4C /* AudioTranscriptionQuery.swift in Sources */, B7191F2C29F9F09A00078D8D /* Paragraphs.swift in Sources */, B737F40B29F837CE00E57577 /* SettingsVIew.swift in Sources */, @@ -1072,6 +1079,7 @@ B7D7AD022A26ACFF003C9AFE /* Settings+ColorGen.swift in Sources */, B78DA9762A04A0A5002343AB /* RepositoryTreeView.swift in Sources */, B7C11F2B2A01C62500701721 /* PTTWrapper.swift in Sources */, + B7DB97DF2A32E1C600304B80 /* SettingsViewModel+Websocket.swift in Sources */, B7544E322A191BCE00961F4C /* ModelsResult.swift in Sources */, B7AED3712A0AC0420013E8D9 /* SageMultiViewModel.swift in Sources */, B7544E362A191BCE00961F4C /* CompletionsQuery.swift in Sources */, @@ -1260,7 +1268,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"SwiftSageiOS/Preview Content\""; DEVELOPMENT_TEAM = ""; @@ -1274,9 +1282,8 @@ INFOPLIST_KEY_CFBundleDisplayName = LogicSageDev; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment"; INFOPLIST_KEY_NSAppleEventsUsageDescription = "To communicate with your computer."; - INFOPLIST_KEY_NSLocalNetworkUsageDescription = "To receive and send messages to LogicSage"; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "To receive and send messages to LogicSage for Mac. See github repo for LogicSage for more info. If declined now, reconnect in Settings menu."; INFOPLIST_KEY_NSMicrophoneUsageDescription = ""; - INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "LogicSage uses your mic and speech recognition to capture commands"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -1312,7 +1319,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = SwiftSageiOS/SwiftSageiOS.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"SwiftSageiOS/Preview Content\""; DEVELOPMENT_TEAM = 72WEN2C47N; @@ -1324,9 +1331,8 @@ INFOPLIST_KEY_CFBundleDisplayName = LogicSage; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment"; INFOPLIST_KEY_NSAppleEventsUsageDescription = "To communicate with your computer."; - INFOPLIST_KEY_NSLocalNetworkUsageDescription = "To receive and send messages to LogicSage"; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "To receive and send messages to LogicSage for Mac. See github repo for LogicSage for more info. If declined now, reconnect in Settings menu."; INFOPLIST_KEY_NSMicrophoneUsageDescription = ""; - INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "LogicSage uses your mic and speech recognition to capture commands"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; diff --git a/SwiftSageiOS/SwiftSageiOS/SwiftSageiOSApp.swift b/SwiftSageiOS/SwiftSageiOS/SwiftSageiOSApp.swift index 32f353f..595aac5 100644 --- a/SwiftSageiOS/SwiftSageiOS/SwiftSageiOSApp.swift +++ b/SwiftSageiOS/SwiftSageiOS/SwiftSageiOSApp.swift @@ -40,21 +40,16 @@ struct SwiftSageiOSApp: App { } var body: some Scene { WindowGroup { - ZStack { - HStack(spacing: 0) { - ContentView(settingsViewModel: settingsViewModel) - } + ContentView(settingsViewModel: settingsViewModel) .onAppear { #if !os(macOS) SwiftSageiOSAppDelegate.applicationDidFinishLaunching() #endif - - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.666) { serviceDiscovery?.startDiscovering() } DispatchQueue.main.async { #if !os(macOS) - isPortrait = UIApplication.shared.statusBarOrientation == .portrait || UIApplication.shared.statusBarOrientation == .portraitUpsideDown #endif settingsViewModel.printVoicesInMyDevice() @@ -84,7 +79,7 @@ struct SwiftSageiOSApp: App { } #endif } - } + } } func setHasSeenInstructions(_ hasSeen: Bool) { @@ -93,31 +88,15 @@ func setHasSeenInstructions(_ hasSeen: Bool) { func hasSeenInstructions() -> Bool { return UserDefaults.standard.bool(forKey: "hasSeenInstructions") } -class AppState: ObservableObject { - @Published var isInBackground: Bool = false - private var cancellables: [AnyCancellable] = [] - - init() { -#if !os(macOS) - // let didEnterBackground = NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification) - // .map { _ in true } - // .debounce(for: .milliseconds(500), scheduler: DispatchQueue.main) - // .removeDuplicates() - // - // let willEnterForeground = NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification) - // .map { _ in false } - // .debounce(for: .milliseconds(500), scheduler: DispatchQueue.main) - // .removeDuplicates() - // - // didEnterBackground - // .merge(with: willEnterForeground) - // .sink { [weak self] in - // self?.isInBackground = $0 - // DispatchQueue.main.asyncAfter(deadline: .now() + reconnectInterval) { - // serviceDiscovery?.startDiscovering() - // } - // } - // .store(in: &cancellables) -#endif - } +func setHasSeenLogo(_ hasSeen: Bool) { + UserDefaults.standard.set(hasSeen, forKey: "hasSeenLogo") +} +func hasSeenLogo() -> Bool { + return UserDefaults.standard.bool(forKey: "hasSeenLogo") +} +func hasSeenAnim() -> Bool { + return UserDefaults.standard.bool(forKey: "hasSeenAnim") +} +func setHasSeenAnim(_ hasSeen: Bool) { + UserDefaults.standard.set(hasSeen, forKey: "hasSeenAnim") } diff --git a/SwiftSageiOS/SwiftSageiOS/Utilities/Screamer.swift b/SwiftSageiOS/SwiftSageiOS/Utilities/Screamer.swift index 0c06e38..475231d 100644 --- a/SwiftSageiOS/SwiftSageiOS/Utilities/Screamer.swift +++ b/SwiftSageiOS/SwiftSageiOS/Utilities/Screamer.swift @@ -13,6 +13,15 @@ var screamer = ScreamClient() let PING_INTERVAL: TimeInterval = 60.666 +let recipientJsonKey = "recipient" +let messageJsonKey = "message" +let usernameJsonKey = "username" +let passwordJsonKey = "password" +let commandJsonKey = "command" +let serverUsernameValue = "SERVER" +let filenameJsonKey = "filename" +let filesizeJsonKey = "filesize" + class ScreamClient: WebSocketDelegate { let reconnectInterval: TimeInterval = 2.666 @@ -42,9 +51,9 @@ class ScreamClient: WebSocketDelegate { func logOpenReport() { #if !os(macOS) let devType = UIDevice.current.userInterfaceIdiom == .phone ? "iOS" : "iPadOS" - logD("WebSocket connected \(devType) device.\n\(SettingsViewModel.shared.logoAscii5())") + logD("WebSocket connected \(devType) device.\n\(logoAscii5())") #else - logD("WebSocket connected Mac device.\n\(SettingsViewModel.shared.logoAscii5())") + logD("WebSocket connected Mac device.\n\(logoAscii5())") #endif } func didReceive(event: WebSocketEvent, client: WebSocketClient) { @@ -53,7 +62,7 @@ class ScreamClient: WebSocketDelegate { logOpenReport() - let authData: [String: Any] = ["username": SettingsViewModel.shared.userName, "password": SettingsViewModel.shared.password] + let authData: [String: Any] = [usernameJsonKey: SettingsViewModel.shared.userName, passwordJsonKey: SettingsViewModel.shared.password] do { let authJSON = try JSONSerialization.data(withJSONObject: authData, options: [.fragmentsAllowed]) let authString = String(data: authJSON, encoding: .utf8) @@ -78,8 +87,8 @@ class ScreamClient: WebSocketDelegate { #if !os(macOS) do { let json = try JSONSerialization.jsonObject(with: Data(text.utf8), options: .fragmentsAllowed) as? [String: String] - guard let recipient = json?["recipient"] as? String, - let message = json?["message"] as? String else { + guard let recipient = json?[recipientJsonKey] as? String, + let message = json?[messageJsonKey] as? String else { return logD("malformed text received on ws") } @@ -121,7 +130,7 @@ class ScreamClient: WebSocketDelegate { isTransferringWorkspace = false DispatchQueue.main.async { - SettingsViewModel.shared.receivedWorkspaceData = self.receivedWorkspaceData + SettingsViewModel.shared.recievedWorkspaceData = self.receivedWorkspaceData } } // END HANDLE WORKSPACE STREAM ZONE ****************************************************** @@ -167,10 +176,10 @@ class ScreamClient: WebSocketDelegate { else if isTransferringWallpaper { if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { - receivedWallpaperFileName = json["filename"] as? String - receivedWallpaperFileSize = json["filesize"] as? Int - } else { - + receivedWallpaperFileName = json[filenameJsonKey] as? String + receivedWallpaperFileSize = json[filesizeJsonKey] as? Int + } + else { receivedData.append(data) } } @@ -259,38 +268,26 @@ class ScreamClient: WebSocketDelegate { } } func sendCommand(command: String) { - logD("Executing: \(command)") - -// if SettingsViewModel.shared.currentMode == .mobile { -// logD("Handling \(command) mobile mode...") -// if callLocalCommand(command) { -// return -// } -// } -// else { - // TODO: The idea, debate, g server, rand should be handled in the "Server chat" - logD("LogicSage handling \(command)...") - if command == "st" || command == "stop" || command == "STOP" { SettingsViewModel.shared.stopVoice() ; stopRandomSpinner() ; } - - if websocket != nil { - let messageData: [String: Any] = ["recipient": "SERVER", "command": command] - do { - let messageJSON = try JSONSerialization.data(withJSONObject: messageData, options: [.fragmentsAllowed]) - let messageString = String(data: messageJSON, encoding: .utf8) - websocket.write(string: messageString ?? "") - } - catch { - logD("error = \(error)") - } + logD("LogicSage handling \(command)...") + if command == "st" || command == "stop" || command == "STOP" { SettingsViewModel.shared.stopVoice() ; stopRandomSpinner() ; } + if websocket != nil { + let messageData: [String: Any] = [recipientJsonKey: serverUsernameValue, commandJsonKey: command] + do { + let messageJSON = try JSONSerialization.data(withJSONObject: messageData, options: [.fragmentsAllowed]) + let messageString = String(data: messageJSON, encoding: .utf8) + websocket.write(string: messageString ?? "") } - else { - logD("Websocket nil, not handling command") - logD("0. Set your Computers API Key for A.I. in GPT-Info.plist before building and running the LogicSage ./run.sh") - - disconnect() + catch { + logD("error = \(error)") } - // } + } + else { + logD("Websocket nil, not handling command") + logD("0. Set your Computers API Key for A.I. in GPT-Info.plist before building and running the LogicSage ./run.sh") + + disconnect() + } } func sendPing() { guard let socket = websocket else { return } diff --git a/SwiftSageiOS/SwiftSageiOS/Utilities/Speak.swift b/SwiftSageiOS/SwiftSageiOS/Utilities/Speak.swift index 9e7a01a..d5ddf71 100644 --- a/SwiftSageiOS/SwiftSageiOS/Utilities/Speak.swift +++ b/SwiftSageiOS/SwiftSageiOS/Utilities/Speak.swift @@ -89,8 +89,6 @@ extension SettingsViewModel { selectedVoiceIndexSaved = UserDefaults.standard.integer(forKey: "selectedVoiceIndex") selectedVoice = installedVoices[SettingsViewModel.shared.selectedVoiceIndexSaved] - - } else { selectedVoiceIndexSaved = 0 @@ -101,12 +99,18 @@ extension SettingsViewModel { #endif } + // Double check this duplicate filterin lol func installedVoiesArr() -> [AVSpeechSynthesisVoice] { var ret = [AVSpeechSynthesisVoice]() - AVSpeechSynthesisVoice.speechVoices().forEach { - - ret.append($0) + AVSpeechSynthesisVoice.speechVoices().forEach { voice in + if ret.contains(where: { otherVoice in + otherVoice.name == voice.name + }) { + } + else { + ret.append(voice) + } } return ret } diff --git a/SwiftSageiOS/SwiftSageiOS/Utilities/WindowManager.swift b/SwiftSageiOS/SwiftSageiOS/Utilities/WindowManager.swift index e0f2487..ceef8d0 100644 --- a/SwiftSageiOS/SwiftSageiOS/Utilities/WindowManager.swift +++ b/SwiftSageiOS/SwiftSageiOS/Utilities/WindowManager.swift @@ -12,6 +12,7 @@ import SwiftUI class WindowManager: ObservableObject { @Published var windows: [WindowInfo] = [] func addWindow(windowType: WindowInfo.WindowType, frame: CGRect, zIndex: Int, file: RepoFile? = nil, fileContents: String = "", url: String = "", convoId: Conversation.ID? = nil) { +#if !os(macOS) // TODO: OFFSET NEW WINDOWS @@ -19,6 +20,7 @@ class WindowManager: ObservableObject { windows.append(newWindow) sortWindowsByZIndex() bringWindowToFront(window: newWindow) +#endif } func removeWindow(window: WindowInfo) { if let index = windows.firstIndex(of: window) { diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/AddView.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/AddView.swift index 3ffbb78..0631de1 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/AddView.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/AddView.swift @@ -168,9 +168,7 @@ struct AddView: View { showAddView.toggle() -#if !os(macOS) windowManager.addWindow(windowType: .project, frame: defSize, zIndex: 0) -#endif } }) { VStack { @@ -196,9 +194,7 @@ struct AddView: View { // TODO: Hide or show terminal chat? showAddView.toggle() -#if !os(macOS) windowManager.addWindow(windowType: .file, frame: defSize, zIndex: 0) -#endif } }) { VStack { @@ -222,9 +218,7 @@ struct AddView: View { showAddView.toggle() // TODO: Hide or show terminal chat? -#if !os(macOS) windowManager.addWindow(windowType: .webView, frame: defSize, zIndex: 0, url: settingsViewModel.defaultURL) -#endif } }) { VStack { @@ -283,9 +277,7 @@ struct AddView: View { // TODO: Hide or show terminal chat? showAddView.toggle() -#if !os(macOS) windowManager.addWindow(windowType: .workingChangesView, frame: defSize, zIndex: 0) -#endif } }) { VStack { @@ -321,9 +313,7 @@ struct AddView: View { logD("Open container containing repo tree") showAddView.toggle() -#if !os(macOS) windowManager.addWindow(windowType: .repoTreeView, frame: defSize, zIndex: 0, url: settingsViewModel.defaultURL) -#endif } } Text("Files") @@ -353,9 +343,7 @@ struct AddView: View { logD("Open container containing repo tree") showAddView.toggle() -#if !os(macOS) windowManager.addWindow(windowType: .windowListView, frame: defSize, zIndex: 0, url: settingsViewModel.defaultURL) -#endif } } Text("Windows") diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ChatView.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ChatView.swift index 643b6b7..e69fb03 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ChatView.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ChatView.swift @@ -18,7 +18,7 @@ struct ChatView: View { @Binding var conversations: [Conversation] - var window: WindowInfo? + var window: WindowInfo @Binding var isEditing: Bool @Binding var isLockToBottom: Bool @@ -40,7 +40,7 @@ struct ChatView: View { VStack(spacing: 0) { // TODO: OPTIMIZE THIS INEFFECIENT - let convoText = window?.convoId == Conversation.ID(-1) ? settingsViewModel.consoleManagerText : settingsViewModel.convoText(conversations, window: window) + let convoText = window.convoId == Conversation.ID(-1) ? settingsViewModel.consoleManagerText : settingsViewModel.convoText(conversations, window: window) // We reuse the SourceCodeEditor as GPT chat view :) SourceCodeTextEditor(text: $sageMultiViewModel.sourceCode, isEditing: $isEditing, isLockToBottom: $isLockToBottom, customization: @@ -94,7 +94,7 @@ struct ChatView: View { } } // Placeholder... - Text("Type \(window?.convoId == Conversation.ID(-1) ? "Cmd" : "Msg")...") + Text("Type \(window.convoId == Conversation.ID(-1) ? "Cmd" : "Msg")...") .padding(.leading,4) .fontWeight(.light) .font(.system(size: settingsViewModel.fontSizeSrcEditor)) @@ -131,7 +131,7 @@ struct ChatView: View { } } - if window?.convoId == Conversation.ID(-1) { + if window.convoId == Conversation.ID(-1) { // EXEC BUTTON Spacer() @@ -224,7 +224,6 @@ struct ChatView: View { VStack { } #endif } - } func resizableButtonImage(systemName: String, size: CGSize) -> some View { Image(systemName: systemName) @@ -240,7 +239,7 @@ struct ChatView: View { return } - if let convoID = window?.convoId { + if let convoID = window.convoId { if convoID == Conversation.ID(-1) { screamer.sendCommand(command: chatText) } diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/CommandButton.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/CommandButton.swift index a19bcf6..9fadac0 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/CommandButton.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/CommandButton.swift @@ -8,285 +8,35 @@ import Foundation import SwiftUI -enum EntryMode { - case commandText - case commandBar -} - struct CommandButtonView: View { @StateObject var settingsViewModel: SettingsViewModel @FocusState var isTextFieldFocused: Bool @State var textEditorHeight : CGFloat = 20 @ObservedObject var windowManager: WindowManager - @Binding var isInputViewShown: Bool func openText() { isInputViewShown.toggle() -#if !os(macOS) - - // consoleManager.isVisible = !settingsViewModel.isInputViewShown -#endif } - var body: some View { GeometryReader { geometry in VStack { Spacer() - HStack { Spacer() -// if isInputViewShown { - - // .padding(.bottom) -// } - // // GOOGLE button - // Button(action: { - // - // logD("toggling google mode") - // - // }) { - // ZStack { - // Text("🌐") - // Text("❌") - // .opacity(0.6) - // - // } - // .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) - // .lineLimit(1) - // .foregroundColor(Color.white) - // .background(settingsViewModel.buttonColor) - // .cornerRadius(10) - // } - // .padding(.bottom) - // - // Button(action: { - // logD("toggling linking mode") - // }) { - // ZStack { - // Text("🔗") - // Text("❌") - // .opacity(0.6) - // } - // .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) - // .lineLimit(1) - // .foregroundColor(Color.white) - // .background(settingsViewModel.buttonColor) - // } - // .padding(.bottom) - -// if settingsViewModel.currentMode == .computer { -// -// // Debate BUTTON -// Button(action: { -// isTextFieldFocused = true -// settingsViewModel.multiLineText += "debate " -// }) { -// Text( "⚖️") -// .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) -// .lineLimit(1) -// .foregroundColor(Color.white) -// .background(settingsViewModel.buttonColor) -// } -// // .padding(.bottom) -// } -// -// -// if settingsViewModel.currentMode == .computer { -// // i BUTTON -// Button(action: { -// isTextFieldFocused = true -// settingsViewModel.multiLineText += "i " -// }) { -// Text( "💡") -// .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) -// .lineLimit(1) -// .foregroundColor(Color.white) -// .background(settingsViewModel.buttonColor) -// } -// //.padding(.bottom) -// } - - // // g BUTTON - // Button(action: { - // - // self.settingsViewModel.isInputViewShown = false - // settingsViewModel.multiLineText = "" - // - // settingsViewModel.createAndOpenNewConvo() - // - // }) { - // Text( "➕") - // .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) - // .lineLimit(1) - // .foregroundColor(Color.white) - // .background(settingsViewModel.buttonColor) - // .multilineTextAlignment(.center) - //// .padding(.leading, 8) - //// .padding(.trailing, 8) - // - // - // } - // - // .padding(.bottom) - -// } - // if !settingsViewModel.multiLineText.isEmpty && settingsViewModel.isInputViewShown { - // - // // STOP // EXIT CONVERSATIONAL MODE BUTTON - // Button(action: { - // - // // cmd send st - // settingsViewModel.multiLineText = "g end" - // DispatchQueue.main.async { - // - // // Execute your action here - // screamer.sendCommand(command: settingsViewModel.multiLineText) - // - // self.settingsViewModel.isInputViewShown = false - // - // settingsViewModel.multiLineText = "" - // } - // - // }) { - // ZStack { - // Text("🧠") - // Text("❌") - // .opacity(0.74) - // } - // .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) - // - // .lineLimit(1) - // .foregroundColor(Color.white) - // .background(settingsViewModel.buttonColor) - // } - // .padding(.bottom) - // } -// if !settingsViewModel.multiLineText.isEmpty && isInputViewShown { -// -// //.padding(.bottom) -// } - - // STOP BUTTON Button(action: { - - // cmd send st - // settingsViewModel.multiLineText = "st" DispatchQueue.main.async { - // Execute your action here screamer.sendCommand(command: "st") - isInputViewShown = false - } - }) { Text("🛑") .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) .lineLimit(1) .foregroundColor(Color.white) .background(settingsViewModel.buttonColor) - // .padding(.bottom) } - // change into use into send in charview -// if settingsViewModel.multiLineText.count > 0 { -// // EXEC BUTTON -// Button(action: { -// if settingsViewModel.multiLineText.isEmpty { -// logD("nothing to exec.") -// -// return -// } -// // Execute your action here -// screamer.sendCommand(command: settingsViewModel.multiLineText) -// -// isInputViewShown = false -// -// // TODO: Hide or show terminal chat? -// -// }) { -// Text("✅") -// .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) -// .lineLimit(1) -// .foregroundColor(Color.white) -// .background(settingsViewModel.buttonColor) -// // .padding(.bottom) -// } -// } - - // TERM/COMMAND BUTTON -// Button(action: { -// if settingsViewModel.commandMode == .commandText { -// // Execute your action here -// screamer.sendCommand(command: settingsViewModel.multiLineText) -// -// isInputViewShown = false -// settingsViewModel.commandMode = .commandBar -// } -// else { -// // TODO: Hide or show terminal chat? -// -// openText() -// } -// }) { -// if isInputViewShown { -// Text("🔽") -// .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) -// .lineLimit(1) -// .foregroundColor(Color.white) -// .background(settingsViewModel.buttonColor) -// } -// else { -// Text( "⬆️") -// .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) -// .lineLimit(1) -// .foregroundColor(Color.white) -// .background(settingsViewModel.buttonColor) -// } -// -// } } -// -// -// if isInputViewShown { -// // MAIN INPUT TEXTFIELD -// -// ZStack(alignment: .leading) { -// Text(settingsViewModel.multiLineText) -// .font(.system(.body)) -// .foregroundColor(.clear) -// .padding(14) -// .background(GeometryReader { -// Color.clear.preference(key: ViewHeightKey.self, -// value: $0.frame(in: .local).size.height) -// }) -// -// TextEditor(text: $settingsViewModel.multiLineText) -// .lineLimit(nil) -// .border(settingsViewModel.buttonColor, width: 2) -// .frame(height: max(40,textEditorHeight)) -// .padding(.leading, 8) -// .padding(.bottom, 30 + settingsViewModel.commandButtonFontSize) -// .autocorrectionDisabled(!settingsViewModel.autoCorrect) -//#if !os(macOS) -// .autocapitalization(.none) -//#endif -// .focused($isTextFieldFocused) -// .scrollDismissesKeyboard(.interactively) -// .toolbar { -// if isTextFieldFocused { -// ToolbarItemGroup(placement: .keyboard) { -// Spacer() -// -// Button("Done") { -// isTextFieldFocused = false -// } -// } -// } -// } -// -// }.onPreferenceChange(ViewHeightKey.self) { textEditorHeight = $0 } -// } } } } @@ -296,7 +46,6 @@ struct CommandButtonView: View { value = value + nextValue() } } - } struct CustomFontSize: ViewModifier { @Binding var size: Double diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ContentView.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ContentView.swift index 2e33d4c..685542a 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ContentView.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/ContentView.swift @@ -17,7 +17,6 @@ import AppKit struct ContentView: View { @State private var isDrawerOpen = false - @State private var showAddView = false @State private var showSettings = false @State private var code: String = "" @@ -26,7 +25,6 @@ struct ContentView: View { @State private var lastScaleValue: CGFloat = 1.0 @State private var buttonScale: CGFloat = 1.0 @ObservedObject var settingsViewModel: SettingsViewModel = SettingsViewModel() - @StateObject var windowManager = WindowManager() #if !os(macOS) @@ -35,232 +33,197 @@ struct ContentView: View { @State private var isInputViewShown = false @State var showInstructions: Bool = !hasSeenInstructions() @State var showHelp: Bool = false - @State var viewSize: CGRect = .zero #if !os(macOS) @Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.verticalSizeClass) var verticalSizeClass @ObservedObject var keyboardResponder = KeyboardResponder() - #endif var body: some View { GeometryReader { geometry in - HStack(spacing: 0) { + ZStack { #if !os(macOS) - if isDrawerOpen { - let drawerWidth = viewSize.width - (viewSize.width / (UIDevice.current.userInterfaceIdiom == .phone ? 3 : 3)) - - DrawerContent(settingsViewModel: settingsViewModel, windowManager: windowManager, isDrawerOpen: $isDrawerOpen, conversations: $settingsViewModel.conversations, isPortrait: $isPortrait, viewSize: $viewSize, showSettings: $showSettings, showAddView: $showAddView) - .transition(.move(edge: .leading)) - .background(settingsViewModel.buttonColor) - .padding(.leading, 0) - .zIndex(999) - .frame(minWidth: drawerWidth, maxWidth: drawerWidth, minHeight: 0, maxHeight: .infinity) - } + if isDrawerOpen { + let drawerWidth = viewSize.width - (viewSize.width / (UIDevice.current.userInterfaceIdiom == .phone ? 3 : 3)) + DrawerContent(settingsViewModel: settingsViewModel, windowManager: windowManager, isDrawerOpen: $isDrawerOpen, conversations: $settingsViewModel.conversations, isPortrait: $isPortrait, viewSize: $viewSize, showSettings: $showSettings, showAddView: $showAddView) + .transition(.move(edge: .leading)) + .background(settingsViewModel.buttonColor) + .padding(.leading, 0) + .zIndex(999) + .frame(minWidth: drawerWidth, maxWidth: drawerWidth, minHeight: 0, maxHeight: .infinity) + } #endif - ZStack { - ZStack { - ZStack { - // START MAC OS SPECIFIC PANE FOR OPENING TERMINALS AND POTENTIALLY MORE. ********************* -#if os(macOS) - VStack(alignment: .leading, spacing: 8) { - Button(action: { - openTerminal() - }) { - Text("Open Terminal") - } +// HStack(spacing: 0) { - Button(action: { - openiTerm2() - }) { - Text("Open iTerm2") - } - - Button(action: { - openTerminalAndRunCommand(command: "echo 'Hello, Terminal!'") - - }) { - Text("Open Terminal.app and run cmd.") - } + ZStack { +#if !os(macOS) + if !isDrawerOpen { + + // START WINDOW MANAGER ZONE ************************************************* + ForEach(windowManager.windows) { window in + WindowView(window: window, frame: window.convoId != nil ? defChatSize : defSize, settingsViewModel: settingsViewModel, windowManager: windowManager, viewModel: SageMultiViewModel(settingsViewModel: settingsViewModel, windowId: window.id, windowManager: windowManager), parentViewSize: $viewSize) + // .edgesIgnoringSafeArea(.all) + .background(.clear) + .opacity(isDrawerOpen ? 0.0 : 1.0) + .allowsHitTesting(!isDrawerOpen) + .padding(.top, 20) } -#endif } - // END MAC OS SPECIFIC PANE FOR OPENING TERMINALS AND POTENTIALLY MORE. ********************* - } -#if !os(macOS) - // START WINDOW MANAGER ZONE ************************************************* - ForEach(windowManager.windows) { window in - WindowView(window: window, frame: window.convoId != nil ? defChatSize : defSize, settingsViewModel: settingsViewModel, windowManager: windowManager, viewModel: SageMultiViewModel(settingsViewModel: settingsViewModel, windowInfo: window), parentViewSize: $viewSize) - .edgesIgnoringSafeArea(.all) - .background(.clear) - .opacity(isDrawerOpen ? 0.0 : 1.0) - .allowsHitTesting(!isDrawerOpen) - .padding(.top, 20) - } - // END WINDOW MANAGER ZONE ************************************************* + // END WINDOW MANAGER ZONE ************************************************* #endif - // START CONVERSATION HAMBURGER ZONE ************************************************* - if !isDrawerOpen { - VStack { - HStack { - Button(action: { - withAnimation { - showAddView = false - showSettings = false - showHelp = false - showInstructions = false - self.isDrawerOpen.toggle() + // START CONVERSATION HAMBURGER ZONE ************************************************* + if !isDrawerOpen { + VStack { + HStack { + Button(action: { + withAnimation { + showAddView = false + showSettings = false + showHelp = false + showInstructions = false + self.isDrawerOpen.toggle() + } + }) { + Image(systemName: isDrawerOpen ? "x.circle.fill" : "line.horizontal.3") + .resizable() + .scaledToFit() + .tint(settingsViewModel.appTextColor) + .padding(.leading,16) + .padding(.top,16) + .padding(.trailing,8) + .padding(.bottom,8) + .background(settingsViewModel.buttonColor.opacity(0.666)) + .frame(width: 40, height: 40 ) + .animation(.easeIn(duration:0.25), value: isDrawerOpen) } - }) { - Image(systemName: isDrawerOpen ? "x.circle.fill" : "line.horizontal.3") - .resizable() - .scaledToFit() - .tint(settingsViewModel.appTextColor) - .background(settingsViewModel.buttonColor) - .padding(.leading,16) - .padding(.top,16) - -#if !os(macOS) - .frame(width: 40, height: 40 ) -#endif - .animation(.easeIn(duration:0.25), value: isDrawerOpen) -// .edgesIgnoringSafeArea(.all) - + Spacer() } Spacer() } - Spacer() + .zIndex(998) } - .zIndex(998) - } - // END CPNVERSATION HAMBURGER ZONE ************************************************* + // END CPNVERSATION HAMBURGER ZONE ************************************************* #if !os(macOS) + if !isDrawerOpen && keyboardResponder.currentHeight == 0 { + // START TOOL BAR / COMMAND BAR ZONE *************************************************************************** + VStack { + Spacer() + CommandButtonView(settingsViewModel: settingsViewModel, windowManager: windowManager, isInputViewShown: $isInputViewShown) + } + .zIndex(-9) + .padding(.horizontal) + .padding(.vertical) + .padding(.leading, 8) + .padding(.trailing, 8) + .animation(.easeIn(duration:0.25), value: !isDrawerOpen && keyboardResponder.currentHeight == 0) + } +#endif - if !isDrawerOpen && keyboardResponder.currentHeight == 0 { - // START TOOL BAR / COMMAND BAR ZONE *************************************************************************** + // TODO: Get rid of this VStack { Spacer() - CommandButtonView(settingsViewModel: settingsViewModel, windowManager: windowManager, isInputViewShown: $isInputViewShown) } - .zIndex(-9) - - .padding(.horizontal) - .padding(.vertical) - .padding(.leading, 8) - .padding(.trailing, 8) - .animation(.easeIn(duration:0.25), value: !isDrawerOpen && keyboardResponder.currentHeight == 0) - } -#endif - - // TODO: Get rid of this - VStack { - Spacer() - } - .background( - ZStack { + .background( + ZStack { #if !os(macOS) - AddView(showAddView: $showAddView, settingsViewModel: settingsViewModel, windowManager: windowManager, isInputViewShown: $isInputViewShown) - .frame(minWidth: geometry.size.width, maxWidth: geometry.size.width, minHeight: 0, maxHeight: .infinity) - .opacity(showAddView ? 1.0 : 0.0) - - SettingsView(showSettings: $showSettings, showHelp: $showHelp, showInstructions: $showInstructions, settingsViewModel: settingsViewModel) - .frame(minWidth: geometry.size.width, maxWidth: geometry.size.width, minHeight: 0, maxHeight: .infinity) - .opacity(showSettings ? 1.0 : 0.0) + AddView(showAddView: $showAddView, settingsViewModel: settingsViewModel, windowManager: windowManager, isInputViewShown: $isInputViewShown) + .frame(minWidth: geometry.size.width, maxWidth: geometry.size.width, minHeight: 0, maxHeight: .infinity) + .opacity(showAddView ? 1.0 : 0.0) + SettingsView(showSettings: $showSettings, showHelp: $showHelp, showInstructions: $showInstructions, settingsViewModel: settingsViewModel) + .frame(minWidth: geometry.size.width, maxWidth: geometry.size.width, minHeight: 0, maxHeight: .infinity) + .opacity(showSettings ? 1.0 : 0.0) #endif + } + .frame(minWidth: geometry.size.width, maxWidth: geometry.size.width , minHeight: 0, maxHeight: .infinity) + ) + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) + } + .overlay( + Group { + if showInstructions { + InstructionsPopup(isPresented: $showInstructions ,settingsViewModel: settingsViewModel ) + } + else if showHelp { + HelpPopup(isPresented: $showHelp ,settingsViewModel: settingsViewModel ) + } } - .frame(minWidth: geometry.size.width, maxWidth: geometry.size.width , minHeight: 0, maxHeight: .infinity) ) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) - } - .overlay( - Group { - if showInstructions { - InstructionsPopup(isPresented: $showInstructions ,settingsViewModel: settingsViewModel ) - } - else if showHelp { - HelpPopup(isPresented: $showHelp ,settingsViewModel: settingsViewModel ) - } - } - ) - .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) - // END TOOL BAR / COMMAND BAR ZONE *************************************************************************** + // END TOOL BAR / COMMAND BAR ZONE *************************************************************************** - // BEGIN CONTENTVIEW BACKGROUND ZONE *************************************************************************** - .background { - ZStack { + // BEGIN CONTENTVIEW BACKGROUND ZONE *************************************************************************** + .background { + ZStack { #if !os(macOS) - if let image = settingsViewModel.actualReceivedImage { - Image(uiImage: image) - .resizable() - .aspectRatio(contentMode: .fill) - .edgesIgnoringSafeArea(.all) - .animation(.easeIn(duration: 0.28), value: image) - } - else { - settingsViewModel.backgroundColor - .animation(.easeIn(duration: 0.28), value: true) - - .edgesIgnoringSafeArea(.all) - } - // TODO: Make this way better. - if settingsViewModel.initalAnim { - - LoadingLogicView() - .frame( maxWidth: .infinity, maxHeight: .infinity) - .ignoresSafeArea() - .onAppear { - setHasSeenAnim(true) + if let image = settingsViewModel.actualReceivedImage { + Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fill) + .edgesIgnoringSafeArea(.all) + .animation(.easeIn(duration: 0.28), value: image) + } + else { + settingsViewModel.backgroundColor + .animation(.easeIn(duration: 0.28), value: true) - } - } + .edgesIgnoringSafeArea(.all) + } + // TODO: Make this way better. + if settingsViewModel.initalAnim { + LoadingLogicView() + .frame( maxWidth: .infinity, maxHeight: .infinity) + .ignoresSafeArea() + .onAppear { + setHasSeenAnim(true) + } + } #else - settingsViewModel.backgroundColor - .edgesIgnoringSafeArea(.all) + settingsViewModel.backgroundColor + .edgesIgnoringSafeArea(.all) #endif - } - .frame(minWidth: viewSize.size.width, maxWidth: viewSize.size.width, minHeight: viewSize.size.height, maxHeight: .infinity) - .edgesIgnoringSafeArea(.all) - .onTapGesture { - withAnimation { - isDrawerOpen = false + } + .frame(minWidth: viewSize.size.width, maxWidth: viewSize.size.width, minHeight: viewSize.size.height, maxHeight: .infinity) + .edgesIgnoringSafeArea(.all) + .onTapGesture { + withAnimation { + isDrawerOpen = false #if !os(macOS) - hideKeyboard() + hideKeyboard() #endif + } } } - } - .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) - .if(isDrawerOpen) { view in - view.onTapGesture { - if isDrawerOpen { withAnimation { isDrawerOpen = false } } + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) + .if(isDrawerOpen) { view in + view.onTapGesture { + if isDrawerOpen { withAnimation { isDrawerOpen = false } } + } } - } #if !os(macOS) - .onAppear { - recalculateWindowSize(size: geometry.size) - - } - .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in - recalculateWindowSize(size: geometry.size) - } - .onChange(of: geometry.size) { size in - recalculateWindowSize(size: geometry.size) - // logD("contentView viewSize update = \(viewSize)") - } - .onChange(of: horizontalSizeClass) { newSizeClass in - print("Size class changed to \(String(describing: newSizeClass))") - recalculateWindowSize(size: geometry.size) - } - .onChange(of: verticalSizeClass) { newSizeClass in - print("Size class changed to \(String(describing: newSizeClass))") - recalculateWindowSize(size: geometry.size) - } + .onAppear { + recalculateWindowSize(size: geometry.size) + } + .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in + recalculateWindowSize(size: geometry.size) + } + .onChange(of: geometry.size) { size in + recalculateWindowSize(size: geometry.size) + // logD("contentView viewSize update = \(viewSize)") + } + .onChange(of: horizontalSizeClass) { newSizeClass in + print("Size class changed to \(String(describing: newSizeClass))") + recalculateWindowSize(size: geometry.size) + } + .onChange(of: verticalSizeClass) { newSizeClass in + print("Size class changed to \(String(describing: newSizeClass))") + recalculateWindowSize(size: geometry.size) + } #endif - } + } } } // END CONTENTVIEW BACKGROUND ZONE *************************************************************************** @@ -271,7 +234,7 @@ struct ContentView: View { defSize = CGRectMake(0, 0, size.width - (size.width * 0.32), size.height - (size.height * 0.5)) defChatSize = CGRectMake(0, 0, size.width - (size.width * 0.32), size.height - (size.height * 0.52)) - // set parentViewSize + // set parentViewSize viewSize = CGRectMake(0, 0, size.width, size.height) //logD("contentView viewSize update = \(viewSize)") diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Drawer.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Drawer.swift index 35537cc..92ba4fc 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Drawer.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Drawer.swift @@ -99,7 +99,6 @@ struct DrawerContent: View { DispatchQueue.main.async { withAnimation { - showAddView = true } } diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/HelpPopup.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/HelpPopup.swift index f2ed707..8c80c89 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/HelpPopup.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/HelpPopup.swift @@ -41,17 +41,18 @@ struct HelpPopup: View { pasteboard.string = email #endif }) { - Text(verbatim: "\(email) (Tap to Copy.)") + Text(verbatim: "\(email) (Tap to Copy)") .foregroundColor(settingsViewModel.buttonColor) } } Group { - - Text("Tips:\nYou can dock terminals to side of screen to get them out of way.\nKeyboards can be swiped away or dismissed with Done button.\nTurn off `Button Shapes` in System Display settings.\nOn iPad, use the floating keyboard for max screen real estate and reduction of keyboard annoyance.") + Text("Tips:\nKeyboards can be swiped away or dismissed with Done button.\nTurn off `Button Shapes` in System Display settings.\nOn iPad, use the floating keyboard for max screen real estate and reduction of keyboard annoyance.") let verGoodMoji = veryGoodOpen ? "🔽" : "▶️" Text("Tap me! \(verGoodMoji) , I will expand/collapse a section...") + .font(.subheadline) + .onTapGesture { playSelect() @@ -67,15 +68,14 @@ struct HelpPopup: View { Group { Text("Set up LogicSage for Mac:") - Text("OPTIONAL: Set up server to use computer commands. This allows you to use Xcode from your iOS device in the Term window.") + Text("OPTIONAL: Set up LogicSage for Mac to use computer commands. This allows you to use Xcode from your iOS device in the Term window.") Text("Follow this order when running LogicSage for Mac.") } Group { - Text("0. run SwiftSageServer and Swifty-GPT with ./run.sh in the Project root") + Text("0. run LogicSage for Mac with ./run.sh in the Project root") Text("1. Background/Foreground your LogicSage clients, you should see websocket connected. ") - - Text("-COMMANDS\nCheck the following link for the Swifty-GPT server command list:\n https://github.com/cdillard/LogicSage/blob/main/Swifty-GPT/Command/CommandTable.swift\nmobile command list:\n https://github.com/cdillard/LogicSage/blob/main/SwiftSageiOS/SwiftSageiOS/Command/CommandTable.swift") + Text("-In the Term window you can use the following command list:\n https://github.com/cdillard/LogicSage/blob/main/Swifty-GPT/Command/CommandTable.swift") .foregroundColor(settingsViewModel.appTextColor) .accentColor(settingsViewModel.buttonColor) } @@ -110,10 +110,8 @@ struct HelpPopup: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .edgesIgnoringSafeArea(.all) .background(settingsViewModel.backgroundColor) - .onAppear { #if !os(macOS) - UIScrollView.appearance().bounces = false #endif } diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/InstructionsPopup.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/InstructionsPopup.swift index 0d99511..406f7f3 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/InstructionsPopup.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/InstructionsPopup.swift @@ -51,7 +51,7 @@ struct InstructionsPopup: View { pasteboard.string = email #endif }) { - Text(verbatim: "\(email) (Tap to Copy.)") + Text(verbatim: "\(email) (Tap to Copy)") .foregroundColor(settingsViewModel.buttonColor) } Text("Check out Settings to set your A.I. key. Set up LogicSage for Mac to use additional functions from the Terminal window. Terminal window with LogicSage for Mac allows you to use Xcode from your iOS device.") @@ -76,19 +76,16 @@ struct InstructionsPopup: View { .padding(geometry.size.width * 0.01) Button(action: { - isPresented = false setHasSeenInstructions(true) - if !hasSeenAnim() { settingsViewModel.initalAnim = true } - }) { Text("Got it!") - .foregroundColor(.white) + .foregroundColor(settingsViewModel.appTextColor) .padding(geometry.size.width * 0.01) - .background(Color.green) + .background(settingsViewModel.buttonColor) .cornerRadius(8) } .padding(geometry.size.width * 0.01) @@ -99,14 +96,13 @@ struct InstructionsPopup: View { .edgesIgnoringSafeArea(.all) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .edgesIgnoringSafeArea(.all) +// .edgesIgnoringSafeArea(.all) .background(settingsViewModel.backgroundColor) .onAppear { #if !os(macOS) UIScrollView.appearance().bounces = false #endif } - } } } diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/LoadingLogic.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/LoadingLogic.swift index cf30ff2..667b466 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/LoadingLogic.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/LoadingLogic.swift @@ -8,7 +8,6 @@ import Foundation import SwiftUI - let logoAscii8 = """ 🤖 👽 Unleash the spark ⚡️ Creativity @@ -24,22 +23,8 @@ let logoAscii6 = """ ╱╱╱╱╱╱╱╭━╯┃╱╱╱╱╱╱╱╱╱╱╭━╯┃ ╱╱╱╱╱╱╱╰━━╯╱╱╱╱╱╱╱╱╱╱╰━━╯ """ - -func setHasSeenLogo(_ hasSeen: Bool) { - UserDefaults.standard.set(hasSeen, forKey: "hasSeenLogo") -} -func hasSeenLogo() -> Bool { - return UserDefaults.standard.bool(forKey: "hasSeenLogo") -} -func hasSeenAnim() -> Bool { - return UserDefaults.standard.bool(forKey: "hasSeenAnim") -} -func setHasSeenAnim(_ hasSeen: Bool) { - UserDefaults.standard.set(hasSeen, forKey: "hasSeenAnim") -} let animFrameDuration: CGFloat = 0.01 - var chosenLogo = getRandomLogo() func getRandomLogo() -> String { @@ -113,7 +98,6 @@ struct LoadingLogicView: View { .onAppear { if hasSeenAnim() { - } else { DispatchQueue.main.asyncAfter(deadline: .now() + 1.98) { diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView+onDrag.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView+onDrag.swift index 59636e1..a70aeda 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView+onDrag.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView+onDrag.swift @@ -17,7 +17,9 @@ extension SageMultiView { func dragsOnChange(value: DragGesture.Value?) { if !isDragDisabled { if !isMoveGestureActivated { - self.windowManager.bringWindowToFront(window: self.window) + DispatchQueue.main.async { + self.windowManager.bringWindowToFront(window: self.window) + } isMoveGestureActivated = true } func doPostConstraint() { @@ -215,7 +217,7 @@ extension SageMultiView { let setY = max(topYBound, newY) // print("setting pos to \(setX) and \(setY)") - position = CGSize(width: setX,height: setY) + position = CGSize(width:setX,height:setY) } } } diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView.swift index 66f51d5..2c7476d 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiView.swift @@ -28,7 +28,7 @@ enum ViewMode { struct SageMultiView: View { let dragDelay = 0.333666 - let dragsPerSecond = 45.0 + let dragsPerSecond = 60.0 @Binding var showAddView: Bool @ObservedObject var settingsViewModel: SettingsViewModel diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiViewModel.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiViewModel.swift index 3f38744..8910e8e 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiViewModel.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/SageMultiViewModel.swift @@ -10,30 +10,44 @@ import Foundation private var lastConsoleUpdate = Date() class SageMultiViewModel: ObservableObject { @ObservedObject var settingsViewModel: SettingsViewModel - - @Published var windowInfo: WindowInfo + @ObservedObject var windowManager: WindowManager + + @Published var windowInfo: WindowInfo? + @Published var windowId: UUID + @Published var sourceCode: String @Published var changes: [ChangeRow] = [] var originalSourceCode: String - init(settingsViewModel: SettingsViewModel, windowInfo: WindowInfo) { + init(settingsViewModel: SettingsViewModel, windowId: UUID, windowManager: WindowManager) { self.settingsViewModel = settingsViewModel - self.windowInfo = windowInfo - - if let convoId = windowInfo.convoId { - let existingConvo = settingsViewModel.convoText(settingsViewModel.conversations, window: windowInfo) - self.sourceCode = existingConvo.isEmpty ? convoId : existingConvo - } - else if windowInfo.convoId == Conversation.ID(-1) { - self.sourceCode = settingsViewModel.consoleManagerText - + self.windowId = windowId + self.windowManager = windowManager + if let winInfo = windowManager.windows.first(where: { $0.id == windowId }) { + self.windowInfo = winInfo + + if let convoId = winInfo.convoId { + let existingConvo = settingsViewModel.convoText(settingsViewModel.conversations, window: winInfo) + self.sourceCode = existingConvo.isEmpty ? convoId : existingConvo + } + else if winInfo.convoId == Conversation.ID(-1) { + self.sourceCode = settingsViewModel.consoleManagerText + + } + else { + self.sourceCode = winInfo.fileContents + } + + self.originalSourceCode = winInfo.fileContents } else { - self.sourceCode = windowInfo.fileContents + self.sourceCode = "" + self.originalSourceCode = "" + } - self.originalSourceCode = windowInfo.fileContents + } func refreshChanges(newText: String) { diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsVIew.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsVIew.swift index 5f4ccd7..4f96e90 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsVIew.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsVIew.swift @@ -91,15 +91,8 @@ struct SettingsView: View { // MODE PICKER Group { HStack { - // VStack { - // DevicePicker(settingsViewModel: settingsViewModel) - // - // Text("mode = \(settingsViewModel.currentMode == .mobile ? "mobile" : "computer")").font(.body) - // .foregroundColor(settingsViewModel.appTextColor) - // } - // PLugItIn BUTTON - Button("🔌") { + Button("🔌:Reconnect") { logD("force reconnect") logD("If this is not working make sure that in Settings Allow LogicSage Access to Local Network is set tot true.") @@ -119,13 +112,16 @@ struct SettingsView: View { } .font(.body) .lineLimit(nil) + .foregroundColor(settingsViewModel.appTextColor) .background(settingsViewModel.buttonColor) } } VStack { Group { - Text("\(showAPISettings ? "🔽" : "▶️") API settings").font(.body) + Text("\(showAPISettings ? "🔽" : "▶️") API settings") + + .font(.headline) .foregroundColor(settingsViewModel.appTextColor) .padding(8) } @@ -135,11 +131,6 @@ struct SettingsView: View { withAnimation { showAPISettings.toggle() } - } - if showAPISettings { - - - } } if showAPISettings { @@ -306,7 +297,7 @@ struct SettingsView: View { .cornerRadius(8) } } - Text("load mode") + Text("Set Load mode") .foregroundColor(settingsViewModel.appTextColor) .font(.caption) .fontWeight(.semibold) @@ -314,7 +305,8 @@ struct SettingsView: View { .frame( maxWidth: .infinity, maxHeight: .infinity) Group { - Text("\(settingsViewModel.showSizeSliders ? "🔽" : "▶️") sizes").font(.body) + Text("\(settingsViewModel.showSizeSliders ? "🔽" : "▶️") sizes") + .font(.headline) .foregroundColor(settingsViewModel.appTextColor) .padding(4) @@ -392,7 +384,7 @@ struct SettingsView: View { } HStack { - Text("Stop button") + Text("Cmd buttons") .fontWeight(.semibold) .foregroundColor(settingsViewModel.appTextColor) @@ -438,7 +430,8 @@ struct SettingsView: View { .frame( maxWidth: .infinity, maxHeight: .infinity) } Group { - Text("\(settingsViewModel.showAllColorSettings ? "🔽" : "▶️") color").font(.body) + Text("\(settingsViewModel.showAllColorSettings ? "🔽" : "▶️") color") + .font(.headline) .foregroundColor(settingsViewModel.appTextColor) .padding(8) } @@ -459,8 +452,10 @@ struct SettingsView: View { Text("Themes:").font(.body) Text("Deep Space Sparkle") - // .foregroundColor(UIColor(red: 245, green: 255, blue: 250)) - // .background( UIColor(red: 74, green: 100, blue: 108)) + .foregroundColor(settingsViewModel.appTextColor) + +// .foregroundColor(UIColor(red: 245, green: 255, blue: 250)) +// .background( UIColor(red: 74, green: 100, blue: 108)) .font(.body) .padding() .onTapGesture { @@ -471,8 +466,10 @@ struct SettingsView: View { } Text("Hackeresque") - // .foregroundColor(UIColor(red: 57, green: 255, blue: 20)) - // .background( UIColor.black) + .foregroundColor(settingsViewModel.appTextColor) + +// .foregroundColor(UIColor(red: 57, green: 255, blue: 20)) +// .background( UIColor.black) .font(.body) .padding() .onTapGesture { @@ -525,7 +522,8 @@ struct SettingsView: View { } // SOURCE EDITOR COLORS SETTINGS ZONE Group { - Text("\(settingsViewModel.showSourceEditorColorSettings ? "🔽" : "▶️") srceditor colors").font(.body) + Text("\(settingsViewModel.showSourceEditorColorSettings ? "🔽" : "▶️") srceditor colors") + .font(.headline) .foregroundColor(settingsViewModel.appTextColor) .padding(4) @@ -652,7 +650,8 @@ struct SettingsView: View { } Group { - Text("\(settingsViewModel.showAudioSettings ? "🔽" : "▶️") audio settings").font(.body) + Text("\(settingsViewModel.showAudioSettings ? "🔽" : "▶️") audio settings") + .font(.headline) .foregroundColor(settingsViewModel.appTextColor) .padding(8) @@ -665,91 +664,45 @@ struct SettingsView: View { settingsViewModel.showAudioSettings.toggle() } } - + // TODO:Double check toggling ducing Audio and audio output this way works. if settingsViewModel.showAudioSettings { - // // ENABLE MIC BUTTON - // Text("\(settingsViewModel.hasAcceptedMicrophone == true ? "Mic enabled" : "Enable mic")") - // .frame( maxWidth: .infinity, maxHeight: .infinity) - // .foregroundColor(settingsViewModel.appTextColor) - // - // Button(action: { - // withAnimation { - // logD("Requesing mic permission...") - // settingsViewModel.requestMicrophoneAccess { granted in - // settingsViewModel.hasAcceptedMicrophone = granted == true - // } - // } - // }) { - // resizableButtonImage(systemName: - // "mic.badge.plus", - // size: geometry.size) - // .fontWeight(.bold) - // .background(settingsViewModel.buttonColor) - // .cornerRadius(8) - // - // } - // .frame( maxWidth: .infinity, maxHeight: .infinity) - // .padding(8) - // IOS AUDIO SETTING ON/OFF - Text("\(settingsViewModel.voiceOutputenabled ? "Disable" : "Enable") iOS audio output (this device)") - .foregroundColor(settingsViewModel.appTextColor) - .frame( maxWidth: .infinity, maxHeight: .infinity) - HStack { - Button { - withAnimation { + Toggle(isOn: $settingsViewModel.voiceOutputenabled) { + VStack(spacing: 4) { + Text("Audio Output") + .foregroundColor(settingsViewModel.appTextColor) + .frame( maxWidth: .infinity, maxHeight: .infinity) + } + } + .frame( maxWidth: geometry.size.width / 3) + .onChange(of: settingsViewModel.voiceOutputenabled) { value in + withAnimation { #if !os(macOS) - SettingsViewModel.shared.logText("toggling audio \(settingsViewModel.voiceOutputenabled ? "off" : "on.")") - if settingsViewModel.voiceOutputenabled { - settingsViewModel.stopVoice() - } + SettingsViewModel.shared.logText("toggling audio \(settingsViewModel.voiceOutputenabled ? "off" : "on.")") + if settingsViewModel.voiceOutputenabled { + settingsViewModel.stopVoice() + } - settingsViewModel.configureAudioSession() - settingsViewModel.printVoicesInMyDevice() + settingsViewModel.configureAudioSession() + settingsViewModel.printVoicesInMyDevice() - settingsViewModel.voiceOutputenabled.toggle() - settingsViewModel.voiceOutputenabledUserDefault.toggle() + settingsViewModel.voiceOutputenabled.toggle() + settingsViewModel.voiceOutputenabledUserDefault.toggle() #endif - } - - } label: { - resizableButtonImage(systemName: - settingsViewModel.voiceOutputenabled ? "speaker.wave.2.bubble.left.fill" : "speaker.slash.circle.fill", - size: geometry.size) - .fontWeight(.bold) - .cornerRadius(8) } - .frame( maxWidth: .infinity, maxHeight: .infinity) - .padding(8) + } + HStack { if settingsViewModel.voiceOutputenabled { - Button { - withAnimation { - logD("duck audio: \(settingsViewModel.voiceOutputenabled ? "off" : "on.")") - - settingsViewModel.duckingAudio.toggle() - } - } label: { - ZStack { - VStack { - Text("Duck Audio?") - .foregroundColor(settingsViewModel.appTextColor) - .font(.body) - Text("🦆") - } - if settingsViewModel.duckingAudio { - Text("❌") - .opacity(0.74) - } + // IOS AUDIO SETTING ON/OFF + Toggle(isOn: $settingsViewModel.duckingAudio) { + VStack(spacing: 4) { + Text("Duck Audio") + .foregroundColor(settingsViewModel.appTextColor) + .frame( maxWidth: .infinity, maxHeight: .infinity) } - .modifier(CustomFontSize(size: $settingsViewModel.commandButtonFontSize)) - .lineLimit(1) - .background(settingsViewModel.buttonColor) - .fontWeight(.bold) - .cornerRadius(8) } - .padding(8) - .frame( maxWidth: .infinity, maxHeight: .infinity) + .frame( maxWidth: geometry.size.width / 3) } } } diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel+Convo.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel+Convo.swift new file mode 100644 index 0000000..f9868c0 --- /dev/null +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel+Convo.swift @@ -0,0 +1,145 @@ +// +// SettingsViewModel+Convo.swift +// SwiftSageiOS +// +// Created by Chris Dillard on 6/8/23. +// + +import Foundation +import SwiftUI +extension SettingsViewModel { + func renameConvo(_ convoId: Conversation.ID, newName: String) { + logD("rename convo id = \(convoId) to \(newName)") + guard let conversationIndex = conversations.firstIndex(where: { $0.id == convoId }) else { + logD("Unable to find conversations id == \(convoId) ... failing") + + return + } + SettingsViewModel.shared.conversations[conversationIndex].name = newName + + saveConvosToDisk() + } + func saveConvosToDisk() { + saveConversationContentToDisk(object: conversations, forKey: jsonFileName) + + } + func appendMessageToConvoIndex(index: Int, message: Message) async { + conversations[index].messages.append(message) + } + func setMessageAtConvoIndex(index: Int, existingMessageIndex: Int, message: Message) async { + conversations[index].messages[existingMessageIndex] = message + } + func nilOutConversationErrorsAt(convoId: Conversation.ID) async { + conversationErrors[convoId] = nil + } + func setConversationError(convoId: Conversation.ID, error: Error) async { + conversationErrors[convoId] = error + } + + + func sendChatText(_ convoID: Conversation.ID, chatText: String) { + gptCommand(conversationId: convoID, input: chatText) + } + func createConversation() -> Conversation.ID { + let conversation = Conversation(id: idProvider(), messages: []) + conversations.append(conversation) + logD("created new convo = \(conversation.id)") + return conversation.id + } + func deleteConversation(_ conversationId: Conversation.ID) { + conversations.removeAll(where: { $0.id == conversationId }) + + latestWindowManager?.removeWindowsWithConvoId(convoID: conversationId) + saveConvosToDisk() + } + func createAndOpenNewConvo() { + let convo = createConversation() + + saveConvosToDisk() + + openConversation(convo) + } + func openConversation(_ convoId: Conversation.ID) { + latestWindowManager?.removeWindowsWithConvoId(convoID: convoId) + +#if !os(macOS) + latestWindowManager?.addWindow(windowType: .chat, frame: defChatSize, zIndex: 0, url: defaultURL, convoId: convoId) +#endif + } + func createAndOpenServerChat() { + + openConversation(Conversation.ID(-1)) + } + func saveConversationContentToDisk(object: [Conversation], forKey key: String) { + + let encoder = JSONEncoder() + do { + let encodedData = try encoder.encode(object) + + saveJSONData(encodedData, filename: "\(key).json") + } + catch { + logD("failed w error = \(error)") + } + } + func retrieveConversationContentFromDisk(forKey key: String) -> [Conversation]? { + + if let savedData = loadJSONData(filename: "\(key).json") { + do { + let decoder = JSONDecoder() + return try decoder.decode([Conversation].self, from: savedData) + } + catch { + logD("failed w error = \(error)") + } + } + return nil + } + func loadJSONData(filename: String) -> Data? { + let fileURL = getDocumentsDirectory().appendingPathComponent(filename) + + do { + let data = try Data(contentsOf: fileURL) + return data + } catch { + logD("Failed to read JSON data: \(error.localizedDescription)") + return nil + } + } + func saveJSONData(_ data: Data, filename: String) { + let fileURL = getDocumentsDirectory().appendingPathComponent(filename) + + do { + try data.write(to: fileURL) + } catch { + logD("Failed to write JSON data: \(error.localizedDescription)") + } + } + + func convoText(_ newConversations: [Conversation], window: WindowInfo?) -> String { + var retString = "" + if let conversation = newConversations.first(where: { $0.id == window?.convoId }) { + for msg in conversation.messages { + retString += "\(msg.role == .user ? savedUserAvatar : savedBotAvatar):\n\(msg.content.trimmingCharacters(in: .whitespacesAndNewlines))\n" + } + + } + return retString + } + + func convoText(_ newConversation: Conversation, window: WindowInfo?) -> String { + var retString = "" + for msg in newConversation.messages { + retString += "\(msg.role == .user ? savedUserAvatar : savedBotAvatar):\n\(msg.content.trimmingCharacters(in: .whitespacesAndNewlines))\n" + } + return retString + } + + func convoName(_ convoId: Conversation.ID) -> String { + if let conversation = conversations.first(where: { $0.id == convoId }) { + return conversation.name ?? String(convoId.prefix(4)) + } + + return "Term" + } +} diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel+Websocket.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel+Websocket.swift new file mode 100644 index 0000000..37dff0f --- /dev/null +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel+Websocket.swift @@ -0,0 +1,107 @@ +// +// SettingsViewModel+Websocket.swift +// SwiftSageiOS +// +// Created by Chris Dillard on 6/8/23. +// + +import Foundation +import SwiftUI + +extension SettingsViewModel { + + func recieveImageData(recievedImageData: Data?) { +#if !os(macOS) + + actualReceivedImage = UIImage(data: recievedImageData ?? Data()) + if let receivedWallpaperFileName, let receivedImageData { + let receivedWallpaperFileName = receivedWallpaperFileName.replacingOccurrences(of: " ", with: "%20") + // print("REC -- going to write -- \(receivedWallpaperFileName) c: \(receivedWallpaperFileSize ?? 0)") + + do { + try FileManager.default.createDirectory(at: getDocumentsDirectory().appendingPathComponent("LogicSageWallpapers"), withIntermediateDirectories: true, attributes: nil) + } + catch { + print("fail to create LogicSageWallpapers dir") + } + + let fileURL = getDocumentsDirectory().appendingPathComponent("LogicSageWallpapers/\(receivedWallpaperFileName)") + + + do { + try FileManager.default.removeItem(at: fileURL) + } + catch { + print("didnt rmv or exist old m") + } + + do { + try receivedImageData.write(to: fileURL) + + print("Successfully wrote out wallpaper \(fileURL)") + logD("LogicSage set your wallpaper.") + } + catch { + print("Failed to write out rec wallpaper") + } + + self.receivedWallpaperFileName = nil + receivedWallpaperFileSize = nil + + refreshDocuments() + } +#endif + } + + func recieveWorkspaceData(receivedWorkspaceData: Data) { +#if !os(macOS) + + logD("received workspace data of size = \(receivedWorkspaceData.count)") + let fileURL = getDocumentsDirectory().appendingPathComponent("workspace_archive.zip") + + do { + try FileManager.default.removeItem(at: fileURL) + + } + catch { + logD("fil enot exist or deketed") + } + do { + try receivedWorkspaceData.write(to: fileURL) + + logD("wrote file \(fileURL) out successfully") + let existingExtraction = getDocumentsDirectory().appendingPathComponent("Workspace") + + do { + try FileManager.default.removeItem(at: existingExtraction) + } + catch { + logD("did not delete or didn't exist old REPO") + } + let myProgress = Progress() + do { + // Workspace is already included in this folder. + try FileManager.default.unzipItem(at: fileURL, to: getDocumentsDirectory(), progress: myProgress) + + logD("unzipped to \(existingExtraction) successfully") + + do { + try FileManager.default.removeItem(at: fileURL) + } + catch { + print("did not delete or didn't exist old REPO") + } + + self.refreshDocuments() + } + catch { + logD("failed to unzip workspace") + } + } + catch { + logD("failed to write out zip") + } +#endif + + } +} diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel.swift index 70a8304..889bbd8 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/Settings/SettingsViewModel.swift @@ -31,22 +31,23 @@ let STRING_LIMIT = 150000 // TODO BEFORE RELEASE: PROD BUNDLE ID // TODO USE BUILT IN BundleID var. let bundleID = "com.chrisdillard.SwiftSage" +let appLink = URL(string: "https://apps.apple.com/us/app/logicsage/id6448485441")! - -public class SettingsViewModel: ObservableObject { - func logoAscii5() -> String { +func logoAscii5(model: String = SettingsViewModel.shared.openAIModel) -> String { """ ┃┃╱╱╭━━┳━━┳┳━━┫╰━━┳━━┳━━┳━━╮ ┃┃╱╭┫╭╮┃╭╮┣┫╭━┻━━╮┃╭╮┃╭╮┃┃━┫ ┃╰━╯┃╰╯┃╰╯┃┃╰━┫╰━╯┃╭╮┃╰╯┃┃━┫ -╰━━━┻━━┻━╮┣┻━━┻━━━┻╯╰┻━╮┣━━╯model: \(openAIModel) +╰━━━┻━━┻━╮┣┻━━┻━━━┻╯╰┻━╮┣━━╯model: \(model) """ - } +} - static let link = URL(string: "https://apps.apple.com/us/app/logicsage/id6448485441")! +public class SettingsViewModel: ObservableObject { public static let shared = SettingsViewModel() + let keychainManager = KeychainManager() + @State private var lastConsoleUpdate: Date? func logText(_ text: String, terminator: String = "\n") { let now = Date() @@ -63,18 +64,14 @@ public class SettingsViewModel: ObservableObject { var latestWindowManager: WindowManager? var serviceDiscovery: ServiceDiscovery? - func doDiscover() { - serviceDiscovery?.startDiscovering() - } - @AppStorage("savedUserAvatar") var savedUserAvatar: String = "👨" - @AppStorage("savedBotAvatar") var savedBotAvatar: String = "🤖" // BEGIN SAVED UI SETTINGS ZONE ************************************************************************************** - let keychainManager = KeychainManager() @Published var root: RepoFile? @AppStorage("hapticsEnabled") var hapticsEnabled: Bool = true + @AppStorage("savedUserAvatar") var savedUserAvatar: String = "👨" + @AppStorage("savedBotAvatar") var savedBotAvatar: String = "🤖" @Published var changes = [ChangeRow]() #if !os(macOS) @@ -84,10 +81,6 @@ public class SettingsViewModel: ObservableObject { @Published var isLoading: Bool = false var cancellable: AnyCancellable? - @Published var commandMode: EntryMode = .commandBar - -// @AppStorage("savedText") var multiLineText = "" - @Published var hasAcceptedMicrophone = false @Published var showAllColorSettings: Bool = false @@ -107,58 +100,19 @@ public class SettingsViewModel: ObservableObject { @AppStorage("autoCorrect") var autoCorrect: Bool = true @AppStorage("defaultURL") var defaultURL = "https://" - @Published var initalAnim: Bool = false - + + func doDiscover() { + serviceDiscovery?.startDiscovering() + } + // END SAVED UI SETTINGS ZONE ************************************************************************************** -// BEGIN STREAMING IMAGES OVER WEBSOCKET ZONE ***************************************************************** +// BEGIN STREAMING IMAGES/ZIPZ OVER WEBSOCKET ZONE ***************************************************************** #if !os(macOS) @Published var receivedImageData: Data? = nil { didSet { - actualReceivedImage = UIImage(data: receivedImageData ?? Data()) - - // received - if let receivedWallpaperFileName, let receivedImageData { - let receivedWallpaperFileName = receivedWallpaperFileName.replacingOccurrences(of: " ", with: "%20") - logD("REC -- going to write -- \(receivedWallpaperFileName) c: \(receivedWallpaperFileSize ?? 0)") - - - do { - try FileManager.default.createDirectory(at: getDocumentsDirectory().appendingPathComponent("LogicSageWallpapers"), withIntermediateDirectories: true, attributes: nil) - } - catch { - logD("fail to create LogicSageWallpapers dir") - } - - let fileURL = getDocumentsDirectory().appendingPathComponent("LogicSageWallpapers/\(receivedWallpaperFileName)") - - - do { - try FileManager.default.removeItem(at: fileURL) - } - catch { - logD("didnt rmv or exist old m") - - } - - do { - try receivedImageData.write(to: fileURL) - - logD("Successfully wrote out wallpaper \(fileURL)") - - } - catch { - logD("Failed to write out rec wallpaper") - - } - - - self.receivedWallpaperFileName = nil - receivedWallpaperFileSize = nil - - refreshDocuments() - } + recieveImageData(recievedImageData: receivedImageData) } } @Published var actualReceivedImage: UIImage? @@ -169,74 +123,25 @@ public class SettingsViewModel: ObservableObject { didSet { actualReceivedSimulatorFrame = UIImage(data: receivedSimulatorFrameData ?? Data()) - if oldValue == nil { DispatchQueue.main.asyncAfter(deadline: .now() + 0.33) { -#if !os(macOS) self.latestWindowManager?.addWindow(windowType: .simulator, frame: defSize, zIndex: 0) -#endif } } } } @Published var actualReceivedSimulatorFrame: UIImage? - @Published var receivedWorkspaceData: Data? = nil { + @Published var recievedWorkspaceData: Data? = nil { didSet { - if let receivedWorkspaceData { - logD("received workspace data of size = \(receivedWorkspaceData.count)") - let fileURL = getDocumentsDirectory().appendingPathComponent("workspace_archive.zip") - - do { - try FileManager.default.removeItem(at: fileURL) - - } - catch { - logD("fil enot exist or deketed") - } - do { - try receivedWorkspaceData.write(to: fileURL) - - logD("wrote file \(fileURL) out successfully") - let existingExtraction = getDocumentsDirectory().appendingPathComponent("Workspace") - - do { - try FileManager.default.removeItem(at: existingExtraction) - } - catch { - logD("did not delete or didn't exist old REPO") - } - let myProgress = Progress() - do { - // Workspace is already included in this folder. - try FileManager.default.unzipItem(at: fileURL, to: getDocumentsDirectory(), progress: myProgress) - - logD("unzipped to \(existingExtraction) successfully") - - do { - try FileManager.default.removeItem(at: fileURL) - } - catch { - print("did not delete or didn't exist old REPO") - } - - self.refreshDocuments() - } - catch { - logD("failed to unzip workspace") - - } - } - catch { - logD("failed to write out zip") - } - + if let recievedWorkspaceData { + recieveWorkspaceData(receivedWorkspaceData: recievedWorkspaceData) } } } #endif -// END STREAMING IMAGES OVER WEBSOCKET ZONE ***************************************************************** +// END STREAMING IMAGES/ZIPZ OVER WEBSOCKET ZONE ***************************************************************** // BEGIN SAVED AUDIO SETTINS ZONE ***************************************************************** @Published var voiceOutputenabled = false @@ -260,115 +165,90 @@ public class SettingsViewModel: ObservableObject { // END SAVED AUDIO SETTINGS ZONE ***************************************************************** // BEGIN SAVED SIZES ZONE ************************************************************************************** - // TOOL BAR BUTOTN SIZE @AppStorage("savedButtonSize") var buttonScale: Double = defaultToolbarButtonScale - // COMMAND BUTTON SIZE @AppStorage("commandButtonFontSize")var commandButtonFontSize: Double = defaultCommandButtonSize @AppStorage("cornerHandleSize")var cornerHandleSize: Double = defaultHandleSize -// @AppStorage("textSize") var textSize: Double = defaultTerminalFontSize { -// didSet { -// if textSize != 0 { -// UserDefaults.standard.set(textSize, forKey: "textSize") -// } -// else { -// logD("failed to set terminal text size") -// } -// } -// } + @AppStorage("fontSizeSrcEditor") var fontSizeSrcEditor: Double = defaultSourceEditorFontSize + // END SAVED SIZES ZONE ******************************************************************************************** @Published var appTextColor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(appTextColor.rawValue, forKey: "appTextColor") -#endif + setUserDefaultFor(appTextColor, "appTextColor") } } @Published var buttonColor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(buttonColor.rawValue , forKey: "buttonColor") -#endif + setUserDefaultFor(buttonColor, "buttonColor") } } @Published var backgroundColor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(backgroundColor.rawValue, forKey: "backgroundColor") -#endif + setUserDefaultFor(backgroundColor, "backgroundColor") } } - // BEGIN SUB ZONE FOR SRC EDITOR COLORS ******************************************** - @AppStorage("fontSizeSrcEditor") var fontSizeSrcEditor: Double = defaultSourceEditorFontSize + @Published var plainColorSrcEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(plainColorSrcEditor.rawValue , forKey: "plainColorSrcEditor") -#endif + setUserDefaultFor(plainColorSrcEditor, "plainColorSrcEditor") } } @Published var numberColorSrcEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(numberColorSrcEditor.rawValue , forKey: "numberColorSrcEditor") -#endif + setUserDefaultFor(numberColorSrcEditor, "numberColorSrcEditor") } } @Published var stringColorSrcEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(stringColorSrcEditor.rawValue , forKey: "stringColorSrcEditor") -#endif + setUserDefaultFor(stringColorSrcEditor, "stringColorSrcEditor") } } @Published var identifierColorSrcEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(identifierColorSrcEditor.rawValue, forKey: "identifierColorSrcEditor") -#endif + setUserDefaultFor(identifierColorSrcEditor, "identifierColorSrcEditor") } } @Published var keywordColorSrcEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(keywordColorSrcEditor.rawValue , forKey: "keywordColorSrcEditor") -#endif + setUserDefaultFor(keywordColorSrcEditor, "keywordColorSrcEditor") } } @Published var commentColorSrceEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(commentColorSrceEditor.rawValue , forKey: "commentColorSrceEditor") -#endif + setUserDefaultFor(commentColorSrceEditor, "commentColorSrceEditor") } } @Published var editorPlaceholderColorSrcEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(editorPlaceholderColorSrcEditor.rawValue , forKey: "editorPlaceholderColorSrcEditor") -#endif + setUserDefaultFor(editorPlaceholderColorSrcEditor, "editorPlaceholderColorSrcEditor") } } @Published var backgroundColorSrcEditor: Color { didSet { -#if !os(macOS) - UserDefaults.standard.set(backgroundColorSrcEditor.rawValue, forKey: "backgroundColorSrcEditor") -#endif + setUserDefaultFor(backgroundColorSrcEditor, "backgroundColorSrcEditor") } } @Published var lineNumbersColorSrcEditor: Color { didSet { + setUserDefaultFor(lineNumbersColorSrcEditor, "lineNumbersColorSrcEditor") + } + } + + func setUserDefaultFor(_ rawValue: Color, _ key: String) { #if !os(macOS) - UserDefaults.standard.set(lineNumbersColorSrcEditor.rawValue, forKey: "lineNumbersColorSrcEditor") + UserDefaults.standard.set(rawValue.rawValue, forKey: key) #endif - } } - // END SUB ZONE FOR SRC EDITOR COLORS******************************************** - // END SAVED COLORS ZONE ************************************************************************************** - // BEGIN CLIENT API KEYS ZONE ****************************************************************************** - // TODO: React to user name and password change +// END SUB ZONE FOR SRC EDITOR COLORS******************************************** +// END SAVED COLORS ZONE ************************************************************************************** + +// BEGIN CLIENT API KEYS ZONE ****************************************************************************** +// TODO: React to user name and password change + let aiKeyKey = "openAIKeySec" + let ghaKeyKey = "ghaPat" + @AppStorage("userName") var userName = "chris" { didSet { } @@ -382,8 +262,6 @@ public class SettingsViewModel: ObservableObject { } } } - let aiKeyKey = "openAIKeySec" - let ghaKeyKey = "ghaPat" @AppStorage("openAIModel") var openAIModel = defaultGPTModel @@ -417,6 +295,10 @@ public class SettingsViewModel: ObservableObject { // END CLIENT APIS ZONE ************************************************************************************** // START GPT CONVERSATION VIEWMODEL ZONE *********************************************************************** + + let idProvider: () -> String + let dateProvider: () -> Date + @Published var conversations: [Conversation] = [] { didSet { //logD("new convo state:\n\(conversations)") @@ -425,141 +307,12 @@ public class SettingsViewModel: ObservableObject { @Published var conversationErrors: [Conversation.ID: Error] = [:] { didSet { if !conversationErrors.isEmpty { - //logD("new convo error state = \(conversationErrors)") + logD("new convo error state = \(conversationErrors)") } } } @AppStorage("completedMessages") var completedMessages = 0 - func renameConvo(_ convoId: Conversation.ID, newName: String) { - - logD("rename convo id = \(convoId) to \(newName)") - guard let conversationIndex = conversations.firstIndex(where: { $0.id == convoId }) else { - logD("Unable to find conversations id == \(convoId) ... failing") - - return - } - SettingsViewModel.shared.conversations[conversationIndex].name = newName - - saveConvosToDisk() - } - func saveConvosToDisk() { - saveConversationContentToDisk(object: conversations, forKey: jsonFileName) - - } - func appendMessageToConvoIndex(index: Int, message: Message) async { - conversations[index].messages.append(message) - } - func setMessageAtConvoIndex(index: Int, existingMessageIndex: Int, message: Message) async { - conversations[index].messages[existingMessageIndex] = message - } - func nilOutConversationErrorsAt(convoId: Conversation.ID) async { - conversationErrors[convoId] = nil - } - func setConversationError(convoId: Conversation.ID, error: Error) async { - conversationErrors[convoId] = error - } - let idProvider: () -> String - let dateProvider: () -> Date - - func sendChatText(_ convoID: Conversation.ID, chatText: String) { - gptCommand(conversationId: convoID, input: chatText) - } - func createConversation() -> Conversation.ID { - let conversation = Conversation(id: idProvider(), messages: []) - conversations.append(conversation) - logD("created new convo = \(conversation.id)") - return conversation.id - } - func deleteConversation(_ conversationId: Conversation.ID) { - conversations.removeAll(where: { $0.id == conversationId }) - - latestWindowManager?.removeWindowsWithConvoId(convoID: conversationId) - saveConvosToDisk() - } - func createAndOpenNewConvo() { - let convo = createConversation() - - saveConvosToDisk() - - openConversation(convo) - } - func openConversation(_ convoId: Conversation.ID) { - latestWindowManager?.removeWindowsWithConvoId(convoID: convoId) - -#if !os(macOS) - latestWindowManager?.addWindow(windowType: .chat, frame: defChatSize, zIndex: 0, url: defaultURL, convoId: convoId) -#endif - } - func createAndOpenServerChat() { - - openConversation(Conversation.ID(-1)) - } - func saveConversationContentToDisk(object: [Conversation], forKey key: String) { - - let encoder = JSONEncoder() - do { - let encodedData = try encoder.encode(object) - - saveJSONData(encodedData, filename: "\(key).json") - } - catch { - logD("failed w error = \(error)") - } - } - func retrieveConversationContentFromDisk(forKey key: String) -> [Conversation]? { - - if let savedData = loadJSONData(filename: "\(key).json") { - do { - let decoder = JSONDecoder() - return try decoder.decode([Conversation].self, from: savedData) - } - catch { - logD("failed w error = \(error)") - } - } - return nil - } - func loadJSONData(filename: String) -> Data? { - let fileURL = getDocumentsDirectory().appendingPathComponent(filename) - - do { - let data = try Data(contentsOf: fileURL) - return data - } catch { - logD("Failed to read JSON data: \(error.localizedDescription)") - return nil - } - } - func saveJSONData(_ data: Data, filename: String) { - let fileURL = getDocumentsDirectory().appendingPathComponent(filename) - - do { - try data.write(to: fileURL) - } catch { - logD("Failed to write JSON data: \(error.localizedDescription)") - } - } - - func convoText(_ newConversations: [Conversation], window: WindowInfo?) -> String { - var retString = "" - if let conversation = newConversations.first(where: { $0.id == window?.convoId }) { - for msg in conversation.messages { - retString += "\(msg.role == .user ? savedUserAvatar : savedBotAvatar):\n\(msg.content.trimmingCharacters(in: .whitespacesAndNewlines))\n" - } - - } - return retString - } - - func convoName(_ convoId: Conversation.ID) -> String { - if let conversation = conversations.first(where: { $0.id == convoId }) { - return conversation.name ?? String(convoId.prefix(4)) - } - - return "Term" - } - // END GPT CONVERSATION VIEWMODEL ZONE *********************************************************************** // START DEBUGGER VIEWMODEL ZONE *********************************************************************** @@ -573,14 +326,13 @@ public class SettingsViewModel: ObservableObject { // END DEBUGGER VIEWMODEL ZONE *********************************************************************** // START STOREKIT ZONE *********************************************************************** - func requestReview() { DispatchQueue.main.async { self.completedMessages += 1 let newCompletionMsgs = self.completedMessages - print("\(newCompletionMsgs) % 7 = will review") + print("\(newCompletionMsgs) % 13 = will review") - guard newCompletionMsgs % 11 == 0 else { + guard newCompletionMsgs % 13 == 0 else { return logD("no review today") } @@ -591,21 +343,12 @@ public class SettingsViewModel: ObservableObject { // END STOREKIT ZONE *********************************************************************** init() { - self.idProvider = { UUID().uuidString } self.dateProvider = Date.init - // BEGIN SIZE SETTING LOAD ZONE FROM DISK - -// if UserDefaults.standard.double(forKey: "textSize") != 0 { -// self.textSize = CGFloat(UserDefaults.standard.double(forKey: "textSize")) -// } -// else { -// self.textSize = defaultTerminalFontSize -// } - +// BEGIN SIZE SETTING LOAD ZONE FROM DISK ********************************* if UserDefaults.standard.double(forKey: "savedButtonSize") != 0 { self.buttonScale = UserDefaults.standard.double(forKey: "savedButtonSize") } @@ -621,8 +364,7 @@ public class SettingsViewModel: ObservableObject { // END SIZE SETTING LOAD ZONE FROM DISK - // BEGIN LOAD CLIENT SECRET FROM KEYCHAIN ZONE ****************************** - +// BEGIN LOAD CLIENT SECRET FROM KEYCHAIN ZONE ****************************** if let key = keychainManager.retrieveFromKeychain(key: aiKeyKey) { self.openAIKey = key @@ -649,28 +391,10 @@ public class SettingsViewModel: ObservableObject { // print("Error retrieving ghaPat == reset") // keychainManager.saveToKeychain(key:ghaKeyKey, value: "") } - - // END LOAD CLIENT SECRET FROM KEYCHAIN ZONE ****************************** +// END LOAD CLIENT SECRET FROM KEYCHAIN ZONE ****************************** #if !os(macOS) - - // BEGIN TERM / APP COLOR LOAD FROM DISK ZONE ****************************** -// if let colorKey = UserDefaults.standard.string(forKey: "terminalBackgroundColor") { -// -// self.terminalBackgroundColor = Color(rawValue:colorKey) ?? .black -// } -// else { -// self.terminalBackgroundColor = .black -// } -// -// if let colorKey = UserDefaults.standard.string(forKey: "terminalTextColor") { -// -// self.terminalTextColor = Color(rawValue:colorKey) ?? .white -// } -// else { -// self.terminalTextColor = .white -// } - +// BEGIN TERM / APP COLOR LOAD FROM DISK ZONE ****************************** if let colorKey = UserDefaults.standard.string(forKey: "buttonColor") { self.buttonColor = Color(rawValue:colorKey) ?? .accentColor } @@ -692,15 +416,12 @@ public class SettingsViewModel: ObservableObject { self.appTextColor = .primary } #else -// self.terminalBackgroundColor = .black -// self.terminalTextColor = .white self.buttonColor = .green self.backgroundColor = .gray self.appTextColor = .primary #endif - // BEGIN SUB ZONE FOR LOADING SRC EDITOR COLORS FROM DISK\ - +// BEGIN SUB ZONE FOR LOADING TERM / CHAT / SRC COLORS FROM DISK ****************************** if UserDefaults.standard.double(forKey: "fontSizeSrcEditor") != 0 { self.fontSizeSrcEditor = UserDefaults.standard.double(forKey: "fontSizeSrcEditor") } @@ -708,7 +429,6 @@ public class SettingsViewModel: ObservableObject { self.fontSizeSrcEditor = defaultSourceEditorFontSize } #if !os(macOS) - if let colorKey = UserDefaults.standard.string(forKey: "plainColorSrcEditor") { self.plainColorSrcEditor = Color(rawValue:colorKey) ?? .white } @@ -779,19 +499,14 @@ public class SettingsViewModel: ObservableObject { self.numberColorSrcEditor = .white self.stringColorSrcEditor = .green self.identifierColorSrcEditor = .gray - self.keywordColorSrcEditor = .gray self.commentColorSrceEditor = .gray - self.editorPlaceholderColorSrcEditor = .gray self.backgroundColorSrcEditor = .gray - self.lineNumbersColorSrcEditor = .gray #endif - // END SUB ZONE FOR LOADING SRC EDITOR COLORS FROM DISK\ - - - // END COLOR LOAD FROM DISK ZONE ****************************** +// END SUB ZONE FOR LOADING TERM / CHAT / SRC EDITOR COLORS FROM DISK ****************************** +// END COLOR LOAD FROM DISK ZONE ****************************** // BEGIN AUDIO SETTING LOAD ZONE FROM DISK self.duckingAudio = UserDefaults.standard.bool(forKey: "duckingAudio") @@ -817,24 +532,17 @@ public class SettingsViewModel: ObservableObject { // END AUDIO SETTING LOAD ZONE FROM DISK - // START LOADING SAVED GIT REPOS LOAD ZONE FROM DISK - refreshDocuments() - // END LOADING SAVED GIT REPOS LOAD ZONE FROM DISK - DispatchQueue.main.async { + DispatchQueue.main.async { if let convos = self.retrieveConversationContentFromDisk(forKey: jsonFileName) { self.conversations = convos } } } - enum Device: Int { - case mobile, computer - } - func currentGitRepoKey() -> String { "\(gitUser)\(SettingsViewModel.gitKeySeparator)\(gitRepo)\(SettingsViewModel.gitKeySeparator)\(gitBranch)" } @@ -856,12 +564,9 @@ public class SettingsViewModel: ObservableObject { #if !os(macOS) switch theme { case .deepSpace: -// terminalBackgroundColor = Color(hex: 0x4A646C, alpha: 1) -// terminalTextColor = Color(hex: 0xF5FFFA, alpha: 1) appTextColor = Color(hex: 0xF5FFFA, alpha: 1) buttonColor = Color(hex: 0x76D7EA, alpha: 1) backgroundColor = Color(hex: 0x008CB4, alpha: 1) - plainColorSrcEditor = Color(hex: 0xBBD2D1, alpha: 1) numberColorSrcEditor = Color(hex: 0xC1D82F, alpha: 1) // FIND A NEW COLOR stringColorSrcEditor = Color(hex: 0xC1D82F, alpha: 1) @@ -873,13 +578,9 @@ public class SettingsViewModel: ObservableObject { lineNumbersColorSrcEditor = Color(hex: 0xC1D82F, alpha: 1) // FIND A NEW COLOR case .hacker: - -// terminalBackgroundColor = Color(hex: 0x000000, alpha: 1) -// terminalTextColor = Color(hex: 0x39FF14, alpha: 1) appTextColor = Color(hex: 0xF5FFFA, alpha: 1) // FIND A NEW COLOR buttonColor = Color(hex: 0x5F7D8E, alpha: 1) backgroundColor = Color(hex: 0x2C3539, alpha: 1) - plainColorSrcEditor = Color(hex: 0xF5F5F5, alpha: 1) numberColorSrcEditor = Color(hex: 0xC1D82F, alpha: 1) stringColorSrcEditor = Color(hex: 0xCCFF00, alpha: 1) // FIND A NEW COLOR @@ -893,16 +594,15 @@ public class SettingsViewModel: ObservableObject { #endif } } + +// END THEME ZONE + enum AppTheme { case deepSpace case hacker } - -// END THEME ZONE - - // CEREPROC VOICE ZONE -// Mac OS Cereproc voices for Sw-S: cmd line voices - not streamed to device. SwiftSageiOS acts as remote for this if you have your headphones hooked up to your mac and +// Mac OS Cereproc voices for LogicSage for Mac cmd line voices - not streamed to device. SwiftSageiOS acts as remote for this if you have your headphones hooked up to your mac and // are using muliple iOS devices for screens, etc. let cereprocVoicesNames = [ "Heather", diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/TopBar.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/TopBar.swift index 8f4e169..437ae1d 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/TopBar.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/TopBar.swift @@ -35,6 +35,7 @@ struct TopBar: View { .minimumScaleFactor(0.75) } .foregroundColor(SettingsViewModel.shared.buttonColor) + .padding(.leading, 7) // IF Debugger is running.... if settingsViewModel.isDebugging { @@ -46,6 +47,8 @@ struct TopBar: View { .minimumScaleFactor(0.75) } .foregroundColor(SettingsViewModel.shared.buttonColor) + .padding(.leading, 7) + } Button(action: { @@ -56,6 +59,7 @@ struct TopBar: View { .minimumScaleFactor(0.75) } .foregroundColor(SettingsViewModel.shared.buttonColor) + .padding(.leading, 7) } Text(getName()) @@ -90,8 +94,18 @@ struct TopBar: View { } let convoText = settingsViewModel.convoText(settingsViewModel.conversations, window: windowInfo) - ShareLink(item: "\(SettingsViewModel.link.absoluteString)\n\(convoText)", message: Text("LogicSage convo")) + ShareLink(item: "Check out LogicSage: the mobile AI workspace on the AppStore for free now: \(appLink.absoluteString)\nHere is the chat I had with my GPT.\n\(convoText)", message: Text("LogicSage convo")) .foregroundColor(SettingsViewModel.shared.buttonColor) + +// TODO RENAME FROM HERE IF IT WASNT SO COMPLICAGTED +// Button { +// +// } label: { +// Label("Rename", systemImage: "rectangle.and.pencil.and.ellipsis") +// .font(.body) +// .labelStyle(DemoStyle()) +// .foregroundColor(SettingsViewModel.shared.buttonColor) +// } } label: { Label("", systemImage: "ellipsis") .font(.body) diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WindowView.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WindowView.swift index a45b86c..40b92e3 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WindowView.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WindowView.swift @@ -7,14 +7,17 @@ import Foundation import SwiftUI -#if !os(macOS) -// Placeholder replaced by geometry reader. var defSize = CGRect(x: 0, y: 0, width: 300, height: 300) var defChatSize = CGRect(x: 0, y: 0, width: 300, height: 300) + +#if !os(macOS) + +// Placeholder replaced by geometry reader. + struct WindowView: View { - var window: WindowInfo + var window: WindowInfo @State private var position: CGSize = .zero @State var frame: CGRect @@ -33,6 +36,7 @@ struct WindowView: View { @Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.verticalSizeClass) var verticalSizeClass + let url = URL(string:SettingsViewModel.shared.defaultURL) var body: some View { GeometryReader { geometry in @@ -46,7 +50,6 @@ struct WindowView: View { .shadow(color:settingsViewModel.appTextColor, radius: 3) .frame(width: window.frame.width, height: window.frame.height) .edgesIgnoringSafeArea(.bottom) - } .offset(position) .onAppear() { @@ -72,68 +75,37 @@ struct WindowView: View { private func recalculateWindowSize(size: CGSize) { if !isResizeGestureActive { - viewSize = CGRect(x: 0, y: 0, width: size.width, height: size.height) } } private func windowContent() -> some View { - let url = URL(string:settingsViewModel.defaultURL) - - switch window.windowType { - case .webView: - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .webView, windowManager: windowManager, window: window, sageMultiViewModel: viewModel, frame: $frame, position: $position, webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .file: - - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .editor, windowManager: windowManager, window: window, sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .chat: - - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .chat, windowManager: windowManager, window: window, sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .simulator: - - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .simulator, windowManager: windowManager, window: window, sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .repoTreeView: - - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .repoTreeView, windowManager: windowManager, window: window,sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .windowListView: - - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .windowListView, windowManager: windowManager, window: window,sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .changeView: - - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .changeView, windowManager: windowManager, window: window,sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .workingChangesView: - - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .workingChangesView, windowManager: windowManager, window: window,sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - case .project: + return AnyView( + SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: windowTypeToViewMode(windowType: window.windowType), windowManager: windowManager, window: window, sageMultiViewModel: viewModel, frame: $frame, position: $position, webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) + .frame(maxWidth: .infinity, maxHeight: .infinity) + ) + } +} - return AnyView( - SageMultiView(showAddView: $showAddView, settingsViewModel: settingsViewModel, viewMode: .project, windowManager: windowManager, window: window, sageMultiViewModel: viewModel, frame: $frame, position: $position,webViewURL: url, viewSize: $parentViewSize, resizeOffset: $resizeOffset, bumping: $bumping, isResizeGestureActive: $isResizeGestureActive) - .frame(maxWidth: .infinity, maxHeight: .infinity) - ) - } +func windowTypeToViewMode(windowType: WindowInfo.WindowType) -> ViewMode { + switch windowType { + case .webView: + return .webView + case .file: + return .editor + case .chat: + return .chat + case .simulator: + return .simulator + case .repoTreeView: + return .repoTreeView + case .windowListView: + return .windowListView + case .changeView: + return .changeView + case .workingChangesView: + return .workingChangesView + case .project: + return .project } } #endif diff --git a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WorkingChangesList.swift b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WorkingChangesList.swift index 3a77f2d..944b119 100644 --- a/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WorkingChangesList.swift +++ b/SwiftSageiOS/SwiftSageiOS/ViewViewModel/WorkingChangesList.swift @@ -8,14 +8,11 @@ import Foundation import SwiftUI #if !os(macOS) - struct FileChange: Identifiable, Equatable { - var id = UUID() var fileURL: URL var status: String var lineChanges: [ChangeRow] - var newFileContents: String } let unstagedTitle = "Unstaged Changes" diff --git a/Swifty-GPT/Config.swift b/Swifty-GPT/Config.swift index a35b2bc..c8ad707 100644 --- a/Swifty-GPT/Config.swift +++ b/Swifty-GPT/Config.swift @@ -229,6 +229,6 @@ enum LogVerbosity { // ASCII MOVIES ZONE /////////////////////////////// // IF the movie is TOO big/blurry for you and your tastes try manually setting your sws/Xcode console font to 2 or 3 and then turn movie width up :). // We'll need to sync each clients terminal window/s widths to make sure the animations and movies play properly. -let movieWidth = 100 +let movieWidth = 175 let matrixScreenWidth = 100 // END ASCII MOVIES ZONE /////////////////////////////// diff --git a/Swifty-GPT/SwiftSageStatusBar b/Swifty-GPT/SwiftSageStatusBar index bdb3005..589e4f2 100755 Binary files a/Swifty-GPT/SwiftSageStatusBar and b/Swifty-GPT/SwiftSageStatusBar differ diff --git a/Swifty-GPT/SwiftSageStatusBar.dSYM/Contents/Resources/DWARF/SwiftSageStatusBar b/Swifty-GPT/SwiftSageStatusBar.dSYM/Contents/Resources/DWARF/SwiftSageStatusBar index 73f0be4..edf6c6f 100644 Binary files a/Swifty-GPT/SwiftSageStatusBar.dSYM/Contents/Resources/DWARF/SwiftSageStatusBar and b/Swifty-GPT/SwiftSageStatusBar.dSYM/Contents/Resources/DWARF/SwiftSageStatusBar differ diff --git a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo index 968aac5..74b2e0e 100644 Binary files a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo and b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo differ diff --git a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/x86_64-apple-macos.swiftsourceinfo b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/x86_64-apple-macos.swiftsourceinfo index 90dd9ae..caa6249 100644 Binary files a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/x86_64-apple-macos.swiftsourceinfo and b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/Project/x86_64-apple-macos.swiftsourceinfo differ diff --git a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/arm64-apple-macos.swiftmodule b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/arm64-apple-macos.swiftmodule index 8e7ef99..7bbb012 100644 Binary files a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/arm64-apple-macos.swiftmodule and b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/arm64-apple-macos.swiftmodule differ diff --git a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/x86_64-apple-macos.swiftmodule b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/x86_64-apple-macos.swiftmodule index feaf8ec..bc08305 100644 Binary files a/Swifty-GPT/SwiftSageStatusBar.swiftmodule/x86_64-apple-macos.swiftmodule and b/Swifty-GPT/SwiftSageStatusBar.swiftmodule/x86_64-apple-macos.swiftmodule differ