Files
dbx/src/stores/settingsStore.ts
T
t8y2 41bceffc0e feat: add web deployment with axum server, Docker support, and browser UI
Introduce src-web (axum HTTP server) exposing all 43 database operations
as REST APIs with SSE streaming for AI, transfers, and SQL file execution.
Frontend adapts automatically between Tauri and HTTP backends via api.ts.
Includes password authentication, Docker packaging, and login page.
2026-05-05 03:16:50 +08:00

145 lines
5.1 KiB
TypeScript

import { defineStore } from "pinia";
import { ref } from "vue";
import * as api from "@/lib/api";
export type AiProvider = "claude" | "openai" | "custom";
export type AiApiStyle = "completions" | "responses";
export interface AiConfig {
provider: AiProvider;
apiKey: string;
endpoint: string;
model: string;
apiStyle: AiApiStyle;
}
const defaultConfigs: Record<AiProvider, Omit<AiConfig, "apiKey">> = {
claude: { provider: "claude", endpoint: "https://api.anthropic.com/v1/messages", model: "claude-sonnet-4-20250514", apiStyle: "completions" },
openai: { provider: "openai", endpoint: "https://api.openai.com/v1/chat/completions", model: "gpt-4o", apiStyle: "completions" },
custom: { provider: "custom", endpoint: "", model: "", apiStyle: "completions" },
};
export type EditorTheme = "one-dark" | "vscode-dark" | "vscode-light" | "nord" | "okaidia" | "material" | "duotone-light" | "duotone-dark" | "xcode";
export interface EditorSettings {
fontFamily: string;
fontSize: number;
theme: EditorTheme;
executeMode: "all" | "current";
}
export const EDITOR_THEMES: { value: EditorTheme; label: string; dark: boolean }[] = [
{ value: "one-dark", label: "One Dark", dark: true },
{ value: "vscode-dark", label: "VS Dark+", dark: true },
{ value: "vscode-light", label: "VS Light+", dark: false },
{ value: "nord", label: "Nord", dark: true },
{ value: "okaidia", label: "Okaidia", dark: true },
{ value: "material", label: "Material", dark: true },
{ value: "duotone-light", label: "Duotone Light", dark: false },
{ value: "duotone-dark", label: "Duotone Dark", dark: true },
{ value: "xcode", label: "Xcode", dark: false },
];
export const FONT_FAMILIES: { value: string; label: string }[] = [
{ value: "'JetBrains Mono', 'Fira Code', monospace", label: "JetBrains Mono" },
{ value: "'Fira Code', monospace", label: "Fira Code" },
{ value: "'Cascadia Code', monospace", label: "Cascadia Code" },
{ value: "'Source Code Pro', monospace", label: "Source Code Pro" },
{ value: "'SF Mono', 'Menlo', monospace", label: "SF Mono / Menlo" },
{ value: "'Consolas', 'Courier New', monospace", label: "Consolas" },
{ value: "monospace", label: "System Monospace" },
];
export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
fontSize: 13,
theme: "one-dark",
executeMode: "all",
};
export const STORAGE_KEY = "dbx-editor-settings";
const OLD_FONT_SIZE_KEY = "dbx-query-editor-font-size";
function loadEditorSettings(): EditorSettings {
// Try new format first
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) {
const parsed = JSON.parse(raw) as Partial<EditorSettings>;
return {
fontFamily: parsed.fontFamily ?? DEFAULT_EDITOR_SETTINGS.fontFamily,
fontSize: parsed.fontSize ?? DEFAULT_EDITOR_SETTINGS.fontSize,
theme: parsed.theme ?? DEFAULT_EDITOR_SETTINGS.theme,
executeMode: parsed.executeMode ?? DEFAULT_EDITOR_SETTINGS.executeMode,
};
}
} catch { /* ignore */ }
// Migrate old font-size key if new settings don't exist
try {
const oldSize = localStorage.getItem(OLD_FONT_SIZE_KEY);
if (oldSize) {
const parsed = parseInt(oldSize, 10);
if (!isNaN(parsed)) {
const migrated: EditorSettings = {
...DEFAULT_EDITOR_SETTINGS,
fontSize: parsed,
};
saveEditorSettings(migrated);
localStorage.removeItem(OLD_FONT_SIZE_KEY);
return migrated;
}
}
} catch { /* ignore */ }
return { ...DEFAULT_EDITOR_SETTINGS };
}
function saveEditorSettings(settings: EditorSettings) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
}
export const useSettingsStore = defineStore("settings", () => {
const aiConfig = ref<AiConfig>({ ...defaultConfigs.claude, apiKey: "", apiStyle: "completions" });
const isAiConfigLoaded = ref(false);
const editorSettings = ref<EditorSettings>(loadEditorSettings());
async function initAiConfig() {
if (isAiConfigLoaded.value) return;
const legacy = localStorage.getItem("dbx-ai-config");
const saved = await api.loadAiConfig().catch(() => null);
if (saved) {
aiConfig.value = saved;
} else if (legacy) {
aiConfig.value = JSON.parse(legacy);
await api.saveAiConfig(aiConfig.value).catch(() => {});
localStorage.removeItem("dbx-ai-config");
}
isAiConfigLoaded.value = true;
}
function updateAiConfig(config: Partial<AiConfig>) {
const previousProvider = aiConfig.value.provider;
if (config.provider && config.provider !== previousProvider) {
Object.assign(aiConfig.value, defaultConfigs[config.provider]);
}
Object.assign(aiConfig.value, config);
api.saveAiConfig(aiConfig.value).catch(() => {});
}
function isConfigured(): boolean {
return !!aiConfig.value.apiKey && !!aiConfig.value.endpoint;
}
function updateEditorSettings(partial: Partial<EditorSettings>) {
Object.assign(editorSettings.value, partial);
saveEditorSettings(editorSettings.value);
}
return {
aiConfig, isAiConfigLoaded, initAiConfig, updateAiConfig, isConfigured,
editorSettings, updateEditorSettings,
};
});