fix(designer): share one panel reconciler across sessions - #127
Merged
Conversation
`createPanelReconciler` minted a new `react-reconciler` renderer per panel session with `isPrimaryRenderer: true`. React keeps a context's live value in only two slots — `_currentValue` for the primary renderer, `_currentValue2` for the secondary — so with a screen selected (Variables, States, Layout, Fill) five renderers, react-dom included, all claimed primary and pushed/popped the same context slot. Each popped from its own stack into the shared slot, so a definition could read another session's or a popped default value, and React logged "Detected multiple renderers concurrently rendering the same context provider". There is now ONE process-wide reconciler with `isPrimaryRenderer: false`: per-session state (instance registry, id counter, commit hook) moved onto the container, instances carry a symbol-keyed back-reference so `detachDeletedInstance` evicts from the right registry, and panels read the secondary context slot while react-dom keeps the primary. Safe because `wrap` already re-provides the store, host services, and selection inside the reconciler root — the only contexts definitions read. Also aligns the designer panel chrome with the shared design-system colors and extends the panel wire vocabulary (button `hint`/`width`, toggle group `width`, menu `icon`/`label`/`variant`/`size`, select field `icon`, popover content `title`/`onClose`, align icons), and fixes `popoverTrigger` rendering nothing: Radix `asChild` clones a single element child, but `renderChildren` returned an array of Fragment-wrapped nodes, so the trigger silently disappeared. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Too many files changed for review (133 files, 100 file limit). Bypass the limit by tagging |
|
Thank you for following the naming conventions for pull request titles! 🙏 |
|
Install the packages built from e56c891: @voidhash/paywalls pnpm add @voidhash/paywalls@https://pkg.voidha.sh/paywalls/e56c891@voidhash/node pnpm add @voidhash/node@https://pkg.voidha.sh/node/e56c891@voidhash/web pnpm add @voidhash/web@https://pkg.voidha.sh/web/e56c891voidhash-cli pnpm add voidhash-cli@https://pkg.voidha.sh/cli/e56c891@voidhash/react-native pnpm add @voidhash/react-native@https://pkg.voidha.sh/react-native/e56c891Internal workspace deps (lib, generated-clients, shared, studio) are published at the same sha and resolved automatically. Replace the sha with |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Editing properties in the designer right panel silently did nothing — background colour on a screen node, border radius on a view, the absolute-positioning toggle. The only symptom in the browser was a React warning:
Root cause
createPanelReconcilercalledcreateReconciler()once per panel session, withisPrimaryRenderer: true. Every call mints a distinct React renderer, and React stores a context's live value in exactly two slots —_currentValuefor the primary renderer,_currentValue2for the secondary.With a screen selected there are four built-in panels (Variables, States, Layout, Fill), so five renderers — react-dom plus four panel reconcilers — all claiming primary and all pushing/popping
PaywallStoreContext,PanelHostServicesProvider, andDefinitionSelectionProvideron the same slot. Each renderer pops from its own stack into a shared slot, so a definition can read another session's — or a popped, default — context value.Fix
One process-wide reconciler with
isPrimaryRenderer: false:PanelContainer, so the host config can be shared.createPanelReconciler()keeps its signature and just mints a fresh container.detachDeletedInstanceonly receives the instance, so instances carry aSymbol-keyed back-reference to their owning container to evict from the right registry.isPrimaryRenderer: falseputs panels on_currentValue2and leaves_currentValueto react-dom. Safe becausewrapalready re-provides everything inside the reconciler root — every definition reads onlyPaywallStoreContext, host services, and the selection store.Also in this PR
hint/width, toggle groupwidth, menuicon/label/variant/size, select fieldicon, popover contenttitle/onClose, align icons.popoverTriggerfixed — RadixasChildclones a single element child, butrenderChildrenreturned an array of Fragment-wrapped nodes, so the trigger rendered nothing.host-renderer.test.tsxun-quarantined (+251 lines of coverage); designer suites get a 20s test timeout.fix: separate community analytics surfacescommit, which was stranded on an orphan root commit in this worktree and has been re-parented ontomain.Verification
libraries/paywalls: 185/185 tests pass.BuiltinPanelHosts under one react-dom store provider) emitted the warning five times before, none after.@voidhash/paywallsis pre-bundled by Vite, so restart the dev server with--force.🤖 Generated with Claude Code