Repository navigation
Conversation
Introduce a sequential (list-style) canvas view for apollo-react as an alternative to the graph layout, with round-trip-safe view switching. - Projection and layout engine mapping the graph to ordered rows, with subtree-aware move operations and slot-based navigation - BaseNode bar render variant and sequential node shells aligned with card behavior for icons, selection, display, and a11y - Connector edges with smooth-step corners, shared branch spines, and dashed container merge-back routing - Insert pipeline with preview gap, shared cross-instance pending state, and container-aware parenting - Gutter, keyboard navigation, and aria labels - Virtualization past a node ceiling to avoid mount-burst loops, fitView on entry, and perf-loop fixes - Stories, i18n catalog, docs, and hardening tests Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
React Flow requires every parent to precede its children in the `nodes` array. When it does not, `updateChildNode` logs a warning and then leaves the child unparented for that pass: no `parentLookup` entry, and the child's relative `position` is used as an absolute one. That reads as broken containment even though `parentId` is correct the whole time. A nested for-each converted from the sequential view rendered its parent with an empty body and the "+ Add step" affordance, dragging the parent left the child behind, and the child sat at coordinates unrelated to the box it belonged to. Neither the projection nor the flow layout notices, because both bucket by `parentId` and are order-independent, so a mis-ordered graph projects and lays out perfectly and only misrenders. `prepareCanvasViewTransition` now repairs the order when entering flow view via `orderNodesParentsFirst`, a stable sort by containment depth that returns an already-ordered array by identity, keeps siblings in their relative order (array order is the layout's tie-breaker for same-rank nodes), and treats dangling `parentId`s and cycles as roots rather than dropping them. `applyGraphChangeSet` was why this class of bug was invisible to the suite: it appended re-added nodes, while production forwards a move as a single `replace` that React Flow applies in place. Outdenting a container under that helper genuinely produced child-before-parent order with nothing asserting otherwise, so the move tests were validating a shape production never emits. It now replaces in place. Also folds in a quality pass over the same area: - Register the `preview` node type for a host-supplied `flowNodeTypes` too. The injection sat after the early return, so the host most likely to render its own `AddNodeManager` through `children` was the one that never got it. Anything the host registered under `preview` still wins. - Point the container-predicate docs at the existing public `useCanvasNodeLayout()` instead of teaching hosts a fifth hand-written copy of `isContainerNodeManifest(registry.getManifest(node.type))`. - Drop three local xyflow mocks already provided globally by `test/canvas-mocks.ts`; each used `importOriginal()`, silently un-mocking every other xyflow export for those files. - Make the `CanvasView` list compiler-enforced via `Record<CanvasView, true>` and reuse the guard in `ViewSwitcher`, removing a third hand-written enumeration and its cast. - Assert `extent` in the insert parity test, which compared only `parentId` and so let the two paths drift. - Trim duplicated commentary and redundant test cases. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rden the view Review remediation, parity, and performance pass over the sequential canvas. Correctness - removeStep heals only sequence edges; artifact edges are removed with the step, never re-sourced onto the predecessor. - Keyboard shortcuts act only on canvas surfaces (pane, node, edge, the accessible list, the wrapper), so keys inside portaled menus and the Add Node panel no longer move selection or delete steps. - Unplaceable sequence edges (second continuation, cross-scope, unreachable fan-in) render as goto connectors; compatibility reports cross-scope-edge and duplicate-edge instead of certifying the graph exact. - In-body cycles walk from the first child and report cycle, not orphan. - Lane order for undeclared branch nodes is deterministic across edits. - BaseCanvas repairs parent-before-child ordering for every host. - Bars read their declared height from the store; no measured-zero write-back. Parity (toggle the view without losing anything) - Bars render all four adornment corners, the footer, label placeholders, drillable and collapsed stacking, and hide the kebab while dragging. - Artifact-only resources (tools, context, memory) are excluded from the projection and surface as attachment chips on their owning step; the compatibility report stays exact for an agent with tools. - Icon tile radius follows the card shape (round for circle, pill for pill). - readOnlyNodeIds is honored by delete and move. - New "Card vs Bar" stories: shape mapping, state parity, toggle round trip. Performance - Context values read by every bar or edge are identity-stable across data-only edits (insert state, attachments, lane add, toolbar handler, read-only and collapsed sets, layout options). - Insert buttons and branch headers render from one canvas-level portal layer instead of one portal per connector. - Per-node handle resolution is cached; the preview-edge selector exits early when no preview node is selected; bars skip the redundant mount-time updateNodeInternals; the card keys its measure effect on a handle string. Structure - Shared helpers replace duplicated code: createSequencePredicates, indexNodesByParent, filterGraphByNodes, resolveSequenceMetrics, isArtifactOnlyManifest, buildEdgeId, shallowEqualExcept, and a map-aware getAbsolutePosition. moveStep is removed in favor of moveSubtree. - prepareCanvasViewTransition accepts getManifest and derives the same predicates the canvas uses. - Comments trimmed to the non-obvious why; the gallery link and README degraded-graphs section corrected. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ntime containers A flow canvas can now show any container's body as a sequence while the rest of the canvas stays flow. The container's header carries a flow/sequence switch; in sequence mode its steps render as bars inside the frame, only the container itself is draggable in the outer flow, and the frame sizes to the stack. Sequenced containers - `sequencedContainerIds` / `onSequencedContainerIdsChange` on SequentialCanvas, host-owned with an internal fallback. - `projectContainerSequence` projects one container's descendants as a scoped sequence (nested containers indent in place); slots keep the id of the container they belong to so inserts parent correctly. - A card-or-bar dispatcher renders each node from view state, never node data. - Change filtering drops projected geometry for bars and frame dimensions while passing selection, renames and the container's own moves. - Insert buttons, the Add Node panel and the preview gap work inside frames, including slots inside a nested For Each; committed nodes get real flow positions and their containers grow, so the body reads correctly in flow. - Step numbers and expand/collapse chevrons render in a gutter inside the frame. - Clones stamp `measured` equal to their declared size, which stops the frame flickering between flow and sequence sizes while dragging. Runtime containers - `display.container.kind: 'runtime'` with no inner-boundary handles is a self-contained subflow expanded inline: outer input and output only, no inner rail, no header badge. - Its sequence body opens with its own start row; Add trigger calls `onAddTrigger(containerId)`. - In flow presentation an empty runtime container adds an unconnected first step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dependency License Review
License distribution
Excluded packages
|
📊 Coverage + size by packagePer-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.
"Coverage" is each package's own |
Storybook visual diffBaseline is the deployed main Storybook, so changes merged to main after this branch was last updated can also appear here. Logs Updated (PT): Oct 18, 2026, 06:28:04 PM |
Supersedes #951, which the stale bot closed.
Summary
Adds a sequential (n8n-style, vertical) presentation of the canvas to
apollo-react, and lets a flow canvas show individual container bodies as sequences. The canonicalnodes/edgesstay the single source of truth; every sequential view is a pure projection, and canonical positions are never written from sequence geometry.Three ways to use it:
SequentialCanvaswithview="flow" | "sequential"flips the entire graph between cards and a numbered bar stack.display.container.kind: 'runtime'and no inner-boundary handles is a self-contained subflow expanded inline: outer input and output only, no inner rail, no badge, and its sequence body opens with its own Workflow start row.Commits
Each commit is a reviewable unit.
feat: add sequential canvas view. The projection engine (utils/sequential), layout, compatibility report, mutations, and theSequentialCanvascomponent with insert, move, collapse, keyboard navigation, and the flow/sequential toggle.fix: keep parent nodes ahead of children in flow view. React Flow silently unparents a child that precedes its parent in the array.feat: bring sequential bars to parity with cards and harden the view. Review remediation, card-to-bar parity (all adornment corners, footer, placeholders, stacking, attachment chips for tools and resources), a performance pass (identity-stable contexts, one portal layer for insert buttons), shared helpers, and trimmed comments.feat: sequence bodies inside flow containers and add runtime containers. Per-container presentation, the header switch, in-frame numbering and collapse, nested steps, the runtime container, and the insert and drag fixes.How to review
Storybook is the fastest way in:
In code, start with
utils/sequential/projectSequence.tsandprojectContainerSequence.tsfor the model, thencomponents/SequentialCanvas/SequentialCanvas.tsxanduseContainerSequences.tsfor how it reaches React Flow.canvas/README.mdhas a "Sequential view" section with the public API, degraded-graph behavior, and the container limits.Public API
From
@uipath/apollo-react/canvas:SequentialCanvas,SequentialCanvasProps(addssequencedContainerIds,onSequencedContainerIdsChange,collapsedStepIds,readOnlyNodeIds;onAddTriggernow receives an optional container id)ViewSwitcher,useCanvasViewMode,SequentialViewProvider,prepareCanvasViewTransitioncanvas/utils:projectSequence,layoutSequence,analyzeSequentialCompatibility,projectContainerSequence,createSequencePredicates, and the mutation opsisSelfContainedContainerManifest,RUNTIME_CONTAINER_KIND,isArtifactOnlyManifestin the container utilsThe feature is pre-GA; the README says the props may still change.
Changes outside the sequential folder
Worth a closer look, since they touch shared code:
BaseNode: resolution logic moved intouseBaseNodePresentation, shared with the new bar renderer. Card behavior is unchanged, verified line by line.BaseCanvas: applies the parents-before-children repair to every host.AddNodeManager: accepts an explicitly flagged unconnected preview (first step of a self-contained container). Every other preview behaves as before.ContainerNode: renders the body view switch when a supporting canvas provides it, and hides resize handles and the empty state while sequenced.usePreviewNode: handle resolution falls back to the manifest default for genericinput/outputids, and its store selector exits early when no preview is selected.Testing
vitest run src/canvasJsonTree.importtimeout under full-suite load, which passes in isolationpnpm build(including declaration files)Known limits
AddNodeManagerfor its empty-state add, like other flow-view adds.🤖 Generated with Claude Code