working through the plan + UI/ UX

This commit is contained in:
itsamejms
2026-07-12 22:13:43 +01:00
parent 5b256242be
commit a24f3615e0
38 changed files with 5295 additions and 328 deletions
+66 -27
View File
@@ -1,5 +1,6 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { invoke } from "@tauri-apps/api/core";
import { useToast } from "./Toast";
interface LlmConfig {
api_url: string;
@@ -8,21 +9,29 @@ interface LlmConfig {
temperature: number;
max_tokens: number;
top_p: number;
image_model: string;
embed_model: string;
}
export function SettingsPanel() {
const [config, setConfig] = useState<LlmConfig | null>(null);
const [loading, setLoading] = useState(false);
const [saved, setSaved] = useState(false);
const [showKey, setShowKey] = useState(false);
const [error, setError] = useState("");
const { addToast } = useToast();
async function loadConfig() {
try {
const c = await invoke<LlmConfig>("get_llm_config");
setConfig(c);
} catch (e) {
console.error("Failed to load config:", e);
}
}
// ponytail: auto-load on mount so users don't see a gate before the form.
useEffect(() => {
(async () => {
try {
const c = await invoke<LlmConfig>("get_llm_config");
setConfig(c);
} catch (e) {
setError(String(e));
}
})();
}, []);
async function saveConfig() {
if (!config) return;
@@ -31,24 +40,20 @@ export function SettingsPanel() {
await invoke("set_llm_config", { config });
setSaved(true);
setTimeout(() => setSaved(false), 2000);
addToast("Settings saved", "success");
} catch (e) {
console.error("Failed to save config:", e);
addToast(`Save failed: ${e}`, "error");
}
setLoading(false);
}
if (!config) {
return (
<div className="flex flex-col items-center justify-center h-full gap-4">
<p className="text-[var(--color-text-secondary)] text-sm">
Configure your LLM connection
</p>
<button
onClick={loadConfig}
className="rounded-lg bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] px-4 py-2 text-sm font-semibold hover:bg-[var(--color-gold-muted)] transition-colors cursor-pointer"
>
Load Settings
</button>
<div className="flex flex-col items-center justify-center h-full gap-3">
<p className="text-[var(--color-text-secondary)] text-sm">Loading settings…</p>
{error && (
<p className="text-[var(--color-danger)] text-xs">Failed to load: {error}</p>
)}
</div>
);
}
@@ -71,13 +76,23 @@ export function SettingsPanel() {
<label className="text-[var(--color-text-secondary)] text-xs font-medium">
API Key (optional)
</label>
<input
className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-3 py-2 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)]"
type="password"
value={config.api_key}
onChange={(e) => setConfig({ ...config, api_key: e.target.value })}
placeholder="sk-... (leave blank for local)"
/>
<div className="flex gap-1.5">
<input
className="flex-1 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-3 py-2 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)]"
type={showKey ? "text" : "password"}
value={config.api_key}
onChange={(e) => setConfig({ ...config, api_key: e.target.value })}
placeholder="sk-... (leave blank for local)"
/>
<button
type="button"
onClick={() => setShowKey((s) => !s)}
className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-2 text-[var(--color-text-dim)] hover:text-[var(--color-gold-bright)] hover:border-[var(--color-gold-bright)] transition-colors text-xs cursor-pointer"
title={showKey ? "Hide" : "Show"}
>
{showKey ? "Hide" : "Show"}
</button>
</div>
</div>
<div className="flex flex-col gap-1">
@@ -92,6 +107,30 @@ export function SettingsPanel() {
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-[var(--color-text-secondary)] text-xs font-medium">
Image model
</label>
<input
className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-3 py-2 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)]"
value={config.image_model}
onChange={(e) => setConfig({ ...config, image_model: e.target.value })}
placeholder="x/flux2-klein:4b"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-[var(--color-text-secondary)] text-xs font-medium">
Embedding model (Lore RAG)
</label>
<input
className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-3 py-2 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)]"
value={config.embed_model}
onChange={(e) => setConfig({ ...config, embed_model: e.target.value })}
placeholder="nomic-embed-text"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="flex flex-col gap-1">
<label className="text-[var(--color-text-secondary)] text-xs font-medium">