mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
feat(settings): rebuild App Settings as a rail over one scrolling document
The modal had grown to 8 tabs that wrapped onto two rows on desktop and became a horizontal scroller on phones, with a "Display" mega-tab holding 11 sections and ~35 controls. Local Echo sat 60% down it, and the model settings were split across two tabs whose three controls fought each other (the 1M Opus toggle's own hint said it was "ignored when a Claude Model is selected above"). Replaced with a left rail that is a TABLE OF CONTENTS over one scrolling document: every section stays mounted, the rail follows the scroll, and find-in-page works across the whole thing. Nine sections: Terminal & Input (Local Echo is the first row of the first section) Appearance, Header & Panels, Models, Agents & CLIs, Notifications, Voice, Shortcuts, System Models are now one page. The picker is a card grid of BASE models with a single "1M context window" switch; context becomes a property of the chosen model and composes back into `claudeModel` as `base + [1m]`, which retires the precedence trap. Thinking effort is a segmented control on the same page, and the old Models tab (task routing) becomes a collapsed Advanced block under it. The 12 header-button toggles and the 8 panel toggles become chip grids, which is most of the old Display tab reclaimed. Rows now say whether a setting is per-device or synced, stated once per group. Phones drop the rail for a sticky jump pill that names the current section and opens a jump list, move Save into the header (the bottom action bar cost 60px), and render groups as one inset rounded list with hairline dividers instead of a stack of bordered cards. Load and save are untouched: every control keeps its id, so openAppSettings()/saveAppSettings() work as before. Model cards and the effort segment are views over hidden <select>s that stay the source of truth. test/app-settings-structure.test.ts pins that contract, plus the rail hooks admin-ui.js injects the multi-user Users section into. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,7 @@ async function bootWith(me: Record<string, unknown>) {
|
||||
const dom = new JSDOM(
|
||||
`<!doctype html><body>
|
||||
<button id="adminPanelBtn" class="btn-admin-panel btn-admin-panel--hidden"></button>
|
||||
<div class="modal" id="appSettingsModal"><div class="modal-tabs"></div><div class="modal-body"></div></div>
|
||||
<div class="modal" id="appSettingsModal"><nav class="set-rail"><div class="set-rail-items"></div></nav><div class="set-doc" id="appSettingsDoc"></div></div>
|
||||
</body>`,
|
||||
{ url: 'http://localhost/', runScripts: 'outside-only' }
|
||||
);
|
||||
@@ -45,22 +45,23 @@ async function bootWith(me: Record<string, unknown>) {
|
||||
}
|
||||
|
||||
describe('admin-ui boot', () => {
|
||||
it('exposes the identity and injects the Users tab for a multi-user admin', async () => {
|
||||
it('exposes the identity and injects the Users section for a multi-user admin', async () => {
|
||||
const { win } = await bootWith({ username: 'root', role: 'admin', multiUser: true, mustChangePassword: false });
|
||||
expect(win.__codemanUser).toMatchObject({ username: 'root', role: 'admin', multiUser: true });
|
||||
const btn = win.document.querySelector('[data-tab="settings-users"]');
|
||||
// The settings modal is a rail over one document: a rail entry, not a tab.
|
||||
const btn = win.document.querySelector('[data-section="settings-users"]');
|
||||
expect(btn).toBeTruthy();
|
||||
expect(win.document.getElementById('settings-users')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does NOT inject the Users tab for a regular user', async () => {
|
||||
it('does NOT inject the Users section for a regular user', async () => {
|
||||
const { win } = await bootWith({ username: 'joe', role: 'user', multiUser: true, mustChangePassword: false });
|
||||
expect(win.document.querySelector('[data-tab="settings-users"]')).toBeFalsy();
|
||||
expect(win.document.querySelector('[data-section="settings-users"]')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does NOT inject the Users tab in single-user mode', async () => {
|
||||
const { win } = await bootWith({ username: 'admin', role: 'admin', multiUser: false, mustChangePassword: false });
|
||||
expect(win.document.querySelector('[data-tab="settings-users"]')).toBeFalsy();
|
||||
expect(win.document.querySelector('[data-section="settings-users"]')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('shows the change-password modal when mustChangePassword is set', async () => {
|
||||
|
||||
Reference in New Issue
Block a user