From 580e798e16346eb894eb899edac82d2efdf40adc Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Sat, 18 Apr 2026 22:32:53 -0500 Subject: initial commit --- src/components/SettingsPanel.tsx | 158 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/components/SettingsPanel.tsx (limited to 'src/components/SettingsPanel.tsx') diff --git a/src/components/SettingsPanel.tsx b/src/components/SettingsPanel.tsx new file mode 100644 index 0000000..bb44c74 --- /dev/null +++ b/src/components/SettingsPanel.tsx @@ -0,0 +1,158 @@ +import { useState, useEffect } from 'react'; +import type { OllamaSettings } from '../hooks/useSettings'; +import { testOllamaConnection } from '../engine/aiGenerator'; + +interface Props { + settings: OllamaSettings; + onChange: (next: Partial) => void; + onClose: () => void; +} + +type TestState = + | { status: 'idle' } + | { status: 'testing' } + | { status: 'ok'; models: string[] } + | { status: 'error'; message: string }; + +export function SettingsPanel({ settings, onChange, onClose }: Props) { + const [url, setUrl] = useState(settings.baseUrl); + const [model, setModel] = useState(settings.model); + const [test, setTest] = useState({ status: 'idle' }); + + // Commit field on blur/enter + const commitUrl = () => onChange({ baseUrl: url }); + const commitModel = (m: string) => { setModel(m); onChange({ model: m }); }; + + const runTest = async () => { + onChange({ baseUrl: url }); // save current URL first + setTest({ status: 'testing' }); + try { + const models = await testOllamaConnection(url); + setTest({ status: 'ok', models }); + } catch (err) { + setTest({ status: 'error', message: (err as Error).message }); + } + }; + + // Close on Escape + useEffect(() => { + const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; + window.addEventListener('keydown', handler); + return () => window.removeEventListener('keydown', handler); + }, [onClose]); + + return ( +
+
e.stopPropagation()}> +
+ Settings + +
+ +
+ {/* AI toggle */} +
+
AI Generation
+ +
+ + {/* Ollama config */} + {settings.enabled && ( +
+
Ollama
+ +
+ +
+ setUrl(e.target.value)} + onBlur={commitUrl} + onKeyDown={e => e.key === 'Enter' && commitUrl()} + placeholder="http://localhost:11434" + spellCheck={false} + /> + +
+ + {test.status === 'ok' && ( +
+ Connected · {test.models.length} model{test.models.length !== 1 ? 's' : ''} available +
+ )} + {test.status === 'error' && ( +
{test.message}
+ )} +
+ +
+ + setModel(e.target.value)} + onBlur={() => commitModel(model)} + onKeyDown={e => e.key === 'Enter' && commitModel(model)} + placeholder="llama3.2" + spellCheck={false} + /> + {test.status === 'ok' && test.models.length > 0 && ( +
+ {test.models.map(m => ( + + ))} +
+ )} +
+ Pull a model first: ollama pull llama3.2 +
+
+
+ )} + + {/* Help */} +
+
About Ollama
+

+ Ollama runs AI models locally on your machine — no API key, no data sent externally. + Install from ollama.com, then pull any model to get started. +

+

+ If connection fails, ensure Ollama is running and CORS is open:
+ OLLAMA_ORIGINS=* ollama serve +

+
+
+
+
+ ); +} -- cgit v1.2.3