Replace dead chat settings with user default surface preference
Remove all chat-era fields from user settings General section (Default Model, System Prompt, Max Tokens, Temperature, Show Thinking). Replace with a Default Surface dropdown that lets users override the global admin-configured landing page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,66 +1,35 @@
|
|||||||
/**
|
/**
|
||||||
* GeneralSection — user preferences (model, system prompt, max tokens, temperature)
|
* GeneralSection — user preferences (default surface)
|
||||||
*/
|
*/
|
||||||
const { html } = window;
|
const { html } = window;
|
||||||
const { useState, useEffect, useCallback, useRef } = hooks;
|
const { useState, useEffect, useCallback } = hooks;
|
||||||
|
|
||||||
export function GeneralSection() {
|
export function GeneralSection() {
|
||||||
const [models, setModels] = useState([]);
|
const [surfaces, setSurfaces] = useState([]);
|
||||||
const [settings, setSettings] = useState({});
|
const [defaultSurface, setDefaultSurface] = useState('');
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [maxHint, setMaxHint] = useState('');
|
|
||||||
const tempRef = useRef(null);
|
|
||||||
|
|
||||||
// Load settings + models
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const s = await sw.api.profile.settings();
|
const [s, surfList] = await Promise.all([
|
||||||
const m = sw.api.models?.enabled ? await sw.api.models.enabled() : null;
|
sw.api.profile.settings(),
|
||||||
|
sw.api.get('/api/v1/surfaces').catch(() => []),
|
||||||
|
]);
|
||||||
const sObj = s || {};
|
const sObj = s || {};
|
||||||
setSettings({
|
setDefaultSurface(sObj.default_surface || '');
|
||||||
model: sObj.model || '',
|
setSurfaces((surfList || []).filter(s => s.source !== 'core' && s.enabled));
|
||||||
system_prompt: sObj.system_prompt || '',
|
|
||||||
max_tokens: sObj.max_tokens || 0,
|
|
||||||
temperature: sObj.temperature ?? 0.7,
|
|
||||||
show_thinking: sObj.show_thinking || false,
|
|
||||||
});
|
|
||||||
const modelList = (m?.data || []).filter(x => !x.hidden);
|
|
||||||
setModels(modelList);
|
|
||||||
|
|
||||||
// Set initial max hint
|
|
||||||
const active = modelList.find(x => x.id === sObj.model);
|
|
||||||
if (active?.capabilities?.max_output_tokens > 0) {
|
|
||||||
setMaxHint(`(model max: ${(active.capabilities.max_output_tokens / 1000).toFixed(0)}K)`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[settings/general] Load failed:', e.message);
|
console.warn('[settings/general] Load failed:', e.message);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onModelChange = useCallback((e) => {
|
|
||||||
const id = e.target.value;
|
|
||||||
setSettings(s => ({ ...s, model: id }));
|
|
||||||
const m = models.find(x => x.id === id);
|
|
||||||
const cap = m?.capabilities || {};
|
|
||||||
setMaxHint(cap.max_output_tokens > 0
|
|
||||||
? `(model max: ${(cap.max_output_tokens / 1000).toFixed(0)}K)` : '');
|
|
||||||
}, [models]);
|
|
||||||
|
|
||||||
const onTempInput = useCallback((e) => {
|
|
||||||
setSettings(s => ({ ...s, temperature: parseFloat(e.target.value) }));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const save = useCallback(async () => {
|
const save = useCallback(async () => {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
await sw.api.profile.updateSettings({
|
await sw.api.profile.updateSettings({
|
||||||
model: settings.model,
|
default_surface: defaultSurface,
|
||||||
system_prompt: settings.system_prompt,
|
|
||||||
max_tokens: settings.max_tokens,
|
|
||||||
temperature: settings.temperature,
|
|
||||||
show_thinking: settings.show_thinking,
|
|
||||||
});
|
});
|
||||||
sw.emit('toast', { message: 'Settings saved', variant: 'success' });
|
sw.emit('toast', { message: 'Settings saved', variant: 'success' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -68,58 +37,23 @@ export function GeneralSection() {
|
|||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
}, [settings]);
|
}, [defaultSurface]);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<h3>Defaults</h3>
|
<h3>Default Surface</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Default Model</label>
|
<label>Landing page after login</label>
|
||||||
<select value=${settings.model} onChange=${onModelChange}>
|
<select value=${defaultSurface} onChange=${e => setDefaultSurface(e.target.value)}>
|
||||||
${models.map(m => html`
|
<option value="">-- Use global default --</option>
|
||||||
<option value=${m.id}>
|
${surfaces.map(s => html`<option key=${s.id} value=${s.id}>${s.icon ? s.icon + ' ' : ''}${s.title || s.id}</option>`)}
|
||||||
${m.name || m.id}${m.provider ? ` (${m.provider})` : ''}
|
|
||||||
</option>
|
|
||||||
`)}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<span style="font-size:12px;color:var(--text-3);">
|
||||||
<label>System Prompt</label>
|
Override the global default set by your administrator.
|
||||||
<textarea rows="3" placeholder="Optional system prompt\u2026"
|
If your chosen surface is uninstalled, the global default is used.
|
||||||
style="max-width:100%;"
|
|
||||||
value=${settings.system_prompt}
|
|
||||||
onInput=${e => setSettings(s => ({ ...s, system_prompt: e.target.value }))} />
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;gap:16px;">
|
|
||||||
<div class="form-group" style="flex:1;">
|
|
||||||
<label>Max Tokens${' '}
|
|
||||||
<span style="font-weight:400;color:var(--text-3);">${maxHint}</span>
|
|
||||||
</label>
|
|
||||||
<input type="number" placeholder="default"
|
|
||||||
value=${settings.max_tokens || ''}
|
|
||||||
onInput=${e => setSettings(s => ({ ...s, max_tokens: parseInt(e.target.value) || 0 }))} />
|
|
||||||
</div>
|
|
||||||
<div class="form-group" style="flex:1;">
|
|
||||||
<label>Temperature</label>
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;">
|
|
||||||
<input type="range" min="0" max="2" step="0.1"
|
|
||||||
ref=${tempRef}
|
|
||||||
value=${settings.temperature}
|
|
||||||
onInput=${onTempInput}
|
|
||||||
style="flex:1;" />
|
|
||||||
<span style="font-size:12px;color:var(--text-2);font-family:var(--mono);">
|
|
||||||
${settings.temperature}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<label style="display:flex;align-items:center;gap:8px;font-size:13px;color:var(--text-2);cursor:pointer;margin-top:4px;">
|
|
||||||
<input type="checkbox"
|
|
||||||
checked=${settings.show_thinking}
|
|
||||||
onChange=${e => setSettings(s => ({ ...s, show_thinking: e.target.checked }))} />
|
|
||||||
Show thinking blocks
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<button class="btn-md btn-primary" style="margin-top:12px;"
|
<button class="btn-md btn-primary" style="margin-top:12px;"
|
||||||
disabled=${saving} onClick=${save}>
|
disabled=${saving} onClick=${save}>
|
||||||
${saving ? 'Saving\u2026' : 'Save'}
|
${saving ? 'Saving\u2026' : 'Save'}
|
||||||
|
|||||||
Reference in New Issue
Block a user