# Chat Switchboard UI Rewrite — Design Document **Version:** 0.0.1 (draft) **Date:** 2026-03-20 **Scope:** Scorched earth rebuild of frontend on Preact+htm --- ## 1. Survey Findings ### What Exists | Metric | Value | |--------|-------| | JS files | 55 | | Total JS lines | ~29,350 | | CSS files | 22 (~203KB) | | Go templates | ~1,920 lines across 14 template files | | `innerHTML =` assignments | 294+ (91 in ui-admin.js alone) | | `querySelector` / `getElementById` calls | 600+ (130 in ui-admin.js) | | Direct `fetch()` outside api.js | ~24 calls across 10 files | | Vendor deps | marked, DOMPurify, mermaid, KaTeX, CM6 (esbuild bundle) | ### The SDK (switchboard-sdk.js) - ~860 lines, version-tagged `v0.28.5` in code but `v0.30.2` in docs - Thin wrapper over globals: `API`, `Events`, `Theme`, `UserMenu` - Delegates to `API._get()`, `API._post()`, etc. — not its own fetch layer - Has pipe/filter pipeline (pre-send, stream, render) — this is good and reusable - Has event bus bridge — good, reusable - Has theme control — good, reusable - No RBAC. Only `sw.isAdmin` (boolean derived from `API.user.role`) - No namespaced domain methods (e.g., `sw.api.channels.list()`) ### The Registry (sb.js) - Action registry: `sb.register(name, fn)` + `sb.ns(name, obj)` - Dual-write to `window[name]` for backward compat - Go templates call `sb.call('action', args)` via onclick handlers - 55 files all register into this flat global space ### Server Templates - Go templates generate initial DOM with hardcoded IDs - JS hydrates by ID lookups (`getElementById`, `querySelector`) - Templates inject `window.__USER__`, `window.__PAGE_DATA__`, `window.__SURFACE__` - This server-rendering + client-hydration pattern is deeply coupled ### What's Missing for RBAC - **No user-facing permissions endpoint.** `GET /admin/users/:id/permissions` is admin-only. There is no `GET /profile/permissions` or equivalent. The login/refresh response includes only `role: "user"|"admin"`, not the resolved permission set. - The frontend has zero permission-level gating. Everything is gated by `API.isAdmin` (binary) or by server-side template conditionals. ### Dependency Pipeline - `Dockerfile.frontend`: Stage 1 `npm pack` pulls marked, DOMPurify, mermaid, KaTeX. Stage 2 builds CM6 via esbuild. Stage 3 copies into nginx image. - Preact + htm would be added to Stage 1 as additional `npm pack` targets. - No bundler for app code — scripts loaded sequentially via ` ``` Application files continue to load as `