Appearance
Browser component API
The Smithers workbench is a Pattern A composition: every visual surface is its own custom element. This page is the flat list. The narrative version is in Workbench (browser UI).
Tag registration
projects/matrix-3/packages/smithers/src/browser/index.ts:7-49 is the authoritative list:
mx-split → MxSplit (from @open-matrix/core)
smithers-app → SmithersApp
smithers-topology-rail → TopologyRail
smithers-source-graph → SourceGraph
smithers-inspector → Inspector
smithers-control-palette → ControlPalette
smithers-runner-panel → RunnerPanel
smithers-agent-chat → AgentChat
smithers-constraint-collector → ConstraintCollector
smithers-status-bar → SmithersStatusBarAll registrations are idempotent (if (!customElements.get(...))).
API summary
<smithers-app>
The root shell. Pattern A — SmithersApp extends MatrixActorHtmlElement directly (SmithersApp.ts:143).
| Accepts | app.refresh |
| Emits | declared at SmithersApp.ts:150-... (force-poll, theme-changed, layout-changed) |
| Children | every other workbench tag, composed via nested <mx-split> |
Polls graph.topology-rail and graph.issue-inspector on a user-selectable interval (5s/10s/15s/30s/60s). Subscribes to $activity frames and routes them to AgentChat or RunnerPanel.
<smithers-topology-rail>
The left rail. Reads a TopologyRailProjection from graph.topology-rail. Renders queue counts (ready / blocked / active / escalated), critical-path issues, blocker badges, ratchet count.
<smithers-source-graph>
The center graph. Switches between forest, neighborhood, critical-path, blast-radius, dependency-delta modes via WorkspaceActor.workspace.set-graph-mode. Pulls projections from ConstraintGraph.
<smithers-inspector>
Right-of-graph panel. Six tabs (InspectorTab from src/types/index.ts:43):
overview, constraints, prompts, reviews, diff, sessionEach tab is a typed projection from a daemon op:
| Tab | Op |
|---|---|
overview | graph.issue-inspector(N) |
constraints | graph.constraints-for(N) |
prompts | smithers.workspace.workspace.tree(N) + the gathered prompt |
reviews | smithers.review-queue filtered by issue |
diff | session trace + applyPatch results |
session | session transcript from system.agents |
<smithers-control-palette>
The action surface. Buttons map directly to supervisor ops:
| Button | Op |
|---|---|
| Start | smithers.start { issueNumber } |
| Stop | smithers.stop { issueNumber } |
| Steer | smithers.steer { issueNumber, prompt } |
| Sign-Off | smithers.sign-off { issueNumber, signOffReport } |
| Auto | smithers.auto { enabled } |
<smithers-runner-panel>
Per-runtime status — which workers are online, last heartbeat, container ID. Reads from smithers.containers and smithers.active-agents.
<smithers-agent-chat>
The live $activity stream from the agent's session. Renders text/tool-call/tool-result frames as a chat-like timeline. Re-uses ActivityFrame from @open-matrix/core.
<smithers-constraint-collector>
Approve / dispatch chat constraints. The user types a constraint; it goes through smithers.constraint-approved to add to the projection for a target issue.
<smithers-status-bar>
Top bar. Renders SmithersStatusBarState:
- Theme toggle (dark/light from
SMITHERS_THEME_TOKENS). - Poll-interval picker (5/10/15/30/60s).
- Workstream-request indicator (count of pending requests).
- Supervisor health pill (from
smithers.health).
Layout primitive
<mx-split> from @open-matrix/core/framework/components/MxSplit. Used to compose the four splits:
ts
// SmithersApp.ts:29-34
[
{ childName: 'main-split', defaultPosition: 22, min: 12, max: 40 },
{ childName: 'source-split', defaultPosition: 70, min: 30, max: 90 },
{ childName: 'work-split', defaultPosition: 50, min: 20, max: 80 },
{ childName: 'chat-split', defaultPosition: 60, min: 20, max: 85 },
]Positions are persisted via WorkspaceActor so they restore on reload.
Theme tokens
SMITHERS_THEME_TOKENS (SmithersApp.ts:44-117) defines dark and light — exhaustive token sets covering surface, text, info/success/warning/danger surfaces, code background, and dividers. Selected via the status bar.
Pattern A reminder
Smithers uses Pattern A, not Pattern B. The shell IS the actor. There is no separated "headless" actor for SmithersApp itself; it owns DOM and logic together. Compare with FlowPad which uses Pattern B (separated FlowpadAppShell + FlowpadApp).
The choice is justified in .claude/rules/webapp-compliance.md: the workbench's logic is mostly plumbing (subscriptions, splits, theme), so separating it would create more boilerplate than it saves.
See also
Source:
projects/matrix-3/packages/smithers/src/browser/index.ts:7-49,projects/matrix-3/packages/smithers/src/browser/SmithersApp.ts:1-200,projects/matrix-3/packages/smithers/src/browser/workbench/.