Initial commit: Nat20 Notes — TTRPG session transcription & summarization
This commit is contained in:
68
frontend/src/pages/Settings.tsx
Normal file
68
frontend/src/pages/Settings.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { api } from "../api";
|
||||
|
||||
export default function Settings() {
|
||||
const [settings, setSettings] = useState<any>(null);
|
||||
const [saved, setSaved] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
api.getSettings().then(setSettings);
|
||||
}, []);
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
const set = (k: string, v: any) => setSettings({ ...settings, [k]: v });
|
||||
|
||||
const save = async () => {
|
||||
await api.updateSettings(settings);
|
||||
setSaved(true);
|
||||
setTimeout(() => setSaved(false), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-xl mx-auto py-16 px-4">
|
||||
<Link to="/" className="text-sm text-ink/40 hover:text-brass">← Back</Link>
|
||||
<h1 className="font-display text-3xl mt-4 mb-8">Settings</h1>
|
||||
|
||||
<div className="card p-6 space-y-4 mb-6">
|
||||
<h2 className="eyebrow">Transcription</h2>
|
||||
<select className="input w-full" value={settings.whisper_model} onChange={(e) => set("whisper_model", e.target.value)}>
|
||||
<option value="tiny">Tiny</option>
|
||||
<option value="small">Small</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="large-v3">Large v3</option>
|
||||
</select>
|
||||
<input className="input w-full" type="password" placeholder="HuggingFace token" value={settings.hf_token} onChange={(e) => set("hf_token", e.target.value)} />
|
||||
</div>
|
||||
|
||||
<div className="card p-6 space-y-4 mb-6">
|
||||
<h2 className="eyebrow">Summarization</h2>
|
||||
<div className="flex gap-3">
|
||||
<button className={settings.llm_mode === "ollama" ? "btn-primary" : "btn-secondary"} onClick={() => set("llm_mode", "ollama")}>Local (Ollama)</button>
|
||||
<button className={settings.llm_mode === "api" ? "btn-primary" : "btn-secondary"} onClick={() => set("llm_mode", "api")}>Hosted API</button>
|
||||
</div>
|
||||
{settings.llm_mode === "ollama" ? (
|
||||
<>
|
||||
<input className="input w-full" value={settings.ollama_host} onChange={(e) => set("ollama_host", e.target.value)} />
|
||||
<input className="input w-full" value={settings.ollama_model} onChange={(e) => set("ollama_model", e.target.value)} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<input className="input w-full" value={settings.api_base_url} onChange={(e) => set("api_base_url", e.target.value)} />
|
||||
<input className="input w-full" type="password" value={settings.api_key} onChange={(e) => set("api_key", e.target.value)} />
|
||||
<input className="input w-full" value={settings.api_model} onChange={(e) => set("api_model", e.target.value)} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="card p-6 space-y-3 mb-6">
|
||||
<h2 className="eyebrow">Campaign context</h2>
|
||||
<textarea className="input w-full h-32" value={settings.world_context} onChange={(e) => set("world_context", e.target.value)} placeholder="Paste world context directly..." />
|
||||
<input className="input w-full" value={settings.world_context_path} onChange={(e) => set("world_context_path", e.target.value)} placeholder="...or path to a file inside the container" />
|
||||
</div>
|
||||
|
||||
<button className="btn-primary" onClick={save}>{saved ? "Saved" : "Save settings"}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user