Skip to content

FE-874: Expand subnets in place with automatic layout (prototype)#8954

Draft
kube wants to merge 1 commit into
mainfrom
cf/fe-874-expand-subnets-in-place-with-automatic-layout
Draft

FE-874: Expand subnets in place with automatic layout (prototype)#8954
kube wants to merge 1 commit into
mainfrom
cf/fe-874-expand-subnets-in-place-with-automatic-layout

Conversation

@kube

@kube kube commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Prototype for expanding a subnet's component instance in place on the canvas (FE-874). Double-clicking an instance box turns it into a frame showing the subnet's internal structure (auto-laid-out with the existing ELK pipeline), and the surrounding net is offset at display time to make room. Double-clicking the frame collapses it again.

Expansion is pure view-layer state — stored x/y positions are never mutated. Displayed positions are stored + deterministic shift; drag commits map back through the inverse shift, so undo/redo and collaboration stay consistent. A design doc covering the approach, alternatives considered (model flattening, full-net re-layout, force-based displacement, true nested editing), limitations, and proposed next steps is included at libs/@hashintel/petrinaut/dev-docs/fe-874-expand-subnets-in-place.md.

🔗 Related links

🚫 Blocked by

  • Nothing

🔍 What does this change?

  • New useExpandedSubnets hook: per-instance expansion state, ELK layout of the subnet content at expand time, and the "insert space" displacement math (computeExpansionShift + inverse).
  • New componentInstanceExpanded node type + ExpandedComponentInstanceNode frame component (header with name/subnet, dashed border).
  • useSdcpnToReactFlow: emits the frame + namespaced (instanceId::elementId) read-only child nodes via React Flow subflows (parentId), applies display-time displacement to surrounding nodes, rewires componentPort arcs directly to the port places inside expanded frames, and renders the subnet's internal arcs.
  • useApplyNodeChanges: optional displayed→stored position mapping so drags while expanded commit undisplaced coordinates.
  • SDCPNView: onNodeDoubleClick expand/collapse, guards so expanded children can't be selected/connected/hovered as real items, expansion reset on net switch/drill-down.
  • Storybook fixture + story "Subnets — expand in place (FE-874 prototype)" with two instances of the same Warehouse subnet.
  • Design doc under libs/@hashintel/petrinaut/dev-docs/.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • modifies an npm-publishable library and I have added a changeset file(s)

📜 Does this require a change to the docs?

The changes in this PR:

  • are in a state where docs changes are not yet required but will be
    • this is tracked in: FE-874 — the user guide (libs/@hashintel/petrinaut/docs/, read by the in-app AI assistant) must be updated before the interaction ships as final UX

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • Expansion/collapse snaps (no animation yet).
  • Double-click also selects the instance (opens the properties panel), and React Flow's default zoomOnDoubleClick zooms when the gesture misses a node — both should be addressed for final UX.
  • Elements inside an expanded frame are a deliberately read-only projection (no select/drag/connect).
  • Simulation state (token counts, firing) is not yet projected onto expanded children.
  • Dragging a node across an expanded frame's centre axis lands slightly off (inverse shift uses the pre-drag stored position); snap-to-grid applies to displayed coordinates while expanded.
  • Nested component instances render collapsed inside a frame; recursive expansion isn't supported yet.

See the "Known limitations / follow-ups" section of the design doc for the full list.

🐾 Next steps

  • UX review of the prototype (gesture, affordance, animation, displacement feel).
  • Productionize: move displacement/frame-layout math into petrinaut-core, unit-test the shift/inverse round-trip, disable zoomOnDoubleClick, suppress select-on-expand.
  • Milestone 2: editable children writing back to the subnet definition.
  • Milestone 3: map flattened simulation ids back to expanded children.
  • Update user docs + AI-assistant doc registry; Visualizer follow-up ticket (per Linear thread).

🛡 What tests cover this?

  • Existing petrinaut unit tests pass (131). No new automated tests yet — the displacement math is a prime candidate for unit tests when productionized (listed in next steps).
  • Manually verified with Playwright against the Storybook story: expanding one/both instances of the same subnet, drag-commit round-trip (exactly two model patches, no snap-back), collapse restoring stored positions.

❓ How to test this?

  1. cd libs/@hashintel/petrinaut && yarn dev
  2. Open the story Petrinaut → Subnets — expand in place (FE-874 prototype)
  3. Double-click WarehouseNorth → it expands into its internal structure; the rest of the net moves to make room
  4. Double-click WarehouseSouth too → both instances of the same subnet are expanded independently
  5. Drag root nodes around while expanded, then double-click the frame headers to collapse → the net returns to consistent positions (check the patch log overlay: drags commit stored, undisplaced coordinates)

📹 Demo

Expanded frame showing the subnet's internal structure in place (see Storybook story; screenshots in the Linear ticket thread).

🤖 Generated with Claude Code

Double-clicking a component instance expands it in place: the box becomes
a frame containing the subnet's internal places/transitions (laid out with
the existing ELK pipeline), and the surrounding net is displaced at display
time to make room. Double-clicking the frame collapses it again.

Expansion is pure view-layer state — stored x/y positions are never
mutated. Displayed positions are `stored + deterministic shift`, and drag
commits map back through the inverse shift so the model always stores
undisplaced coordinates. Child node/edge ids are namespaced by instance id
so multiple instances of the same subnet expand independently. Arcs into
ports reattach directly to the port places inside the frame while expanded.

Includes a Storybook fixture/story ("Subnets — expand in place") and a
design doc (dev-docs/fe-874-expand-subnets-in-place.md) covering the
approach, alternatives considered, limitations, and next steps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment Jul 4, 2026 12:57am
petrinaut Ready Ready Preview, Comment Jul 4, 2026 12:57am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Jul 4, 2026 12:57am

@github-actions github-actions Bot added area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team area/apps > hash.design Affects the `hash.design` design site (app) labels Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps > hash.design Affects the `hash.design` design site (app) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

1 participant