159 lines
12 KiB
HTML
159 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<script src="./support.js"></script>
|
|
</head>
|
|
<body>
|
|
<x-dc>
|
|
<helmet>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="stylesheet" href="_ds/organic-9b68eb3f-20a3-40f4-b132-a5a6542b5b19/styles.css">
|
|
<script src="pansy-theme.js"></script>
|
|
<script src="_ds/organic-9b68eb3f-20a3-40f4-b132-a5a6542b5b19/_ds_bundle.js"></script>
|
|
<style>
|
|
:root{--p-field:#efe3c9;--p-grid-ink:#201e1d;--p-ink-strong:#474238;--p-ink-soft:#645c50;--p-ink-mute:#82796a;--p-bed-fill:#e3d0ac;--p-bed-stroke:#b5a37f;--p-ing-fill:#d6bf98;--p-ing-stroke:#b1a07c;--p-path-fill:#ece1cb;--p-path-stroke:#cabfa6;--p-bag-fill:#d8c5a2;--p-bag-stroke:#a99677;--p-bkt-fill:#cfc3ad;--p-bkt-stroke:#9a8d76;--p-tree-fill:#dce9c6;--p-tree-stroke:#8fa073;--p-str-fill:#d9cfbc;--p-str-stroke:#a3947c}
|
|
html,body{margin:0;min-height:100%}
|
|
a{color:var(--color-accent)} a:hover{color:var(--color-accent-600)}
|
|
</style>
|
|
</helmet>
|
|
<div data-screen-label="Settings" style="min-height:100vh;background:var(--color-bg);font-family:var(--font-body);color:var(--color-text)">
|
|
<nav class="nav">
|
|
<span class="nav-brand" style="display:flex;align-items:center;gap:8px">
|
|
<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="var(--color-accent-2-600)" stroke-width="2.75" stroke-linecap="round" stroke-linejoin="round"><path d="M7 20h10"></path><path d="M10 20c5.5-2.5.8-6.4 3-10"></path><path d="M9.5 9.4c1.1.8 1.8 2.2 2.3 3.7-2 .4-3.5.4-4.8-.3-1.2-.6-2.3-1.9-3-4.2 2.8-.5 4.4 0 5.5.8z"></path><path d="M14.1 6a7 7 0 0 0-1.1 4c1.9-.1 3.3-.6 4.3-1.4 1-1 1.6-2.3 1.7-4.6-2.7.1-4 1-4.9 2z"></path></svg>
|
|
pansy
|
|
</span>
|
|
<a href="Pansy Gardens.dc.html">Gardens</a>
|
|
<a href="Pansy Plants.dc.html">Plants</a>
|
|
<span style="margin-left:auto;display:flex;align-items:center;gap:10px">
|
|
<button class="btn btn-icon btn-secondary" style="border-radius:999px" onClick="{{ onTheme }}" title="{{ themeTitle }}">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round" stroke-linejoin="round"><path d="{{ themeD1 }}"></path><path d="{{ themeD2 }}"></path><path d="{{ themeD3 }}"></path></svg>
|
|
</button>
|
|
<a href="Pansy Settings.dc.html" aria-current="page" class="btn btn-icon btn-secondary" style="border-radius:999px;background:var(--color-accent-100)" title="Settings">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0 2-2z"></path></svg>
|
|
</a>
|
|
<a href="Pansy Login.dc.html" style="width:32px;height:32px;border-radius:999px;background:var(--color-accent-2-300);color:var(--color-accent-2-800);display:inline-flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;text-decoration:none">S</a>
|
|
</span>
|
|
</nav>
|
|
<div style="max-width:720px;margin:0 auto;padding:28px 24px 56px;display:flex;flex-direction:column;gap:18px">
|
|
<div style="display:flex;align-items:baseline;gap:12px">
|
|
<h2 style="margin:0">Settings</h2>
|
|
<span class="tag tag-accent-2" style="border-radius:999px">admin</span>
|
|
<span style="font-size:13px;color:var(--p-ink-mute);font-weight:600">Instance-wide · everyone on this pansy</span>
|
|
</div>
|
|
|
|
<div style="background:var(--color-neutral-100);border:1px solid var(--color-divider);border-radius:var(--radius-lg);padding:22px 24px;display:flex;flex-direction:column;gap:14px">
|
|
<h5 style="margin:0">Appearance</h5>
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
|
|
<span style="display:flex;flex-direction:column;gap:1px;flex:1;min-width:160px">
|
|
<span style="font-size:13.5px;font-weight:600">Theme</span>
|
|
<span style="font-size:12px;color:var(--p-ink-mute)">System follows your device</span>
|
|
</span>
|
|
<span style="display:flex;background:var(--color-neutral-200);border-radius:999px;padding:3px">
|
|
<sc-for list="{{ themeOpts }}" as="t"><button onClick="{{ t.onClick }}" style="{{ t.style }}">{{ t.label }}</button></sc-for>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="background:var(--color-neutral-100);border:1px solid var(--color-divider);border-radius:var(--radius-lg);padding:22px 24px;display:flex;flex-direction:column;gap:14px">
|
|
<h5 style="margin:0">Who gets in</h5>
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
|
|
<span style="font-size:13.5px;font-weight:600;flex:1;min-width:160px">Self-service signup</span>
|
|
<span style="display:flex;background:var(--color-neutral-200);border-radius:999px;padding:3px">
|
|
<sc-for list="{{ regOpts }}" as="r"><button onClick="{{ r.onClick }}" style="{{ r.style }}">{{ r.label }}</button></sc-for>
|
|
</span>
|
|
</div>
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
|
|
<span style="display:flex;flex-direction:column;gap:1px;flex:1;min-width:160px">
|
|
<span style="font-size:13.5px;font-weight:600">Local passwords</span>
|
|
<span style="font-size:12px;color:var(--p-ink-mute)">Turn off for pure-Authentik sign-in</span>
|
|
</span>
|
|
<button onClick="{{ onLocalAuth }}" style="{{ localAuthStyle }}">{{ localAuthLabel }}</button>
|
|
</div>
|
|
<div class="field"><label>OIDC issuer</label>
|
|
<input class="input" style="border-radius:999px" value="https://auth.dudenhoeffer.casa/application/o/pansy/" readOnly>
|
|
</div>
|
|
<div style="font-size:12px;color:var(--p-ink-mute);line-height:1.5">Client ID and secret live in the environment, not here. The first account ever registered became admin — that's you.</div>
|
|
</div>
|
|
|
|
<div style="background:var(--color-neutral-100);border:1px solid var(--color-divider);border-radius:var(--radius-lg);padding:22px 24px;display:flex;flex-direction:column;gap:14px">
|
|
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap">
|
|
<h5 style="margin:0">Garden assistant</h5>
|
|
<span class="tag tag-accent-2" style="border-radius:999px">{{ agentStatus }}</span>
|
|
</div>
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
|
|
<span style="display:flex;flex-direction:column;gap:1px;flex:1;min-width:160px">
|
|
<span style="font-size:13.5px;font-weight:600">Assistant</span>
|
|
<span style="font-size:12px;color:var(--p-ink-mute)">Swaps live — no restart</span>
|
|
</span>
|
|
<button onClick="{{ onAgent }}" style="{{ agentStyle }}">{{ agentLabel }}</button>
|
|
</div>
|
|
<div class="field"><label>Chat model — blank inherits the environment default</label>
|
|
<input class="input" style="border-radius:999px" value="{{ model }}" onChange="{{ onModel }}" placeholder="ollama-cloud/glm-5.2:cloud">
|
|
</div>
|
|
<div class="field"><label>Vision model — reads seed packets; must be vision-capable</label>
|
|
<input class="input" style="border-radius:999px" value="{{ vModel }}" onChange="{{ onVModel }}" placeholder="empty — packet scanning off">
|
|
</div>
|
|
<div style="font-size:12px;color:var(--p-ink-mute);line-height:1.5">The API key stays in the environment on purpose — a secret in the database would ride along in every backup. Precedence: this page → env → default.</div>
|
|
</div>
|
|
|
|
<div style="background:var(--color-neutral-100);border:1px solid var(--color-divider);border-radius:var(--radius-lg);padding:22px 24px;display:flex;flex-direction:column;gap:14px">
|
|
<h5 style="margin:0">You</h5>
|
|
<div style="display:flex;gap:10px;flex-wrap:wrap">
|
|
<div class="field" style="flex:1;min-width:180px"><label>Display name</label><input class="input" style="border-radius:999px" value="{{ dName }}" onChange="{{ onDName }}"></div>
|
|
<div class="field" style="flex:1;min-width:180px"><label>Email</label><input class="input" style="border-radius:999px" value="[email protected]" readOnly></div>
|
|
</div>
|
|
<div style="display:flex;justify-content:flex-end">
|
|
<a href="Pansy Login.dc.html" class="btn btn-ghost" style="border-radius:999px;text-decoration:none">Sign out</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-dc>
|
|
<script type="text/x-dc" data-dc-script>
|
|
class Component extends DCLogic {
|
|
state = { reg: 'closed', localAuth: true, agent: true, model: 'ollama-cloud/glm-5.2:cloud', vModel: 'ollama-cloud/qwen3.5-vl:cloud', dName: 'Steve Dudenhoeffer' };
|
|
componentDidMount() { this.themeMount(); }
|
|
componentWillUnmount() { this._unwatchTheme && this._unwatchTheme(); }
|
|
themeMount() {
|
|
const PT = window.PansyTheme; if (!PT) return;
|
|
this.setState({ themePref: PT.get() });
|
|
this._unwatchTheme = PT.watch(() => { if ((this.state.themePref || 'system') === 'system') PT.apply(PT.isDark('system')); });
|
|
}
|
|
cycleTheme = () => { const PT = window.PansyTheme; if (!PT) return; const p = PT.next(this.state.themePref || 'system'); PT.set(p); PT.apply(PT.isDark(p)); this.setState({ themePref: p }); };
|
|
themeVals() {
|
|
const p = this.state.themePref || 'system';
|
|
const d = window.PansyTheme ? window.PansyTheme.icon(p) : ['', '', ''];
|
|
return { themeD1: d[0], themeD2: d[1], themeD3: d[2], onTheme: this.cycleTheme, themeTitle: 'Theme: ' + p };
|
|
}
|
|
renderVals() {
|
|
const st = this.state;
|
|
const toggle = (on) => ({ cursor: 'pointer', border: 'none', borderRadius: '999px', padding: '6px 18px', fontFamily: 'var(--font-body)', fontSize: '12.5px', fontWeight: 700, background: on ? 'var(--color-accent-2-300)' : 'var(--color-neutral-300)', color: on ? 'var(--color-accent-2-800)' : 'var(--p-ink-soft)' });
|
|
return {
|
|
...this.themeVals(),
|
|
themeOpts: ['system', 'light', 'dark'].map(v => ({
|
|
key: v, label: v[0].toUpperCase() + v.slice(1),
|
|
style: { padding: '5px 16px', cursor: 'pointer', border: 'none', borderRadius: '999px', fontFamily: 'var(--font-body)', fontSize: '12.5px', fontWeight: 700, background: (st.themePref || 'system') === v ? 'var(--color-neutral-100)' : 'transparent', color: (st.themePref || 'system') === v ? 'var(--color-text)' : 'var(--p-ink-soft)', boxShadow: (st.themePref || 'system') === v ? 'var(--shadow-sm)' : 'none' },
|
|
onClick: () => { const PT = window.PansyTheme; if (!PT) return; PT.set(v); PT.apply(PT.isDark(v)); this.setState({ themePref: v }); },
|
|
})),
|
|
regOpts: [['open', 'Open'], ['closed', 'Closed']].map(([id, label]) => ({
|
|
key: id, label,
|
|
style: { padding: '5px 16px', cursor: 'pointer', border: 'none', borderRadius: '999px', fontFamily: 'var(--font-body)', fontSize: '12.5px', fontWeight: 700, background: st.reg === id ? 'var(--color-neutral-100)' : 'transparent', color: st.reg === id ? 'var(--color-text)' : 'var(--p-ink-soft)', boxShadow: st.reg === id ? 'var(--shadow-sm)' : 'none' },
|
|
onClick: () => this.setState({ reg: id }),
|
|
})),
|
|
onLocalAuth: () => this.setState(s => ({ localAuth: !s.localAuth })),
|
|
localAuthStyle: toggle(st.localAuth), localAuthLabel: st.localAuth ? 'On' : 'Off',
|
|
onAgent: () => this.setState(s => ({ agent: !s.agent })),
|
|
agentStyle: toggle(st.agent), agentLabel: st.agent ? 'On' : 'Off',
|
|
agentStatus: st.agent ? 'key present · glm-5.2 live' : 'off — key still in env',
|
|
model: st.model, onModel: (e) => this.setState({ model: e.target.value }),
|
|
vModel: st.vModel, onVModel: (e) => this.setState({ vModel: e.target.value }),
|
|
dName: st.dName, onDName: (e) => this.setState({ dName: e.target.value }),
|
|
};
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|