import { html, nothing } from 'lit'; import { LightElement } from '../lib/base.js'; export class ConfigPage extends LightElement { static properties = { _open: { state: true }, _properties: { state: true }, _values: { state: true }, // { [key]: string } _saving: { state: true }, // Set _saved: { state: true }, // Set (brief flash) _error: { state: true }, }; constructor() { super(); this._open = false; this._properties = []; this._values = {}; this._saving = new Set(); this._saved = new Set(); this._error = null; } connectedCallback() { super.connectedCallback(); window.addEventListener('llm-page-change', (e) => { this._open = e.detail.page === 'config'; this.style.display = this._open ? 'flex' : 'none'; if (this._open) this._load(); }); } async _load() { this._error = null; try { const res = await fetch('/api/config'); if (!res.ok) throw new Error(await res.text()); const data = await res.json(); this._properties = data.sets ?? []; const vals = {}; for (const s of this._properties) for (const p of s.properties) vals[p.key] = p.value ?? ''; this._values = vals; } catch (e) { this._error = e.message; } } _setValue(key, val) { this._values = { ...this._values, [key]: val }; } async _save(prop) { const key = prop.key; const value = this._values[key] ?? ''; this._saving = new Set([...this._saving, key]); this.requestUpdate(); try { const res = await fetch(`/api/config/${encodeURIComponent(key)}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value }), }); if (!res.ok) throw new Error(await res.text()); this._saved = new Set([...this._saved, key]); setTimeout(() => { this._saved = new Set([...this._saved].filter(k => k !== key)); }, 1500); } catch (e) { alert(`Error saving ${prop.name}: ${e.message}`); } finally { this._saving = new Set([...this._saving].filter(k => k !== key)); } } _renderInput(prop) { const val = this._values[prop.key] ?? ''; if (prop.property_type === 'bool') { const effective = val !== '' ? val : (prop.default_value ?? 'true'); const checked = effective !== 'false'; return html`
{ this._setValue(prop.key, e.target.checked ? 'true' : 'false'); this._save(prop); }} />
`; } if (prop.property_type === 'int') { return html` this._setValue(prop.key, e.target.value)} />`; } if (prop.property_type === 'security_group') { const groups = prop.options ?? []; return html` `; } return html` this._setValue(prop.key, e.target.value)} />`; } _renderSet(set) { return html`
${set.name}
${set.description}
${set.properties.map(p => this._renderRow(p))}
`; } _renderRow(prop) { const saving = this._saving.has(prop.key); const saved = this._saved.has(prop.key); return html`
${prop.name}
${prop.description}
${this._renderInput(prop)} ${prop.property_type !== 'bool' ? html` ` : nothing}
`; } render() { return html`

Config

${this._error ? html`
${this._error}
` : nothing} ${this._properties.length === 0 && !this._error ? html`

Loading…

` : nothing}
${this._properties.map(s => this._renderSet(s))}
`; } }