FE-1500: Add controlled Petrinaut navigation - #9426
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
5895255 to
e959446
Compare
e959446 to
7d0dac9
Compare
7d0dac9 to
d43067f
Compare
d43067f to
1c3f652
Compare
PR SummaryMedium Risk Overview
Reviewed by Cursor Bugbot for commit fe32d17. Bugbot is set up for automated code reviews on this repo. Configure here. |
956381d to
dd8cf8a
Compare
dd8cf8a to
91c72e6
Compare
91c72e6 to
cda2e98
Compare
cda2e98 to
95fd630
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit eea0f99. Configure here.

🌟 What is the purpose of this PR?
Makes Petrinaut's app location controllable by the host through a new
navigationprop: a router-neutral controller that reads and drives mode, Simulate section and resource, scenario, subnet, canvas selection, and creation drawers, and decides how each change is recorded in history. Without the prop, Petrinaut keeps its location in internal state and behaves as before. Stacked on #9359.🔗 Related links
🔍 What does this change?
react/navigationmodule:PetrinautNavigationState, thePetrinautNavigationControllercontract (state+onNavigatewith updater functions + optionalhistoryPolicy), navigation intents with a push-vs-replace history policy, and helpers such asopenPetrinautSimulationResourceandopenPetrinautSubnet.@hashintel/petrinaut-core:selectionItemTypes(now the source theSelectionItemTypeunion derives from) andcanonicalizeSelectionare exported from a new dependency-free@hashintel/petrinaut-core/selectionentry. Hosts that validate selection outside the app — a URL route, an HTTP handler — get the list and the ordering without bundling the model or React, and the editor's positional selection comparison can no longer disagree with a host's encoding.EditorProviderderives mode, Simulate view, drawer, and selection from navigation state; experiments, optimizations, simulation-scenario, and subnet selection navigate instead of setting local state; invalid locations normalize back with replace-history intents.SimulationCreationDrawerhost replaces the per-view drawer mounts, so opening and closing scenario, metric, experiment, and optimization drawers participates in Back/Forward.drawing-a-net.md,experiments.md,visual-settings.md).EditorProviderno longer seeds navigation-owned fields into its local state; the error banner's "Edit Item" button switches to edit mode even when the erroring item was deleted; removed two unused helper exports.How a host injects its router
Petrinaut never imports a router. A host that wants URL-driven navigation passes a controller object; everything else is unchanged.
Without a router (default) — Petrinaut owns its location in internal state:
With a router — the host owns the location.
stateis decoded from the host's current URL;onNavigatereceives an updater and writes the result back to the URL:onNavigatereceives an updater function rather than a computed state so that several navigations fired in one browser event each apply to the host's freshest state. The optionalhistoryPolicyoverrides the push-vs-replace default per navigation intent; an iframe host can passhistoryPolicy: () => "replace"to keep the embed out of the parent page's history. #9362 wires this contract to TanStack Router for the website's example pages.canonicalizeSelectionorders by UTF-16 code unit instead oflocaleCompare, so the canonical form agrees across locales and ICU builds (the two disagree on ids likeplace-bandplacea); a creation drawer now layers over the record already open instead of clearingsimulateResource, matching how the overlay is preserved on the adjacent line; the optimistic navigation preview is kept with the base it was derived from and expires with its event, which fixes a lost update when an asynchronous host rerenders mid-flight and a declined update swallowing the user's retry (both were written as failing tests first); deep-link normalization filters inside the updater rather than passing a render-time snapshot; the hamburger menu hides Layout on a read-only net, where it silently did nothing; removed the deadonCreatedprop from both create drawers and the__reinitializeaction, which this PR had turned into a partial reset with no caller.Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
🛡 What tests cover this?
react/navigation/index.test.tsx(controlled and uncontrolled providers, optimistic state across same-event updates, duplicate suppression, declined-navigation retries, history policies, selection canonicalization, drawer conversions),react/state/editor-provider.test.tsx(selection gesture phases, react-flow microtask batching, interrupted gestures, deep-link normalization of unknown resources and stale selections),react/simulation/provider.test.tsx(scenario selection canonicalization),simulation-creation-drawer.test.tsx, and navigation assertions in the experiments and optimizations provider tests.❓ How to test this?
yarn workspace @hashintel/petrinaut dev.