Feat v0.2.7 user settings audit (#11)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #11.
This commit is contained in:
@@ -56,9 +56,6 @@
|
||||
.settings-nav-link:hover { background: var(--bg-hover); color: var(--text); text-decoration: none; }
|
||||
.settings-nav-link.active { background: var(--accent-dim); color: var(--accent); }
|
||||
.settings-nav-sep { margin: 8px 0; border-top: 1px solid var(--border); }
|
||||
.settings-nav-footer {
|
||||
margin-top: auto; padding: 10px 12px; border-top: 1px solid var(--border);
|
||||
}
|
||||
.settings-content { flex: 1; overflow-y: auto; padding: 28px; }
|
||||
.settings-content h2 { font-size: 18px; font-weight: 600; margin: 0 0 20px; }
|
||||
.settings-content .settings-section {
|
||||
|
||||
@@ -164,14 +164,9 @@ describe('Admin Preact surface handles settings', () => {
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
const settingsSrc = fs.readFileSync(settingsPath, 'utf-8');
|
||||
|
||||
it('admin settings component references allow_user_byok', () => {
|
||||
assert.ok(settingsSrc.includes('allow_user_byok'),
|
||||
'MISSING: allow_user_byok in admin settings component');
|
||||
});
|
||||
|
||||
it('admin settings component references allow_user_personas', () => {
|
||||
assert.ok(settingsSrc.includes('allow_user_personas'),
|
||||
'MISSING: allow_user_personas in admin settings component');
|
||||
it('admin settings component references allow_registration', () => {
|
||||
assert.ok(settingsSrc.includes('allow_registration'),
|
||||
'MISSING: allow_registration in admin settings component');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
/**
|
||||
* AppearanceSection — theme, UI scale, message font size
|
||||
* AppearanceSection — theme, UI scale
|
||||
*/
|
||||
const { html } = window;
|
||||
const { useState, useCallback } = hooks;
|
||||
|
||||
// Migrate cs- prefix to sb- (one-time, preserves existing prefs)
|
||||
if (!localStorage.getItem('sb-appearance') && localStorage.getItem('cs-appearance')) {
|
||||
localStorage.setItem('sb-appearance', localStorage.getItem('cs-appearance'));
|
||||
localStorage.removeItem('cs-appearance');
|
||||
}
|
||||
|
||||
const THEMES = [
|
||||
{ key: 'light', icon: '\u2600', label: 'Light' },
|
||||
{ key: 'dark', icon: '\u263E', label: 'Dark' },
|
||||
@@ -11,7 +17,7 @@ const THEMES = [
|
||||
];
|
||||
|
||||
function getPrefs() {
|
||||
try { return JSON.parse(localStorage.getItem('cs-appearance') || '{}'); }
|
||||
try { return JSON.parse(localStorage.getItem('sb-appearance') || '{}'); }
|
||||
catch { return {}; }
|
||||
}
|
||||
|
||||
@@ -19,7 +25,6 @@ export function AppearanceSection() {
|
||||
const prefs = getPrefs();
|
||||
const [theme, setTheme] = useState(sw.theme?.current || 'system');
|
||||
const [scale, setScale] = useState(prefs.scale || 100);
|
||||
const [msgFont, setMsgFont] = useState(prefs.msgFont || 14);
|
||||
|
||||
const applyTheme = useCallback((mode) => {
|
||||
setTheme(mode);
|
||||
@@ -30,8 +35,7 @@ export function AppearanceSection() {
|
||||
const save = useCallback(() => {
|
||||
const p = getPrefs();
|
||||
p.scale = scale;
|
||||
p.msgFont = msgFont;
|
||||
localStorage.setItem('cs-appearance', JSON.stringify(p));
|
||||
localStorage.setItem('sb-appearance', JSON.stringify(p));
|
||||
|
||||
// Apply scale to content area only (not shell/nav)
|
||||
const inner = document.getElementById('surfaceInner');
|
||||
@@ -48,10 +52,9 @@ export function AppearanceSection() {
|
||||
inner.style.height = '';
|
||||
}
|
||||
}
|
||||
document.documentElement.style.setProperty('--msg-font', msgFont + 'px');
|
||||
|
||||
sw.emit('toast', { message: 'Appearance saved', variant: 'success' });
|
||||
}, [scale, msgFont]);
|
||||
}, [scale]);
|
||||
|
||||
return html`
|
||||
<div class="settings-section">
|
||||
@@ -78,18 +81,6 @@ export function AppearanceSection() {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<h3 style="margin-top:16px;">Message Font Size</h3>
|
||||
<div class="form-group">
|
||||
<div style="display:flex;align-items:center;gap:8px;">
|
||||
<input type="range" min="12" max="20" step="1"
|
||||
value=${msgFont}
|
||||
onInput=${e => setMsgFont(parseInt(e.target.value))}
|
||||
style="flex:1;" />
|
||||
<span style="font-size:12px;color:var(--text-2);font-family:var(--mono);min-width:32px;">
|
||||
${msgFont}px
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-md btn-primary" onClick=${save}>Save</button>
|
||||
`;
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
* __BASE__ — base path
|
||||
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null)
|
||||
*
|
||||
* Policy flags (BYOK, personas) come from sw.auth.policies,
|
||||
* populated at SDK boot via GET /api/v1/profile/permissions.
|
||||
*
|
||||
* Layout: topbar + left nav + content area (same CSS classes as before).
|
||||
* All sections are native Preact components loaded lazily.
|
||||
*/
|
||||
const { html } = window;
|
||||
const { useState, useEffect, useCallback, useMemo } = hooks;
|
||||
const { useState, useEffect, useCallback } = hooks;
|
||||
const { render } = preact;
|
||||
|
||||
import { ToastContainer } from '../../primitives/toast.js';
|
||||
@@ -40,8 +37,6 @@ const NAV_ITEMS = [
|
||||
{ key: 'notifications', label: 'Notifications' },
|
||||
];
|
||||
|
||||
const BYOK_ITEMS = [];
|
||||
|
||||
// ── Section title map ───────────────────────
|
||||
const SECTION_TITLES = {
|
||||
general: 'General', appearance: 'Appearance',
|
||||
@@ -67,22 +62,8 @@ function SettingsSurface() {
|
||||
const BASE = window.__BASE__ || '';
|
||||
const section = window.__SECTION__ || 'general';
|
||||
|
||||
const policies = sw.auth?.policies || {};
|
||||
const [byokEnabled, setByokEnabled] = useState(policies.allow_user_byok === 'true');
|
||||
const [personasEnabled, setPersonasEnabled] = useState(policies.allow_user_personas === 'true');
|
||||
const [SectionComponent, setSectionComponent] = useState(null);
|
||||
|
||||
// Update when permissions refresh (e.g. after boot or policy change)
|
||||
useEffect(() => {
|
||||
function _onPermsChanged() {
|
||||
const p = sw.auth?.policies || {};
|
||||
setByokEnabled(p.allow_user_byok === 'true');
|
||||
setPersonasEnabled(p.allow_user_personas === 'true');
|
||||
}
|
||||
sw.on('auth.permissions.changed', _onPermsChanged);
|
||||
return () => sw.off('auth.permissions.changed', _onPermsChanged);
|
||||
}, []);
|
||||
|
||||
// Load the section component
|
||||
useEffect(() => {
|
||||
setSectionComponent(null);
|
||||
@@ -153,9 +134,7 @@ function SettingsSurface() {
|
||||
<div style="display:flex;flex:1;min-height:0;">
|
||||
${/* Left Nav */''}
|
||||
<div class="settings-nav">
|
||||
${NAV_ITEMS
|
||||
.filter(item => !item.gate || (item.gate === 'personas' && personasEnabled))
|
||||
.map(item => html`
|
||||
${NAV_ITEMS.map(item => html`
|
||||
<a href="${BASE}/settings/${item.key}"
|
||||
class="settings-nav-link ${section === item.key ? 'active' : ''}"
|
||||
onClick=${onNavClick}>
|
||||
@@ -163,27 +142,6 @@ function SettingsSurface() {
|
||||
</a>
|
||||
`)}
|
||||
|
||||
${byokEnabled && html`
|
||||
<div>
|
||||
<div class="settings-nav-sep"></div>
|
||||
${BYOK_ITEMS.map(item => html`
|
||||
<a href="${BASE}/settings/${item.key}"
|
||||
class="settings-nav-link ${section === item.key ? 'active' : ''}"
|
||||
onClick=${onNavClick}>
|
||||
${item.label}
|
||||
</a>
|
||||
`)}
|
||||
</div>
|
||||
<div class="settings-nav-footer">
|
||||
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;">
|
||||
<span style="font-size:11px;font-weight:600;color:var(--success);">BYOK Enabled</span>
|
||||
</div>
|
||||
<p style="font-size:10px;color:var(--text-3);margin:0;line-height:1.5;">
|
||||
Admin has enabled Bring Your Own Key
|
||||
</p>
|
||||
</div>
|
||||
`}
|
||||
|
||||
${EXT_NAV_ITEMS.length > 0 && html`
|
||||
<div>
|
||||
<div class="settings-nav-sep"></div>
|
||||
|
||||
Reference in New Issue
Block a user