import { useEffect, useState } from 'react' import { Alert } from '@/components/ui/Alert' import { Button } from '@/components/ui/Button' import { Select } from '@/components/ui/Select' import { TextField } from '@/components/ui/TextField' import { toast } from '@/components/ui/toast' import { errorMessage } from '@/lib/api' import { conflictSettings, useSettings, useUpdateSettings, type EffectiveAgent, type InstanceSettings, } from '@/lib/settings' import { usePageTitle } from '@/lib/usePageTitle' // agentEnabled is a tri-state on the wire (null = inherit env, true, false); the // form models it as three named choices so "inherit" is a deliberate pick, not // an empty control. type EnabledChoice = 'inherit' | 'on' | 'off' const toChoice = (v: boolean | null): EnabledChoice => (v === null ? 'inherit' : v ? 'on' : 'off') const fromChoice = (c: EnabledChoice): boolean | null => (c === 'inherit' ? null : c === 'on') /** Admin-only instance settings (#79). The one that matters today is the agent * model; the API key stays in the environment and is only ever reported as * present/absent, never shown or edited. */ export function SettingsPage() { usePageTitle('Settings') const settings = useSettings() const update = useUpdateSettings() if (settings.isPending) { return

Loading settings…

} if (settings.isError) { return {errorMessage(settings.error, "Couldn't load settings.")} } return } function SettingsForm({ data, update, }: { data: { settings: InstanceSettings; effective: EffectiveAgent } update: ReturnType }) { const [model, setModel] = useState(data.settings.agentModel) const [enabled, setEnabled] = useState(toChoice(data.settings.agentEnabled)) const [version, setVersion] = useState(data.settings.version) const [error, setError] = useState(null) // Rebase the form when the query cache updates (e.g. a successful save writes // the new row back), so the version we submit is never stale. useEffect(() => { setVersion(data.settings.version) }, [data.settings.version]) const eff = data.effective const save = () => { setError(null) update.mutate( { agentModel: model.trim(), agentEnabled: fromChoice(enabled), version }, { onSuccess: () => toast.info('Settings saved.'), onError: (err) => { const current = conflictSettings(err) if (current) { // Someone else saved first. Adopt their row so the next attempt is // clean, and say so rather than silently discarding this edit. setModel(current.agentModel) setEnabled(toChoice(current.agentEnabled)) setVersion(current.version) setError('Someone else changed these settings just now — reloaded their version. Re-apply your change if you still want it.') return } setError(errorMessage(err, "Couldn't save settings.")) }, }, ) } return (

Settings

Instance-wide, admin-only. Changes take effect immediately — no restart.

Garden assistant

The model runs against your Ollama Cloud key, which is set in the environment and never shown here.

setModel(e.target.value)} hint={ model.trim() === '' ? `Empty — inheriting ${eff.model || 'the built-in default'} from the environment.` : 'A majordomo model spec. A comma-separated list is a failover chain.' } />