AgentCanvas / Pages / Developer Guide / Core / Decisions / Components / ADR-components-006
2026-04-04
Date
2026-04-04
Status
accepted
Field
components
Old ID
ADR-019

Context

AgentCanvas had 6 custom .tsx React components for canvas nodes that bypass GenericBlockRenderer (ADR-components-004). This meant adding certain node types still required writing TypeScript — violating the "Python-driven UI" principle. Analysis via ralplan consensus (Planner → Architect → Critic) revealed these 6 fall into three categories: (1) 4 output viewers (ObservationViewer, ThinkingLog, ActionLog, Metrics) — pure display widgets subscribing to WebSocket data, structurally identical; (2) CompositeCanvasNode — behavioral node with runtime subgraph introspection and drill-down navigation; (3) StateContainerNode — structurally privileged entity with 18 hardcoded references across 8 files (FRONTEND_ONLY_TYPES, dedicated graph conversion, state edge normalization). The Architect identified that composite and container are behavioral/structural problems, not rendering problems. The Critic confirmed ConfigField conflation (mixing user-editable controls with read-only data display) as a category error.

Decision

(1) Introduce DisplayField dataclass alongside ConfigField in bases.py — separates user-editable config controls (slider, text, select) from read-only runtime data display widgets (image_viewer, log_list, metric_table). Add display_fields: List[DisplayField] to NodeUIConfig. (2) Add layout="viewer" to NodeUIConfig for output viewer nodes. (3) Refactor GenericBlockRenderer into a thin ~80-line dispatcher + separate layout component files: BlockLayout.tsx, StripLayout.tsx, ViewerLayout.tsx (each <200 lines). Extract shared code to layoutUtils.ts and useNodeOutput.ts hook. (4) Eliminate 4 custom output viewer .tsx files. (5) Keep CompositeCanvasNode.tsx and StateContainerNode.tsx as explicit entries in unifiedNodeTypes.ts — they are architectural singletons with behavioral/structural coupling that cannot be purely data-driven from Python. (6) New viewerFieldRenderers.tsx handles image_viewer (base64 → img), log_list (scrollable entry list), metric_table (key-value metrics).

Alternatives

(a) All 6 at once (single phase) — rejected by Architect: composite/container are behavioral problems masquerading as rendering problems; forcing them through GenericBlockRenderer gives false unification while structural divergence remains. (b) Single-file extension (~900+ lines) — rejected by 800-line coding style rule. (c) Overload ConfigField with display types — rejected by Critic: semantic conflation of user-editable controls and read-only data widgets violates single responsibility.

Rationale

Phased approach (4 output viewers now, composite/container deferred) delivers 67% of the value with ~20% of the risk. The 4 output viewers are the category that multiplies as features grow — future output nodes will need zero .tsx. The 2 remaining custom files are architectural singletons that will never proliferate. DisplayField preserves the clean boundary between "what users configure" (ConfigField) and "what the node displays from runtime data" (DisplayField). The Layout Component System keeps each file under 200 lines while maintaining the single-dispatcher entry point.

Affected docs

canvas-system.md, base-canvas-node.md, roadmap.md, glossary.md