Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Shift+Tab navigates to the previous cell in the data grid
- Copy rows writes TSV, HTML table, and plain text to the clipboard for richer paste in spreadsheet apps
- Row drag adds TSV and HTML representations alongside the internal drag type
- AI provider settings allow manually entering a model name when the provider does not return one

### Changed

Expand Down
51 changes: 29 additions & 22 deletions TablePro/Views/Settings/AIProviderDetailSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct AIProviderDetailSheet: View {
ToolbarItem(placement: .confirmationAction) {
Button(String(localized: "Save")) {
cancelTasks()
onSave(draft, apiKey)
onSave(normalizedDraft, apiKey)
}
.keyboardShortcut(.defaultAction)
.disabled(!isSaveEnabled)
Expand Down Expand Up @@ -107,6 +107,12 @@ struct AIProviderDetailSheet: View {
}
}

private var normalizedDraft: AIProviderConfig {
var provider = draft
provider.model = draft.model.trimmingCharacters(in: .whitespacesAndNewlines)
return provider
Comment thread
FaiChou marked this conversation as resolved.
}

// MARK: - Auth

@ViewBuilder
Expand Down Expand Up @@ -307,33 +313,34 @@ struct AIProviderDetailSheet: View {

@ViewBuilder
private var modelControl: some View {
if isFetchingModels {
ProgressView().controlSize(.small)
} else if fetchedModels.isEmpty {
HStack(spacing: 6) {
if !draft.model.isEmpty {
Text(draft.model)
.foregroundStyle(.secondary)
}
HStack(spacing: 8) {
TextField(String(localized: "Model name"), text: $draft.model)
.textFieldStyle(.roundedBorder)
.frame(width: 260)

if isFetchingModels {
ProgressView().controlSize(.small)
} else if fetchedModels.isEmpty {
Button(String(localized: "Reload")) {
fetchModels()
}
.buttonStyle(.borderless)
.controlSize(.small)
}
} else {
Picker("", selection: $draft.model) {
if draft.model.isEmpty {
Text(String(localized: "Select a model")).tag("")
}
ForEach(fetchedModels, id: \.self) { model in
Text(model).tag(model)
} else {
Menu {
ForEach(fetchedModels, id: \.self) { model in
Button(model) {
draft.model = model
}
}
} label: {
Image(systemName: "chevron.down.circle")
}
.menuStyle(.borderlessButton)
.help(String(localized: "Choose a fetched model"))
}
.labelsHidden()
.pickerStyle(.menu)
.fixedSize()
}
.fixedSize()
}

// MARK: - Advanced
Expand Down Expand Up @@ -436,7 +443,7 @@ struct AIProviderDetailSheet: View {
return
}

let provider = AIProviderFactory.createProvider(for: draft, apiKey: apiKey)
let provider = AIProviderFactory.createProvider(for: normalizedDraft, apiKey: apiKey)
isFetchingModels = true
modelFetchError = nil

Expand Down Expand Up @@ -465,7 +472,7 @@ struct AIProviderDetailSheet: View {
return
}

let provider = AIProviderFactory.createProvider(for: draft, apiKey: apiKey)
let provider = AIProviderFactory.createProvider(for: normalizedDraft, apiKey: apiKey)
isTesting = true
testResult = nil

Expand Down
2 changes: 1 addition & 1 deletion docs/features/ai-assistant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Open **Settings** (`Cmd+,`) > **AI**. The tab is modeled on Xcode's Intelligence

1. Click **Add Provider...** and pick a type (GitHub Copilot, Claude, OpenAI, Gemini, OpenRouter, Ollama, or a custom OpenAI-compatible endpoint).
2. Enter the API key, or run device-flow sign-in for Copilot.
3. Pick a model. The list fetches automatically; click **Reload** if needed.
3. Enter a model name, or pick one from the fetched list. Click **Reload** if needed.
4. Click **Test Connection**.

API keys are stored in the macOS Keychain. Ollama is detected at launch.
Expand Down
Loading