feat(web): inspector + widget controls (NF-UI-4) #355

Merged
code-lead merged 1 commit from feat/334-nfui4-inspector into main 2026-04-24 13:54:23 +00:00
Collaborator

Summary

  • Right-hand Inspector panel with three tabs: Config (widget-driven args editing), Docs (Markdown from nodeRegistry[type].ui.doc), Run (disabled, NF-UI-7 wires it up). Sticky 360px column, collapsible with persisted state.
  • Seven widgets under apps/web/src/features/flows/widgets/ sharing a common WidgetField wrapper: StringInput, TextArea, NumberInput (min/max clamp on blur), EnumSelect, BooleanToggle, LabelMultiSelect (free-form placeholder until NF-UI-5), JsonEditor (textarea + JSON.parse on blur — Monaco not in apps/web).
  • RegistryNode gets optional ui?: { widgets?, doc? }. Widget decls populated on router.switch / router.filter / template.string / agent.dispatch / forge.add_labels / forge.merge_pull_request / util.log / util.delay. Every entry also gets a one-paragraph ui.doc. Nodes without widget decls fall back to a raw JSON editor showing the full args object.
  • FlowCanvas mirrors React Flow's selection via onNodesChange (select changes), exposes the selected GraphNode to the Inspector, and applies widget edits back to the existing nodeArgs map — debounced at 300 ms with a flush-on-unmount safety. Dirty flag flips on every arg write; the existing Ctrl+S / Save path round-trips unchanged.

Test plan

  • bun x vitest run src/features/flows/ — 68 passed (29 new: 16 widget-level + 13 Inspector).
  • bun x turbo run typecheck — 4/4 workspaces clean.
  • bun x biome check — 6291 files, no fixes.
  • Manual smoke test in bun run dev once rebased against main.

Out of scope

  • Input-reference picker (NF-UI-5).
  • Annotations / node comments (NF-UI-6).
  • Run / dry-run tab enablement (NF-UI-7) — tab is rendered disabled per spec.
  • Monaco editor — stuck with <textarea> + parse validation until Monaco lands in the workspace.
  • LabelMultiSelect hitting /forge/repo-labels — deferred until NF-UI-5 / NF-7.

Design notes

  • Widget onChange emits undefined on clear (not empty string / zero) so the graph body preserves the "missing vs. set-to-default" distinction the executor cares about.
  • JsonEditor holds a separate draft string so mid-edit typing doesn't throw through JSON.parse on every keystroke; blur parses + pretty-prints or exposes an inline error.
  • Args changes reach the canvas through a mutation of the existing nodeArgs Map plus an argsVersion counter; keeps the save path (rfToGraph) untouched.

Closes #334

🤖 Generated with Claude Code

## Summary - Right-hand `Inspector` panel with three tabs: **Config** (widget-driven `args` editing), **Docs** (Markdown from `nodeRegistry[type].ui.doc`), **Run** (disabled, NF-UI-7 wires it up). Sticky 360px column, collapsible with persisted state. - Seven widgets under `apps/web/src/features/flows/widgets/` sharing a common `WidgetField` wrapper: `StringInput`, `TextArea`, `NumberInput` (min/max clamp on blur), `EnumSelect`, `BooleanToggle`, `LabelMultiSelect` (free-form placeholder until NF-UI-5), `JsonEditor` (textarea + `JSON.parse` on blur — Monaco not in `apps/web`). - `RegistryNode` gets optional `ui?: { widgets?, doc? }`. Widget decls populated on `router.switch` / `router.filter` / `template.string` / `agent.dispatch` / `forge.add_labels` / `forge.merge_pull_request` / `util.log` / `util.delay`. Every entry also gets a one-paragraph `ui.doc`. Nodes without widget decls fall back to a raw JSON editor showing the full `args` object. - `FlowCanvas` mirrors React Flow's selection via `onNodesChange` (`select` changes), exposes the selected `GraphNode` to the Inspector, and applies widget edits back to the existing `nodeArgs` map — debounced at 300 ms with a flush-on-unmount safety. Dirty flag flips on every arg write; the existing Ctrl+S / Save path round-trips unchanged. ## Test plan - [x] `bun x vitest run src/features/flows/` — 68 passed (29 new: 16 widget-level + 13 Inspector). - [x] `bun x turbo run typecheck` — 4/4 workspaces clean. - [x] `bun x biome check` — 6291 files, no fixes. - [ ] Manual smoke test in `bun run dev` once rebased against `main`. ## Out of scope - Input-reference picker (NF-UI-5). - Annotations / node comments (NF-UI-6). - Run / dry-run tab enablement (NF-UI-7) — tab is rendered disabled per spec. - Monaco editor — stuck with `<textarea>` + parse validation until Monaco lands in the workspace. - `LabelMultiSelect` hitting `/forge/repo-labels` — deferred until NF-UI-5 / NF-7. ## Design notes - Widget `onChange` emits `undefined` on clear (not empty string / zero) so the graph body preserves the "missing vs. set-to-default" distinction the executor cares about. - `JsonEditor` holds a separate draft string so mid-edit typing doesn't throw through `JSON.parse` on every keystroke; blur parses + pretty-prints or exposes an inline error. - Args changes reach the canvas through a mutation of the existing `nodeArgs` Map plus an `argsVersion` counter; keeps the save path (`rfToGraph`) untouched. Closes #334 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(web): inspector + widget controls (NF-UI-4)
All checks were successful
qa / qa (pull_request) Successful in 4m30s
qa / dockerfile (pull_request) Successful in 8s
d10441c68d
Right-hand inspector panel with Config / Docs / Run (disabled) tabs.
Seven widgets: StringInput, TextArea, NumberInput, EnumSelect,
BooleanToggle, LabelMultiSelect, JsonEditor. Registry entries carry
`ui.widgets` + `ui.doc`; nodes without widgets fall back to a raw
JsonEditor. FlowCanvas mirrors selection state and dispatches
debounced (300 ms) arg writes back to the editor's `nodeArgs` map.

Closes #334

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
code-lead deleted branch feat/334-nfui4-inspector 2026-04-24 13:54:23 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!355
No description provided.