Surfaced while debugging a stuck "connecting…" footer badge after the
DOMPurify hardening (C1). Three related concerns; each independent.
1. Renderer init has no error surface
A throw in any module reachable from app.ts aborts the whole renderer.
The user sees a frozen UI and no error indication. Today's example:
markdown.ts did import * as DOMPurifyMod from "dompurify" and called
DOMPurifyMod.addHook(...) — the namespace had no such method, threw
at module top level, killed everything downstream (status badge, IPC
listeners, shortcuts).
Suggestions:
- Global
window.onerror + window.onunhandledrejection that paint a
visible banner (footer or toast) with the error + reload affordance.
- Move side-effectful top-level code in app.ts into an explicit
bootstrap() function so init failure is catchable in one place.
2. app.ts is ~2500 lines
Contributing factor to #1 (one module to break, one module to grep).
Suggested split (rough — can be revisited):
- `chat-controller.ts` — message send/abort/streaming wiring
- `status.ts` — agent + galaxy status badges
- `prefs.ts` — Preferences modal + Galaxy/API key flows
- `files-panel.ts` — left-pane file tree (already partially extracted)
- `bootstrap.ts` — DOM-ready glue, error handler, listener attach order
Not a single PR. Stack of small, mechanical extractions; each PR keeps
the app working.
3. Hardcoded "connecting…" in index.html
`connecting...` is what the user sees if
anything in the init chain breaks before the first `setStatusBadge`
call. This is what made #1 so visible — a stuck UI that lied about the
brain state.
Suggestions:
- Start the badge empty (or `"…"`) so an unset state looks unset.
- Push brain init progress through the badge or chat (spawn → MCP boot
→ ready) so a slow startup doesn't look frozen.
- Consider merging the `agent-status` pill and the `galaxy-status` dot
into one "system status" widget — they represent related states and
two unrelated controls makes the footer noisier than it needs to be.
Recent context
- Race fix landed in 43db513: `agent:get-status` snapshot pulled by
renderer after listener attach. Defends against listener-attached-
after-event ordering, but doesn't help when the renderer fails to
load at all (today's case — DOMPurify import).
- DOMPurify import fix landed in 2b6a571.
Surfaced while debugging a stuck "connecting…" footer badge after the
DOMPurify hardening (C1). Three related concerns; each independent.
1. Renderer init has no error surface
A throw in any module reachable from
app.tsaborts the whole renderer.The user sees a frozen UI and no error indication. Today's example:
markdown.tsdidimport * as DOMPurifyMod from "dompurify"and calledDOMPurifyMod.addHook(...)— the namespace had no such method, threwat module top level, killed everything downstream (status badge, IPC
listeners, shortcuts).
Suggestions:
window.onerror+window.onunhandledrejectionthat paint avisible banner (footer or toast) with the error + reload affordance.
bootstrap()function so init failure is catchable in one place.2. app.ts is ~2500 lines
Contributing factor to #1 (one module to break, one module to grep).
Suggested split (rough — can be revisited):
Not a single PR. Stack of small, mechanical extractions; each PR keeps
the app working.
3. Hardcoded "connecting…" in index.html
`connecting...` is what the user sees if
anything in the init chain breaks before the first `setStatusBadge`
call. This is what made #1 so visible — a stuck UI that lied about the
brain state.
Suggestions:
→ ready) so a slow startup doesn't look frozen.
into one "system status" widget — they represent related states and
two unrelated controls makes the footer noisier than it needs to be.
Recent context
renderer after listener attach. Defends against listener-attached-
after-event ordering, but doesn't help when the renderer fails to
load at all (today's case — DOMPurify import).