Skip to content

feat(flows): Workflows B5b.1 — read-only canvas view (/flows/:id)#4477

Merged
senamakel merged 2 commits into
tinyhumansai:mainfrom
graycyrus:feat/flows-b5b-canvas
Jul 4, 2026
Merged

feat(flows): Workflows B5b.1 — read-only canvas view (/flows/:id)#4477
senamakel merged 2 commits into
tinyhumansai:mainfrom
graycyrus:feat/flows-b5b-canvas

Conversation

@graycyrus

@graycyrus graycyrus commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

First slice of the visual builder: view any saved workflow as a node graph at /flows/:id (read-only). De-risks the whole @xyflow/react integration before editing lands (B5b.2+).

Changes

  • Adds @xyflow/react ^12.11.1 — MIT; its dist/style.css inlines via Vite (no CSP change — the app CSP allows 'unsafe-inline' styles); CEF-safe SVG/DOM. Static import per repo rule.
  • lib/flows/types.ts — TS mirror of the tinyflows 0.3 model (verified vs crate source).
  • lib/flows/graphAdapter.ts (+20 tests) — workflowGraphToXyflow/xyflowToWorkflowGraph/autoLayout. Derives effective in/out ports per node from declared ports ∪ touching edges (Rust Port is output-only; a switch's live case ports are runtime-computed), so branch/switch nodes render correctly. Node positions round-trip via the graph's position field.
  • FlowCanvas + FlowNodeComponent — ReactFlow read-only (no drag/connect/select) + minimap/controls; per-kind node cards.
  • FlowCanvasPage/flows/:id: getFlow → adapter → canvas; loading/error/not-found.
  • flowsApi.getFlow; AppRoutes split /flows (list) + /flows/:id (canvas); FlowListRow flow-name is now a View link. navigation.spec unchanged (/flows/:id isn't tab-level).
  • i18n flows.canvas.* + flows.list.view + flows.nodeKind.* (12) × 14 locales.

Slicing

B5b.1 is read-only (view). Next: B5b.2 palette + drag/connect editing · B5b.3 per-node config panels · B5b.4 save + wire "New workflow"/proposal → canvas.

Verification

pnpm typecheck + lint + prettier clean · 75 Vitest tests pass · pnpm i18n:check parity clean.

Summary by CodeRabbit

  • New Features
    • Added a read-only workflow canvas for individual flows, including a dedicated canvas page and back-to-list navigation.
    • Added a “View” action on flows list items to open the canvas.
    • Improved graph viewing with themed styling, minimap/controls, and automatic layout.
    • Expanded translations for the canvas, flow actions, and workflow node types.
  • Bug Fixes
    • Improved route matching and canvas navigation behavior.
    • Enhanced canvas loading/error handling (including “not found” vs other failures).

First slice of the visual builder: view any saved flow as a node graph.

- Adds @xyflow/react ^12.11.1 (MIT; dist/style.css inlines via Vite — no CSP
  change; CEF-safe SVG/DOM). Static import per repo rule.
- lib/flows/types.ts — TS mirror of tinyflows 0.3 model (verified vs crate source).
- lib/flows/graphAdapter.ts (+20 tests) — workflowGraphToXyflow / xyflowToWorkflowGraph
  / autoLayout(nodes,edges) / edgeId. Derives effective input/output ports per node
  from declared ports ∪ touching edges (Rust Port is {name,label?}, output-only; a
  switch's live case ports are runtime-computed), so switch/branch nodes render right.
  Node positions round-trip via the graph's position field.
- components/flows/canvas/{FlowCanvas,FlowNodeComponent}.tsx + styles — ReactFlow in
  read-only mode (no drag/connect/select) + minimap/controls; per-kind node cards.
- pages/FlowCanvasPage.tsx — /flows/:id: getFlow → adapter → FlowCanvas; loading/
  error/not-found.
- flowsApi.getFlow; AppRoutes split /flows (list) + /flows/:id (canvas); FlowListRow
  flow-name is now a View link → navigate. navigation.spec unchanged (not tab-level).
- i18n flows.canvas.* + flows.list.view + flows.nodeKind.* (12) x14 locales.

typecheck/lint/prettier clean; 75 tests pass; i18n parity clean.
@graycyrus graycyrus requested a review from a team July 3, 2026 21:35
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b9a50cfe-d6ee-45ed-9ccc-581237de1440

📥 Commits

Reviewing files that changed from the base of the PR and between bd5f02b and 7bb91e4.

📒 Files selected for processing (6)
  • app/src/components/flows/canvas/FlowNodeComponent.tsx
  • app/src/components/flows/canvas/__tests__/FlowCanvas.test.tsx
  • app/src/lib/flows/graphAdapter.test.ts
  • app/src/lib/flows/graphAdapter.ts
  • app/src/pages/FlowCanvasPage.tsx
  • app/src/pages/__tests__/FlowCanvasPage.test.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/components/flows/canvas/tests/FlowCanvas.test.tsx
  • app/src/pages/FlowCanvasPage.tsx
  • app/src/lib/flows/graphAdapter.ts
  • app/src/components/flows/canvas/FlowNodeComponent.tsx

📝 Walkthrough

Walkthrough

Adds a read-only Workflow Canvas feature: graph conversion to @xyflow/react, a dedicated /flows/:id page and route, list-item navigation to that page, API loading for individual flows, and locale keys for the new canvas and node labels.

Changes

Workflow Canvas Feature

Layer / File(s) Summary
Workflow types and graph adapter
app/src/lib/flows/types.ts, app/src/lib/flows/graphAdapter.ts, app/src/lib/flows/graphAdapter.test.ts
Defines workflow graph types and implements conversion between WorkflowGraph and xyflow nodes and edges, layout, and edge identifiers with tests.
Canvas rendering components
app/package.json, app/src/components/flows/canvas/FlowCanvas.tsx, app/src/components/flows/canvas/FlowNodeComponent.tsx, app/src/components/flows/canvas/flowCanvasStyles.css, app/src/components/flows/canvas/__tests__/FlowCanvas.test.tsx
Adds @xyflow/react, the read-only canvas and custom node renderer, CSS overrides, and canvas tests.
getFlow API client
app/src/services/api/flowsApi.ts
Adds getFlow(id) and wires it into flowsApi.
FlowCanvasPage and routing
app/src/pages/FlowCanvasPage.tsx, app/src/AppRoutes.tsx, app/src/pages/__tests__/FlowCanvasPage.test.tsx
Adds the flow canvas page, fetch/load states, graph adaptation, and the /flows and /flows/:id routes.
View navigation from flows list
app/src/components/flows/FlowListRow.tsx, app/src/components/flows/FlowListRow.test.tsx, app/src/pages/FlowsPage.tsx, app/src/pages/FlowsPage.test.tsx
Turns the flow name into a View control and navigates to the canvas route.
Localization for Workflow Canvas
app/src/lib/i18n/*.ts
Adds canvas, node-kind, and flow-list view strings across all supported locales.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

Suggested labels: feature

Poem

A rabbit hopped to the canvas bright,
With nodes and edges in read-only light.
“View” opened paths with a gentle hop,
And flow names danced without a stop 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a read-only workflow canvas at /flows/:id.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the feature Net-new user-facing capability or product behavior. label Jul 3, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd5f02bd3e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/src/lib/flows/graphAdapter.ts Outdated

/** Stable, collision-free xyflow edge id for one `WorkflowEdge`. */
export function edgeId(edge: WorkflowEdge): string {
return `${edge.from_node}-${edge.from_port}-${edge.to_node}-${edge.to_port}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use collision-safe edge IDs

When a valid graph uses - in a node id or port name, different edges can produce the same React Flow id here; for example from_node: "a-b", from_port: "c", to_node: "d", to_port: "e" and from_node: "a", from_port: "b-c", to_node: "d", to_port: "e" both become a-b-c-d-e. The flow proposal schema only constrains these fields as strings, and React Flow treats edge ids as unique keys, so one of the connections can be overwritten or rendered incorrectly in the canvas. Encode the tuple (or escape delimiters) before using it as the id.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7bb91e4edgeId now uses JSON.stringify([from_node,from_port,to_node,to_port]), collision-safe when names contain -. Added tests for the colliding case + edgeId determinism.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
app/src/lib/flows/graphAdapter.ts (1)

50-53: 📐 Maintainability & Code Quality | 🔵 Trivial

Point duplicates Position from types.ts.

Both are { x: number; y: number }. Reusing Position here would remove the duplication.
[optional_refactor_low_reward_skip]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/flows/graphAdapter.ts` around lines 50 - 53, `Point` in
graphAdapter duplicates the existing `Position` type from types.ts, so update
the graphAdapter typings to reuse `Position` instead of redefining the same `{
x: number; y: number }` shape. Remove the local Point interface and switch any
graphAdapter references to the shared Position symbol to keep the type
definition centralized.
app/src/pages/FlowCanvasPage.tsx (2)

122-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: extract ready-state rendering and memoize the graph conversion.

The IIFE inline in JSX is a bit awkward, and workflowGraphToXyflow (which runs autoLayout) recomputes on every render while state.flow is unchanged. Consider hoisting to a useMemo keyed on state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/FlowCanvasPage.tsx` around lines 122 - 127, The ready-state JSX
in FlowCanvasPage is using an inline IIFE and recalculates the graph layout on
every render. Refactor the `state.status === 'ready'` branch by hoisting the
`workflowGraphToXyflow` conversion out of JSX and memoizing it with `useMemo`,
keyed on the relevant `state.flow`/graph data, then render `FlowCanvas` directly
from the memoized `nodes` and `edges`.

66-73: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use the RPC kind here instead of matching the error text. CoreRpcError already exposes kind: 'thread_not_found' and isThreadNotFoundCoreRpcError(...); branch on that so not-found handling doesn’t depend on backend wording.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/FlowCanvasPage.tsx` around lines 66 - 73, The not-found branch
in FlowCanvasPage’s load error handling is relying on matching error text, which
is brittle. Update the catch block to branch on the RPC error type instead of
message content by using CoreRpcError’s kind value and the
isThreadNotFoundCoreRpcError helper, then keep the existing notFound vs error
state updates in the same load logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/flows/canvas/FlowNodeComponent.tsx`:
- Around line 74-81: Add a defensive fallback in FlowNodeComponent so an unknown
FlowNode kind does not break canvas rendering. In the FlowNodeComponent path
where NODE_KIND_META[data.kind] is read, guard against a missing entry before
using meta.color and derive a safe default color/label for unsupported kinds.
Keep the rest of the node rendering working by falling back in the
FlowNodeComponent logic rather than assuming every kind exists in
NODE_KIND_META.

In `@app/src/lib/flows/graphAdapter.ts`:
- Around line 36-45: `xyflowToWorkflowGraph` is dropping `type_version` on
round-trip because `FlowNodeData` does not carry it and the rebuild in
`xyflowToWorkflowGraph` omits it, causing non-default versions to be downgraded
to the default. Thread `type_version` through `FlowNodeData` and both conversion
paths in `graphAdapter.ts`, updating the
`flowGraphToXyflow`/`xyflowToWorkflowGraph` logic so `WorkflowNode` preserves
the original version when present and still defaults correctly when absent. Also
update the lossy-field doc comment near `xyflowToWorkflowGraph` to mention
`type_version` alongside `position`.

In `@app/src/pages/FlowCanvasPage.tsx`:
- Around line 54-79: The FlowCanvasPage load flow can be overwritten by stale
responses from a previous id because getFlow() is never cancelled when id
changes. Update the load/useEffect path in FlowCanvasPage so each request is
tied to the current id and any in-flight request is aborted or ignored when a
newer load starts; only call setState from load after confirming the response
still matches the latest id, and clean up the request in the useEffect that
invokes load().

---

Nitpick comments:
In `@app/src/lib/flows/graphAdapter.ts`:
- Around line 50-53: `Point` in graphAdapter duplicates the existing `Position`
type from types.ts, so update the graphAdapter typings to reuse `Position`
instead of redefining the same `{ x: number; y: number }` shape. Remove the
local Point interface and switch any graphAdapter references to the shared
Position symbol to keep the type definition centralized.

In `@app/src/pages/FlowCanvasPage.tsx`:
- Around line 122-127: The ready-state JSX in FlowCanvasPage is using an inline
IIFE and recalculates the graph layout on every render. Refactor the
`state.status === 'ready'` branch by hoisting the `workflowGraphToXyflow`
conversion out of JSX and memoizing it with `useMemo`, keyed on the relevant
`state.flow`/graph data, then render `FlowCanvas` directly from the memoized
`nodes` and `edges`.
- Around line 66-73: The not-found branch in FlowCanvasPage’s load error
handling is relying on matching error text, which is brittle. Update the catch
block to branch on the RPC error type instead of message content by using
CoreRpcError’s kind value and the isThreadNotFoundCoreRpcError helper, then keep
the existing notFound vs error state updates in the same load logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 05b48a10-d3db-4cde-9dae-8f2b753c0f5b

📥 Commits

Reviewing files that changed from the base of the PR and between 6faeeaa and bd5f02b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • app/package.json
  • app/src/AppRoutes.tsx
  • app/src/components/flows/FlowListRow.test.tsx
  • app/src/components/flows/FlowListRow.tsx
  • app/src/components/flows/canvas/FlowCanvas.tsx
  • app/src/components/flows/canvas/FlowNodeComponent.tsx
  • app/src/components/flows/canvas/__tests__/FlowCanvas.test.tsx
  • app/src/components/flows/canvas/flowCanvasStyles.css
  • app/src/lib/flows/graphAdapter.test.ts
  • app/src/lib/flows/graphAdapter.ts
  • app/src/lib/flows/types.ts
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • app/src/pages/FlowCanvasPage.tsx
  • app/src/pages/FlowsPage.test.tsx
  • app/src/pages/FlowsPage.tsx
  • app/src/pages/__tests__/FlowCanvasPage.test.tsx
  • app/src/services/api/flowsApi.ts

Comment thread app/src/components/flows/canvas/FlowNodeComponent.tsx
Comment thread app/src/lib/flows/graphAdapter.ts
Comment thread app/src/pages/FlowCanvasPage.tsx Outdated
…pe_version, fetch race

- edgeId now JSON.stringify([from_node,from_port,to_node,to_port]) — collision-safe
  when node/port names contain '-' (React Flow needs unique edge ids). [Codex]
- FlowNodeComponent: DEFAULT_NODE_META fallback so an unrecognized NodeKind renders
  as a plain node instead of throwing on COLOR_CLASSES[meta.color] and crashing the
  whole canvas. [CodeRabbit]
- graphAdapter: carry + restore type_version through the xyflow round-trip (was
  silently dropped → would downgrade a non-default version once B5b.4 saves). [CodeRabbit]
- FlowCanvasPage: single id-keyed effect with a cancelled flag so a slow getFlow for a
  superseded :id can't clobber newer state (mirrors useFlowRunPoller). [CodeRabbit]

55 tests pass; typecheck/lint/prettier/i18n clean.
@senamakel senamakel merged commit 318027d into tinyhumansai:main Jul 4, 2026
17 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants