FE-1129: Token encoding playground (Storybook)#8943
Conversation
Exposes coerce/encode/decode/default token attribute helpers and compileUserCode so the UI package can reuse the exact product encoding pipeline; replaces the duplicated default-value switches in the spreadsheet and initial-state editor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dev-only playground for the simulation token buffer encoding: a store-free dimension editor, a Monaco value editor typed against generated Token declarations (opts into Monaco's TS language service locally — the shared provider only ships the SDCPN LSP), and a bit-level memory view with IEEE-754 anatomy shading, round-trip display, and little-endian byte order toggle. The memory view is built against the planned format-v2 layout abstraction (per-field physical types, alignment ordering, stride padding), so the shipped v1 all-f64 encoding and the planned v2 packed layout (u8 booleans) can be compared side by side. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One byte (8 bits) per row grouped by slot; static legend on the left; per-slot details (offset, hex, round-trip) in a side panel shown on hover; hovering a row or legend chip highlights the whole slot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… cf/token-encoding-playground
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
PR SummaryLow Risk Overview Adds a Dev / Token Encoding Playground Storybook story under Reviewed by Cursor Bugbot for commit 59a88b8. Bugbot is set up for automated code reviews on this repo. Configure here. |
| allowNonTsExtensions: true, | ||
| }); | ||
| typescriptDefaults.setEagerModelSync(true); | ||
| } |
There was a problem hiding this comment.
Monaco TS never torn down
Medium Severity
The playground enables Monaco’s TypeScript worker and setExtraLibs via module-level state, but nothing restores MonacoEnvironment or clears extra libs when the story unmounts. After visiting the playground once in Storybook, other typescript editors in the same session can keep the patched worker and playground typings.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit da4b714. Configure here.
| dimensions: readonly PlaygroundDimension[], | ||
| ): string { | ||
| const properties = dimensions | ||
| .filter((dimension) => IDENTIFIER_RE.test(dimension.name)) |
There was a problem hiding this comment.
Invalid dimension names desync layout
Medium Severity
generateTokenDefs drops dimensions whose names are not valid identifiers, but evaluate still passes the full dimension list into computeTokenLayout. Renaming a field to a non-identifier leaves the memory view encoding that slot while the Monaco Token type and sample code no longer mention it, so displayed bits and coercion no longer match what the editor shows.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit da4b714. Configure here.
There was a problem hiding this comment.
Pull request overview
Adds a dev-only Storybook playground in @hashintel/petrinaut for inspecting how typed token values are coerced/encoded into simulation buffers, and moves the “token value codec” + compileUserCode to public exports in @hashintel/petrinaut-core so UI surfaces can reuse the canonical defaults/encoding logic.
Changes:
- Exported token value codec helpers and
compileUserCodefrom@hashintel/petrinaut-core(with a changeset) and reuseddefaultTokenAttributeValuein UI editors. - Added a new Storybook “Dev / Token Encoding Playground” with a dimensions editor, Monaco-based typed token editor, and a byte/bit memory visualization for v1 vs planned v2 layouts.
- Added unit coverage for layout/padding, encode/decode round-trips, and IEEE-754 bit extraction.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/initial-state-editor.tsx | Reuses defaultTokenAttributeValue from core instead of local defaults logic. |
| libs/@hashintel/petrinaut/src/ui/components/spreadsheet.tsx | Reuses defaultTokenAttributeValue for spreadsheet default cell values. |
| libs/@hashintel/petrinaut-core/src/index.ts | Re-exports token codec helpers + compileUserCode from the core package entrypoint. |
| .changeset/token-codec-exports.md | Versions the public API change (core minor, petrinaut patch). |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/token-encoding-playground.tsx | Implements the playground UI and debounced evaluation pipeline. |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/token-memory-view.tsx | Renders encoded token bytes/bits with hover-driven field detail. |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/physical-layout.ts | Computes v1/v2 layouts and encodes/decodes tokens via the real core codec. |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/dimension-editor.tsx | Store-free dimension/type editor for the playground. |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/playground-monaco.ts | Adds Story-scoped Monaco TypeScript contribution wiring + token type defs generation. |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/monaco-typescript.d.ts | Declares minimal types for Monaco TS contribution APIs used by the playground. |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/token-encoding-playground.stories.tsx | Registers the Storybook story under “Dev / Token Encoding Playground”. |
| libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/physical-layout.test.ts | Adds unit tests for layout, round-trips, and bit/hex inspection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function enableTypescriptLanguageService(): void { | ||
| if (environmentPatched) { | ||
| return; | ||
| } | ||
| environmentPatched = true; | ||
|
|
||
| const previousEnvironment = window.MonacoEnvironment; | ||
| window.MonacoEnvironment = { | ||
| getWorker(workerId: string, label: string) { |
| use(use(MonacoContext)); | ||
| // Both calls are idempotent and MUST run before the editor's `typescript` | ||
| // model is created below (worker routing + extra libs). | ||
| enableTypescriptLanguageService(); | ||
| setPlaygroundTokenDefs(defs); |
| groups.push({ | ||
| key: `field-${field.name}`, | ||
| field, | ||
| fieldIndex, | ||
| startByte: field.byteOffset, |
| <span | ||
| key={field.name} | ||
| className={legendChipStyle} | ||
| onMouseEnter={() => setHoveredKey(`field-${field.name}`)} | ||
| onMouseLeave={() => setHoveredKey(null)} |
… cf/token-encoding-playground
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 59a88b8. Configure here.
| onChange={(next) => onChange(next ?? "")} | ||
| options={{ minimap: { enabled: false }, scrollBeyondLastLine: false }} | ||
| /> | ||
| ); |
There was a problem hiding this comment.
Monaco TS never torn down
Medium Severity
PlaygroundEditor enables Monaco’s TypeScript worker, compiler options, eager model sync, and playground extra libs during render but never reverses that on unmount. Module-level environmentPatched keeps the patched MonacoEnvironment and TS defaults for the whole Storybook session, so leaving the playground can leave TS checking active on other typescript editors (including LSP stories).
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 59a88b8. Configure here.


🌟 What is the purpose of this PR?
Adds a dev-only Storybook playground for inspecting how token values are encoded into the simulation frame buffers: define a colour's dimensions, author a token value in a typed Monaco editor, and see the exact bits stored — with coercion round-trips made visible (
input 2.7 → stored 3 → reads back 3).It doubles as the visual testbed for the packed-struct token layout (format v2, follow-up PR), and lands a small enabler: the token value codec is now exported from
@hashintel/petrinaut-coreinstead of being duplicated in UI components.🔗 Related links
🚫 Blocked by
🔍 What does this change?
coerceTokenAttributeValue,coerceTokenRecord,encodeTokenAttributeValue,decodeTokenAttributeValue,decodeTokenRecord,defaultTokenAttributeValue) andcompileUserCodefrom@hashintel/petrinaut-core; the spreadsheet and initial-state editors reusedefaultTokenAttributeValueinstead of local switches.src/ui/dev/token-encoding-playground/with a Storybook story (Dev / Token Encoding Playground):export default Token(() => ({ … }))) typed against declarations generated live from the dimensions;compileUserCode+ the real codec), debounced, with errors displayed inline.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:
compileUserCodepipeline and same-realm sandboxing posture as the product's code surfaces) — acceptable for a dev tool, not a hardened boundary.🐾 Next steps
🛡 What tests cover this?
libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/physical-layout.test.ts(layout ordering/padding/stride, encode/decode round-trips, IEEE-754 bit extraction)test:unit,lint:tsc(tsgo),lint:eslint(oxlint) for both petrinaut packages❓ How to test this?
yarn devinlibs/@hashintel/petrinaut, open Dev / Token Encoding Playground.Tokentype updates live; wrong-typed values get squiggles.count: 2.7— the memory view shows the stored value3and the round-trip line.📹 Demo
The story itself is the demo — screenshots in the Storybook story render exactly what reviewers will see locally.