From 5a0f01221fa5e6bbda5b69fbd6c3d5c3769ef8e7 Mon Sep 17 00:00:00 2001 From: DBT Date: Wed, 25 Feb 2026 13:34:41 +0000 Subject: [PATCH] webui: add config form editor (structured fields + raw toggle) --- webui/src/App.tsx | 68 +++++++++++++++++++++++++++++++++++++++----- webui/src/styles.css | 6 +++- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/webui/src/App.tsx b/webui/src/App.tsx index 1d602bd..0f6fadc 100644 --- a/webui/src/App.tsx +++ b/webui/src/App.tsx @@ -3,12 +3,32 @@ import { useEffect, useMemo, useState } from 'react' type ChatItem = { role: 'user' | 'assistant'; text: string } type Session = { key: string; title: string } type CronJob = { id: string; name: string; enabled: boolean; schedule?: { kind?: string } } +type Cfg = Record const defaultSessions: Session[] = [{ key: 'webui:default', title: 'Default' }] +function getPath(obj: any, path: string, fallback: any = '') { + return path.split('.').reduce((acc, k) => (acc && acc[k] !== undefined ? acc[k] : undefined), obj) ?? fallback +} + +function setPath(obj: any, path: string, value: any) { + const keys = path.split('.') + const next = JSON.parse(JSON.stringify(obj || {})) + let cur = next + for (let i = 0; i < keys.length - 1; i++) { + const k = keys[i] + if (typeof cur[k] !== 'object' || cur[k] === null) cur[k] = {} + cur = cur[k] + } + cur[keys[keys.length - 1]] = value + return next +} + export function App() { const [token, setToken] = useState('') - const [cfgText, setCfgText] = useState('{}') + const [cfg, setCfg] = useState({}) + const [cfgRaw, setCfgRaw] = useState('{}') + const [showRaw, setShowRaw] = useState(false) const [sessions, setSessions] = useState(defaultSessions) const [active, setActive] = useState('webui:default') const [chat, setChat] = useState>({ 'webui:default': [] }) @@ -21,19 +41,38 @@ export function App() { async function loadConfig() { const r = await fetch(`/webui/api/config${q}`) - setCfgText(await r.text()) + const txt = await r.text() + setCfgRaw(txt) + try { + setCfg(JSON.parse(txt)) + } catch { + setCfg({}) + } } async function saveConfig() { - const parsed = JSON.parse(cfgText) + const payload = showRaw ? JSON.parse(cfgRaw) : cfg const r = await fetch(`/webui/api/config${q}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(parsed), + body: JSON.stringify(payload), }) alert(await r.text()) } + const bindText = (path: string) => ({ + value: String(getPath(cfg, path, '')), + onChange: (e: React.ChangeEvent) => setCfg((v) => setPath(v, path, e.target.value)), + }) + const bindNum = (path: string) => ({ + value: Number(getPath(cfg, path, 0)), + onChange: (e: React.ChangeEvent) => setCfg((v) => setPath(v, path, Number(e.target.value || 0))), + }) + const bindBool = (path: string) => ({ + checked: Boolean(getPath(cfg, path, false)), + onChange: (e: React.ChangeEvent) => setCfg((v) => setPath(v, path, e.target.checked)), + }) + async function refreshNodes() { const r = await fetch(`/webui/api/nodes${q}`) const j = await r.json() @@ -129,9 +168,23 @@ export function App() {
-
Config
-
-