diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 5c73fb7..e89ef87 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -272,6 +272,7 @@ } }, "Choose different folder" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -282,6 +283,7 @@ } }, "Choose folder" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -485,6 +487,7 @@ } }, "Directory:" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -777,6 +780,7 @@ } }, "https://huggingface.co/TheBloke/MythoMax-L2-Kimiko-v2-13B-GGUF/blob/main/mythomax-l2-kimiko-v2-13b.Q4_K_M.gguf" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -797,6 +801,7 @@ } }, "In incognito mode, TypeaheadAI works without connecting to the Internet, and your copy-paste history is 100% private. We will make this more user-friendly, but you can choose which model you want to run below." : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -999,6 +1004,7 @@ } }, "Online" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -1019,6 +1025,7 @@ } }, "Open in Finder" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -1404,6 +1411,7 @@ } }, "Something went wrong..." : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -1422,6 +1430,9 @@ } } } + }, + "Sorry, this is unsupported right now. Will circle back on this later." : { + }, "Space" : { "localizations" : { @@ -1551,6 +1562,7 @@ } }, "The model failed to load." : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -1581,6 +1593,7 @@ } }, "This is still a work in progress. Need to use a more advanced sampling method, since it's currently just doing a greedy search. Will integrate with HuggingFace in a future version, but for now, try downloading this to your model directory:" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { diff --git a/TypeaheadAI/Actors/SpecialCopyActor.swift b/TypeaheadAI/Actors/SpecialCopyActor.swift index a234a6d..d757cc1 100644 --- a/TypeaheadAI/Actors/SpecialCopyActor.swift +++ b/TypeaheadAI/Actors/SpecialCopyActor.swift @@ -77,8 +77,9 @@ actor SpecialCopyActor: CanSimulateCopy, CanGetUIElements { // Serialize the UIElement if isAutopilotEnabled { let appContext = appInfo.appContext - async let (uiElement, elementMap) = getUIElements(appContext: appContext) + async let (uiElement, elementMap) = getUIElements(appContext: appContext, inFocus: true) if let serializedUIElement = await uiElement?.serialize() { + print(serializedUIElement) appInfo.appContext?.serializedUIElement = serializedUIElement appInfo.elementMap = await elementMap } diff --git a/TypeaheadAI/Actors/SpecialOpenActor.swift b/TypeaheadAI/Actors/SpecialOpenActor.swift index 978fed8..9941370 100644 --- a/TypeaheadAI/Actors/SpecialOpenActor.swift +++ b/TypeaheadAI/Actors/SpecialOpenActor.swift @@ -68,8 +68,9 @@ actor SpecialOpenActor: CanGetUIElements { // Serialize the UIElement if isAutopilotEnabled { let appContext = appInfo.appContext - async let (uiElement, elementMap) = getUIElements(appContext: appContext) + async let (uiElement, elementMap) = getUIElements(appContext: appContext, inFocus: true) if let serializedUIElement = await uiElement?.serialize() { + print(serializedUIElement) appInfo.appContext?.serializedUIElement = serializedUIElement appInfo.elementMap = await elementMap } diff --git a/TypeaheadAI/ClientManager.swift b/TypeaheadAI/ClientManager.swift index 69b9a1a..f02b685 100644 --- a/TypeaheadAI/ClientManager.swift +++ b/TypeaheadAI/ClientManager.swift @@ -221,6 +221,7 @@ class ClientManager: CanGetUIElements { if isAutopilotEnabled { let (uiElement, elementMap) = getUIElements(appContext: appInfo?.appContext) if let serializedUIElement = uiElement?.serialize() { + print(serializedUIElement) appInfo?.appContext?.serializedUIElement = serializedUIElement appInfo?.elementMap = elementMap } diff --git a/TypeaheadAI/Models/UIElement.swift b/TypeaheadAI/Models/UIElement.swift index 707edb3..2e830a8 100644 --- a/TypeaheadAI/Models/UIElement.swift +++ b/TypeaheadAI/Models/UIElement.swift @@ -135,7 +135,8 @@ extension UIElement { } if let value = self.value { - text += ", value: \(value)" + let escapedNewlines = value.replacingOccurrences(of: "\n", with: "\\n") + text += ", value: \(escapedNewlines)" } } diff --git a/TypeaheadAI/Traits/CanGetUIElements.swift b/TypeaheadAI/Traits/CanGetUIElements.swift index c729709..4a919f6 100644 --- a/TypeaheadAI/Traits/CanGetUIElements.swift +++ b/TypeaheadAI/Traits/CanGetUIElements.swift @@ -13,16 +13,20 @@ protocol CanGetUIElements { } extension CanGetUIElements { - func getUIElements(appContext: AppContext?) -> (UIElement?, ElementMap) { + func getUIElements(appContext: AppContext?, inFocus: Bool = false) -> (UIElement?, ElementMap) { var element: AXUIElement? = nil if let appContext = appContext, let pid = appContext.pid { element = AXUIElementCreateApplication(pid) - // Narrow down to the first (top-most) window - if let windowElement = element?.children().first(where: { + if inFocus, NSWorkspace.shared.isVoiceOverEnabled, let focusedElement = element?.getElementInFocus() { + element = focusedElement + } else if NSWorkspace.shared.isVoiceOverEnabled, let focusedWindow = element?.subelement(forAttribute: kAXFocusedWindowAttribute) { + element = focusedWindow + } else if let windowElement = element?.children().first(where: { $0.stringValue(forAttribute: kAXRoleAttribute) == "AXWindow" && !$0.children().isEmpty }) { + // Narrow down to the first (top-most) window element = windowElement } } else { diff --git a/TypeaheadAI/Views/Menu/MenuView.swift b/TypeaheadAI/Views/Menu/MenuView.swift index 6ac1e3f..cf23acc 100644 --- a/TypeaheadAI/Views/Menu/MenuView.swift +++ b/TypeaheadAI/Views/Menu/MenuView.swift @@ -41,25 +41,25 @@ struct MenuView: View { Spacer() - Toggle("Online", isOn: $modalManager.online) - .scaleEffect(0.8) - .onChange(of: modalManager.online) { online in - if let manager = modalManager.clientManager?.llamaModelManager, - !online, - let _ = selectedModelURL { - Task { - do { - try await manager.load() - } catch { - print(error.localizedDescription) - } - } - } - } - .foregroundColor(Color.secondary) - .toggleStyle(.switch) - .accentColor(.blue) - .padding(0) +// Toggle("Online", isOn: $modalManager.online) +// .scaleEffect(0.8) +// .onChange(of: modalManager.online) { online in +// if let manager = modalManager.clientManager?.llamaModelManager, +// !online, +// let _ = selectedModelURL { +// Task { +// do { +// try await manager.load() +// } catch { +// print(error.localizedDescription) +// } +// } +// } +// } +// .foregroundColor(Color.secondary) +// .toggleStyle(.switch) +// .accentColor(.blue) +// .padding(0) } .padding(.vertical, verticalPadding) .padding(.leading, horizontalPadding) diff --git a/TypeaheadAI/Views/Settings/GeneralSettingsView.swift b/TypeaheadAI/Views/Settings/GeneralSettingsView.swift index 0b58625..02fec22 100644 --- a/TypeaheadAI/Views/Settings/GeneralSettingsView.swift +++ b/TypeaheadAI/Views/Settings/GeneralSettingsView.swift @@ -17,7 +17,7 @@ struct GeneralSettingsView: View { @AppStorage("notifyOnUpdate") private var notifyOnUpdate: Bool = true @AppStorage("isWebSearchEnabled") private var isWebSearchEnabled: Bool = true @AppStorage("isAutopilotEnabled") private var isAutopilotEnabled: Bool = true - @AppStorage("isNarrateEnabled") private var isNarrateEnabled: Bool = false + @AppStorage("isNarrateEnabled") private var isNarrateEnabled: Bool = true var body: some View { VStack(alignment: .leading) { diff --git a/TypeaheadAI/Views/Settings/IncognitoModeView.swift b/TypeaheadAI/Views/Settings/IncognitoModeView.swift index 6d3933a..9181412 100644 --- a/TypeaheadAI/Views/Settings/IncognitoModeView.swift +++ b/TypeaheadAI/Views/Settings/IncognitoModeView.swift @@ -26,132 +26,10 @@ struct IncognitoModeView: View { Divider() - VStack(alignment: .leading) { - Text("This is still a work in progress. Need to use a more advanced sampling method, since it's currently just doing a greedy search. Will integrate with HuggingFace in a future version, but for now, try downloading this to your model directory:") - Link(destination: URL(string: "https://huggingface.co/TheBloke/MythoMax-L2-Kimiko-v2-13B-GGUF/blob/main/mythomax-l2-kimiko-v2-13b.Q4_K_M.gguf")!) { - Text("https://huggingface.co/TheBloke/MythoMax-L2-Kimiko-v2-13B-GGUF/blob/main/mythomax-l2-kimiko-v2-13b.Q4_K_M.gguf") - .foregroundColor(.accentColor) - .underline() - } - }.textSelection(.enabled) - - Divider() - - Text("In incognito mode, TypeaheadAI works without connecting to the Internet, and your copy-paste history is 100% private. We will make this more user-friendly, but you can choose which model you want to run below.") - .textSelection(.enabled) - - Spacer() - - HStack(spacing: 3) { - Text("Directory:").font(.headline) - Text(directoryURL?.relativePath ?? "Not configured") - } - .textSelection(.enabled) - - HStack { - Spacer() - - Button(directoryURL == nil ? "Choose folder" : "Choose different folder") { - isPickerPresented = true - } - .fileImporter( - isPresented: $isPickerPresented, - allowedContentTypes: [.folder], - allowsMultipleSelection: false - ) { result in - switch result { - case .success(let urls): - if let url = urls.first { - self.llamaModelManager.setModelDirectory(url) - Task { - do { - try await self.llamaModelManager.load() - } catch { - print(error.localizedDescription) - } - } - } - case .failure(let error): - print("Error selecting directory: \(error.localizedDescription)") - } - } - - Button("Open in Finder") { - guard let directoryURL = directoryURL else { - return - } - NSWorkspace.shared.activateFileViewerSelecting([directoryURL]) - } - .disabled(directoryURL == nil) - } - - List(llamaModelManager.modelFiles ?? [], id: \.self) { url in - Button(action: { - if selectedModelURL == url { - llamaModelManager.unloadModel() - } else { - Task { - do { - isLoading = true - currentlyLoadingModel = url - - try await llamaModelManager.loadModel(from: url) - - selectedModelURL = url - } catch { - showAlert = true - print(error.localizedDescription) - } - - isLoading = false - currentlyLoadingModel = nil - } - } - }) { - HStack { - if isLoading && currentlyLoadingModel == url { - ProgressView() - .progressViewStyle(CircularProgressViewStyle()) - .scaleEffect(0.5, anchor: .center) - .frame(width: 24, height: 24, alignment: .center) - } else { - Image(systemName: selectedModelURL == url ? "doc.circle.fill" : "doc.circle") - .font(.system(size: 24)) - .foregroundColor(selectedModelURL == url ? .accentColor : .primary) - .frame(width: 24, height: 24, alignment: .center) - } - - Text(url.lastPathComponent) - .fontWeight(.medium) - } - .padding(.vertical, 5) - .padding(.leading, 10) - .padding(.trailing, 15) - .background(Color.gray.opacity(0.1)) - .cornerRadius(8) - } - .buttonStyle(.plain) - .listRowSeparator(.hidden) - } - .disabled(isLoading) - } - .alert(isPresented: $showAlert) { - Alert(title: Text("Something went wrong..."), - message: Text("The model failed to load."), - dismissButton: .default(Text("OK"))) + Text("Sorry, this is unsupported right now. Will circle back on this later.") } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .padding(10) - .onAppear { - Task { - do { - try await self.llamaModelManager.load() - } catch { - showAlert = true - print(error.localizedDescription) - } - } - } } } diff --git a/TypeaheadAI/WindowManagers/ModalManager.swift b/TypeaheadAI/WindowManagers/ModalManager.swift index 1c7b340..ae186c5 100644 --- a/TypeaheadAI/WindowManagers/ModalManager.swift +++ b/TypeaheadAI/WindowManagers/ModalManager.swift @@ -31,7 +31,7 @@ class ModalManager: ObservableObject { @AppStorage("toastY") var toastY: Double? @AppStorage("toastWidth") var toastWidth: Double = 400.0 @AppStorage("toastHeight") var toastHeight: Double = 400.0 - @AppStorage("isNarrateEnabled") var isNarrateEnabled: Bool = false + @AppStorage("isNarrateEnabled") var isNarrateEnabled: Bool = true private let logger = Logger( subsystem: "ai.typeahead.TypeaheadAI",