Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
}
},
"Choose different folder" : {
"extractionState" : "stale",
"localizations" : {
"it" : {
"stringUnit" : {
Expand All @@ -282,6 +283,7 @@
}
},
"Choose folder" : {
"extractionState" : "stale",
"localizations" : {
"it" : {
"stringUnit" : {
Expand Down Expand Up @@ -485,6 +487,7 @@
}
},
"Directory:" : {
"extractionState" : "stale",
"localizations" : {
"it" : {
"stringUnit" : {
Expand Down Expand Up @@ -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" : {
Expand All @@ -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" : {
Expand Down Expand Up @@ -999,6 +1004,7 @@
}
},
"Online" : {
"extractionState" : "stale",
"localizations" : {
"it" : {
"stringUnit" : {
Expand All @@ -1019,6 +1025,7 @@
}
},
"Open in Finder" : {
"extractionState" : "stale",
"localizations" : {
"it" : {
"stringUnit" : {
Expand Down Expand Up @@ -1404,6 +1411,7 @@
}
},
"Something went wrong..." : {
"extractionState" : "stale",
"localizations" : {
"it" : {
"stringUnit" : {
Expand All @@ -1422,6 +1430,9 @@
}
}
}
},
"Sorry, this is unsupported right now. Will circle back on this later." : {

},
"Space" : {
"localizations" : {
Expand Down Expand Up @@ -1551,6 +1562,7 @@
}
},
"The model failed to load." : {
"extractionState" : "stale",
"localizations" : {
"it" : {
"stringUnit" : {
Expand Down Expand Up @@ -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" : {
Expand Down
3 changes: 2 additions & 1 deletion TypeaheadAI/Actors/SpecialCopyActor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
3 changes: 2 additions & 1 deletion TypeaheadAI/Actors/SpecialOpenActor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions TypeaheadAI/ClientManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
3 changes: 2 additions & 1 deletion TypeaheadAI/Models/UIElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
}
}

Expand Down
10 changes: 7 additions & 3 deletions TypeaheadAI/Traits/CanGetUIElements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
38 changes: 19 additions & 19 deletions TypeaheadAI/Views/Menu/MenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion TypeaheadAI/Views/Settings/GeneralSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
124 changes: 1 addition & 123 deletions TypeaheadAI/Views/Settings/IncognitoModeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion TypeaheadAI/WindowManagers/ModalManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down