/* Brain of Reese — Global Tuning view (phase 27; phase 76 task 01: * shell view module). * * The standalone manager for steering notes: create / list / edit / * delete WITHOUT a chat conversation. This module is the single owner * of the view's behaviour: * * • loadNotes() — GET /api/steering → the newest-first note list * (#tune-list) + the empty state. A failed fetch (API down, or the * anonymous direct-URL 403) keeps the LAST RENDERED list — * progressive enhancement, never a blanked panel. * • create — #tune-form submit → POST /api/steering. 201 clears the * textarea, announces through the live region, and reloads the * list; any failure keeps the form (the instruction survives) and * shows the API detail inline under the button (role=alert). * #tune-save is disabled while the request is out. * • edit — a row's Edit button swaps the text for an inline * .tuning-edit-form: a prefilled textarea (maxlength 2000) + * Save / Cancel. Save → PUT /api/steering/{id}; 200 replaces the * form with the .tuning-saved status (role=status) and announces; * a failure keeps the form + an inline error; Cancel reverts to * the text span. The note id rides on the form (data attribute). * • delete — DELETE /api/steering/{id}. 204 removes the row * immediately (optimistic) and announces; 404 also drops the row * and reloads to resync; other failures re-enable the button and * announce a retry. The empty state is re-checked on every removal. * • announce(msg) — #tune-announcer (role=status, aria-live=polite), * the screen-reader confirmation for create / edit / delete. * * Phase 76 (task 01) — shell view module (the "Global Tuning" view of * the ONE-document shell; /tuning.html now serves the shell, and * assets/router.js lazy-imports THIS module on first show): * * • the top-level boot is now `export async function mount(root)` — * root is the view's
, and every DOM * lookup scopes to root (the view ids stay unique across the * shell — scoped lookups keep the module honest and testable). * The router mounts a view ONCE (mount-once, hide-forever), so * the binding + state survive every switch. * • the initSharedHeader() call is DROPPED: in the shell the shared * header boots exactly once, via the chat module (app.js) at shell * boot — the view never re-boots it. The admin gate keeps * fetchIsAdmin() — the SAME cached /api/whoami promise header.js * exports (zero extra requests; the flag decides whether the note * list loads at all, the Sources-page gate pattern). * * Anonymous-safe (phase 27 task 03, unchanged in the shell): the * header hides the "Tuning" nav link for anonymous visitors; a DIRECT * anonymous URL still gets a safe view — loadNotes() only runs when * the cached whoami says admin, the list stays on its empty state, * and the create form 403s gracefully on submit (the inline error * carries the API detail). Note text is always rendered with * textContent — never innerHTML (XSS-safe, like app.js's steering * panel). * * The shared header module loads through this script's own relative * import ("./header.js") — a hoisted import evaluated before this body * runs (single-evaluation design: no direct