mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 16:30:27 +00:00
Replace simple API provider selection with comprehensive model dropdown organized by provider (Claude, OpenAI, Gemini). Update settings schema from `apiProvider` to `model`, add AIProvider.fromModel() helper, simplify provider URLs, and fix indentation inconsistencies throughout codebase.
177 lines
No EOL
5.4 KiB
TypeScript
177 lines
No EOL
5.4 KiB
TypeScript
import { AIProviderModel } from "Enums/ApiProvider";
|
|
import { Copy } from "Enums/Copy";
|
|
import { Path } from "Enums/Path";
|
|
import { Selector } from "Enums/Selector";
|
|
import type AIAgentPlugin from "main";
|
|
import { PluginSettingTab, Setting, App, setIcon, setTooltip } from "obsidian";
|
|
|
|
export class AIAgentSettingTab extends PluginSettingTab {
|
|
plugin: AIAgentPlugin;
|
|
apiKeySetting: Setting | null = null;
|
|
|
|
constructor(app: App, plugin: AIAgentPlugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
|
|
display(): void {
|
|
const { containerEl } = this;
|
|
|
|
containerEl.empty();
|
|
|
|
/* Model Selection Setting */
|
|
new Setting(containerEl)
|
|
.setName("Model")
|
|
.setDesc("Select the AI model to use.")
|
|
.addDropdown((dropdown) => {
|
|
const select = dropdown.selectEl;
|
|
|
|
// Claude models group
|
|
const claudeGroup = select.createEl("optgroup", { attr: { label: "Claude" } });
|
|
claudeGroup.createEl("option", {
|
|
value: AIProviderModel.ClaudeSonnet_4_5,
|
|
text: Copy.ClaudeSonnet_4_5
|
|
});
|
|
claudeGroup.createEl("option", {
|
|
value: AIProviderModel.ClaudeSonnet_4,
|
|
text: Copy.ClaudeSonnet_4
|
|
});
|
|
claudeGroup.createEl("option", {
|
|
value: AIProviderModel.ClaudeSonnet_3_7,
|
|
text: Copy.ClaudeSonnet_3_7
|
|
});
|
|
claudeGroup.createEl("option", {
|
|
value: AIProviderModel.ClaudeOpus_4_1,
|
|
text: Copy.ClaudeOpus_4_1
|
|
});
|
|
claudeGroup.createEl("option", {
|
|
value: AIProviderModel.ClaudeOpus_4,
|
|
text: Copy.ClaudeOpus_4
|
|
});
|
|
claudeGroup.createEl("option", {
|
|
value: AIProviderModel.ClaudeHaiku_4_5,
|
|
text: Copy.ClaudeHaiku_4_5
|
|
});
|
|
|
|
// OpenAI models group
|
|
const openaiGroup = select.createEl("optgroup", { attr: { label: "OpenAI" } });
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_5,
|
|
text: Copy.GPT_5
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_5_Mini,
|
|
text: Copy.GPT_5_Mini
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_5_Nano,
|
|
text: Copy.GPT_5_Nano
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_5_Pro,
|
|
text: Copy.GPT_5_Pro
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_4o,
|
|
text: Copy.GPT_4o
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_4o_Mini,
|
|
text: Copy.GPT_4o_Mini
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_4_1,
|
|
text: Copy.GPT_4_1
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_4_1_Mini,
|
|
text: Copy.GPT_4_1_Mini
|
|
});
|
|
openaiGroup.createEl("option", {
|
|
value: AIProviderModel.GPT_4_1_Nano,
|
|
text: Copy.GPT_4_1_Nano
|
|
});
|
|
|
|
// Gemini models group
|
|
const geminiGroup = select.createEl("optgroup", { attr: { label: "Gemini" } });
|
|
geminiGroup.createEl("option", {
|
|
value: AIProviderModel.GeminiFlash_2_5_Lite,
|
|
text: Copy.GeminiFlash_2_5_Lite
|
|
});
|
|
geminiGroup.createEl("option", {
|
|
value: AIProviderModel.GeminiFlash_2_5,
|
|
text: Copy.GeminiFlash_2_5
|
|
});
|
|
geminiGroup.createEl("option", {
|
|
value: AIProviderModel.GeminiPro_2_5,
|
|
text: Copy.GeminiPro_2_5
|
|
});
|
|
|
|
dropdown.setValue(this.plugin.settings.model);
|
|
dropdown.onChange(async (value) => {
|
|
this.plugin.settings.model = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
|
|
/* API Key Setting */
|
|
let apiKeyInputEl: HTMLInputElement;
|
|
this.apiKeySetting = new Setting(containerEl)
|
|
.setName("API Key")
|
|
.setDesc("Enter your API key here.")
|
|
.addText(text => {
|
|
text.setPlaceholder("Enter your API key")
|
|
.setValue(this.plugin.settings.apiKey)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.apiKey = value;
|
|
await this.plugin.saveSettings();
|
|
this.highlightApiKey();
|
|
});
|
|
text.inputEl.type = "password";
|
|
apiKeyInputEl = text.inputEl;
|
|
})
|
|
.addExtraButton(button => {
|
|
button
|
|
.setTooltip("Show API Key")
|
|
.onClick(() => {
|
|
if (apiKeyInputEl.type === "password") {
|
|
apiKeyInputEl.type = "text";
|
|
setIcon(button.extraSettingsEl, "eye-off");
|
|
setTooltip(button.extraSettingsEl, "Hide API Key");
|
|
} else {
|
|
apiKeyInputEl.type = "password";
|
|
setIcon(button.extraSettingsEl, "eye");
|
|
setTooltip(button.extraSettingsEl, "Show API Key");
|
|
}
|
|
});
|
|
setIcon(button.extraSettingsEl, "eye");
|
|
});
|
|
this.highlightApiKey();
|
|
|
|
/* Exclusions Setting */
|
|
new Setting(containerEl)
|
|
.setName("AI File Exclusions")
|
|
.setDesc("Set which directories and files the AI should ignore. Enter one path per line - supports glob patterns like folder/**, *.md")
|
|
.addTextArea(text => {
|
|
text.setPlaceholder(`Examples:\n\n${Path.UserInstruction}\n${Path.Conversations}/*.json\nPrivateNotes/**`)
|
|
.setValue(this.plugin.settings.exclusions.join("\n"))
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.exclusions = value.split("\n").map(line => line.trim()).filter(line => line.length > 0);
|
|
await this.plugin.saveSettings();
|
|
});
|
|
text.inputEl.classList.add(Selector.AIExclusionsInput);
|
|
});
|
|
}
|
|
|
|
private highlightApiKey(): void {
|
|
if (this.apiKeySetting) {
|
|
if (this.plugin.settings.apiKey.trim() === "") {
|
|
this.apiKeySetting.settingEl.removeClass(Selector.ApiKeySettingOk);
|
|
this.apiKeySetting.settingEl.addClass(Selector.ApiKeySettingError);
|
|
} else {
|
|
this.apiKeySetting.settingEl.removeClass(Selector.ApiKeySettingError);
|
|
this.apiKeySetting.settingEl.addClass(Selector.ApiKeySettingOk);
|
|
}
|
|
}
|
|
}
|
|
} |