Skip to content

FE-1486: Add the ad-hoc initial state and parameters form - #9285

Draft
kube wants to merge 8 commits into
cf/fe-1485-ad-hoc-scenario-synthesis-in-petrinaut-corefrom
cf/fe-1486-ad-hoc-initial-state-and-parameters-form-component
Draft

FE-1486: Add the ad-hoc initial state and parameters form#9285
kube wants to merge 8 commits into
cf/fe-1485-ad-hoc-scenario-synthesis-in-petrinaut-corefrom
cf/fe-1486-ad-hoc-initial-state-and-parameters-form-component

Conversation

@kube

@kube kube commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

The visual half of the ad-hoc scenario feature (FE-778): the shared form for defining Initial State + Parameters inline, built to the ratified prototype (the "shared column values" variation) over the FE-1485 (#9284) shapes. Nothing is wired into a view yet — the three consumers follow as FE-1487/FE-1488/FE-1489 in stack #9286.

🔗 Related links

🔍 What does this change?

New component ui/components/ad-hoc-scenario-form/ in @hashintel/petrinaut, following the prototype's anatomy:

  • The token table (token-table.tsx): one column per colour element; the row gutter cycles the row's kind — Fixed (#n) → Dynamic (i, blue tint) → count-Optimized (purple tint) → Fixed — retaining the count across the cycle; dynamic rows carry a quiet strip above their cells (× scenario.n_satellites, or × 1 … 24 when the count is optimized); a phantom trailing row materializes into a fixed row on click and opens the clicked cell's editor; the place total renders at the bottom, resolved to a number unless a count is (transitively) optimized.
  • Shared columns: column headers are share toggles (aria-pressed); a shared column takes a subtle wash (a background image, so it composites under the row tints), its value slot sits directly below the header, and the cells beneath render dimmed, chevron-prefixed, out of the tab order, with clicks routed to the shared value's editor.
  • The value editor (value-editor.tsx): every slot opens an anchored popover titled with the slot's attribution path (Space › item 0 › x), hosting a Monaco single-line expression editor bound to the form's LSP session — completion and inline type markers included. The Optimize toggle sits bottom-right behind a thin border; turning it on replaces the expression editor with Min/Max/Scale (Step only on integers other than counts) in the same slot, and turning it off restores the expression, bounds retained.
  • Diagnostics on closed slots: the form runs its own ad-hoc LSP session (use-ad-hoc-lsp-session.ts) and a synthesis dry-run per change; a closed slot with a problem underlines in red and carries the first synthesis error or LSP diagnostic as its tooltip, joined through adHocSlotKey. With the default no-op language client (Storybook, tests) everything degrades silently.
  • Variables render as compact joined rows (name · value slot · type · Optimize · remove) added from icon-only header buttons; an empty list renders nothing. Net parameters are exposed directly with default (…) placeholders; uncoloured places are a single count slot.
  • An optimizable prop removes every Optimize control — the same component serves quick simulation and plain experiments.

Fixes in passing: code-editor.tsx's single-line height, placeholder offset, and font utilities were interpolated constants Panda's extractor cannot resolve, so the utilities were silently missing and single-line editors collapsed to a hairline (visible in the experiment drawer's parameter rows too); the values are now literal.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

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

📜 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:

  • do not affect the execution graph.

🛡 What tests cover this?

Seven jsdom tests (Monaco mocked): phantom-row materialization, the full gutter cycle with count-optimize defaults, column sharing/unsharing with retention and aria-pressed, Optimize controls absent when optimizable is off, uncoloured count editing, a synthesis error rendered on the closed slot's trigger, and the unresolved place total.

❓ How to test this?

  1. turbo run test:unit --filter @hashintel/petrinaut
  2. turbo run dev --filter @hashintel/petrinaut → Components / AdHocScenarioForm. Six stories: the Satellites Launcher model (plain, non-optimizable, optimized, shared column), a two-field model, and an empty net.

🐾 Next steps

FE-1487 wires quick simulation, FE-1488 the experiment drawer, FE-1489 the optimization drawer.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 22, 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 Aug 24, 2026 1:55am
petrinaut Ready Ready Preview Aug 24, 2026 1:55am
petrinaut-docs Ready Ready Preview Aug 24, 2026 1:55am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Aug 24, 2026 1:55am

@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 Aug 22, 2026
@kube
kube force-pushed the cf/fe-1486-ad-hoc-initial-state-and-parameters-form-component branch from 57ec861 to f4f987c Compare August 23, 2026 14:18
@kube
kube force-pushed the cf/fe-1486-ad-hoc-initial-state-and-parameters-form-component branch from 01473ec to 4d3338d Compare August 23, 2026 15:42
kube added 5 commits August 24, 2026 02:39
The shared component over FE-1485's state model. Coloured places render a
token spreadsheet: one column per colour element, a gutter reading #n for
fixed rows and i for template rows (each template's count in a strip
beneath, its own Optimize included), and clickable column headers that
share one value for the whole column — cells beneath render derived and
out of the tab order, and clicking one opens the shared editor. Variables
render at both scopes; net parameters are exposed directly; uncoloured
places are one count slot.

Every value slot opens the same anchored editor: an expression input, and
where optimization is available an Optimize toggle that swaps the
expression for Min/Max/Scale (and Step on integers) in place, backed by
the core transitions so nothing is lost on toggle. An optimizable prop
removes the Optimize surface entirely for the plain-simulation consumers.

Storybook stories for both modes and an empty net; jsdom tests pin row
mixing, share/un-share retention, the optimizable flag, and expression
editing. No consumer is wired yet.
The token table follows the shared-column-values prototype: the row gutter cycles Fixed → Dynamic (blue) → count-Optimized (purple), dynamic rows carry a quiet strip showing the count or its bounds, column headers toggle shared values with a wash under the row tints and derived cells routing clicks to the shared editor, a phantom trailing row materializes on click, and the place total renders at the bottom. Every value slot opens a Monaco single-line editor bound to the form's ad-hoc LSP session; closed slots underline with the first synthesis error or diagnostic as their tooltip. Variables render as compact joined rows added from icon-only header buttons.
Panda's extractor cannot resolve interpolated constants, so the single-line editor's height, placeholder offset, and font utilities were silently missing; the values are now literal. The popover hosts the editor in a row so its flex: 1 keeps meaning width. Stories wrap the form in MonacoProvider and the header share-toggles restore the text box the Tooltip wrapper's zero line-height collapsed.
Variable-name errors render on the name input instead of an undisplayed slot. Boolean slots optimize coherently everywhere as a true/false choice with no bounds. The gutter cycle skips the count-Optimized stage in consumers without Optimize. Clicking the phantom row in a shared column routes to the shared value's editor instead of auto-opening a derived cell. updateCell pads rows shorter than the table so edits land.
The token table, Variables, and Parameters share the token spreadsheet's visual grammar: a bordered table with 28px rows, hairline delimitations, square type-select cells, and full-width clickable value cells. Cells navigate with arrows and Tab. The gutter opens a row-kind menu (Fixed / Dynamic count / Optimized count) instead of cycling. The value editor opens in place: a Monaco single-line editor at exactly the cell's position, the slot's path floating quietly above and the labeled purple Optimize pill below. Uncoloured places show their count on the place-name line, and the TOKENS subtitle is gone. Stories run the real language client, so type-checking works end to end in Storybook.
The overlay portals into the PortalContainerContext container, staying inside the Panda scope (document.body loses every token). A pointer click selects the cell; a second click, a double-click, or Enter opens the editor, and opening one editor closes any other. The floating path label and Optimize control fade in; the editor itself shows immediately.
kube added 2 commits August 24, 2026 03:35
… delete

The floating path and Optimize bars span the editor's full width and sit flush against it, sharing its focus ring. Opening the editor focuses the expression editor (or the Min bound). The type Select neutralizes its base-input box to render as a plain cell. Variables and Parameters navigate with arrow keys like the token table. Row deletion is a quiet trashcan that appears on row hover or focus.
…page

AdHocFormContext now carries the form state, the net context, and the Optimize availability, so per-slot components hold only their own value and location; the value editor computes its own attribution label from its target, and a derived cell joins to its column's slot so the shared value's problems surface where the user looks. The folder declares the ui.adhoc-form layer, and an authored architecture page documents the three shapes, the slot join, the type-checking loop, the vocabulary, the component split, and the invariants.
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 > backend Owned by the @backend team type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

1 participant