Skip to content

fix(designer): share one panel reconciler across sessions - #127

Merged
KingDoxik merged 2 commits into
mainfrom
t3code/align-designer-colors
Aug 11, 2026
Merged

fix(designer): share one panel reconciler across sessions#127
KingDoxik merged 2 commits into
mainfrom
t3code/align-designer-colors

Conversation

@KingDoxik

Copy link
Copy Markdown
Contributor

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:

Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.

Root cause

createPanelReconciler called createReconciler() once per panel session, with isPrimaryRenderer: true. Every call mints a distinct React renderer, and React stores a context's live value in exactly two slots — _currentValue for the primary renderer, _currentValue2 for 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, and DefinitionSelectionProvider on 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:

  • Per-session state (instance registry, id counter, commit hook) moved from the closure onto PanelContainer, so the host config can be shared. createPanelReconciler() keeps its signature and just mints a fresh container.
  • detachDeletedInstance only receives the instance, so instances carry a Symbol-keyed back-reference to their owning container to evict from the right registry.
  • isPrimaryRenderer: false puts panels on _currentValue2 and leaves _currentValue to react-dom. Safe because wrap already re-provides everything inside the reconciler root — every definition reads only PaywallStoreContext, host services, and the selection store.

Also in this PR

  • Designer panel chrome aligned with the shared design-system colours.
  • Panel wire vocabulary extended: button hint/width, toggle group width, menu icon/label/variant/size, select field icon, popover content title/onClose, align icons.
  • popoverTrigger fixed — Radix asChild clones a single element child, but renderChildren returned an array of Fragment-wrapped nodes, so the trigger rendered nothing.
  • host-renderer.test.tsx un-quarantined (+251 lines of coverage); designer suites get a 20s test timeout.
  • Carries the earlier fix: separate community analytics surfaces commit, which was stranded on an orphan root commit in this worktree and has been re-parented onto main.

Verification

  • libraries/paywalls: 185/185 tests pass.
  • Designer suite: 628/628 across 66 files.
  • Build and typecheck clean.
  • Repro (five BuiltinPanelHosts under one react-dom store provider) emitted the warning five times before, none after.

⚠️ The renderer collision is a definite defect and was the only anomaly present, but it could not be made to reproduce the no-op edit in jsdom — the repros committed correctly despite the warning. Worth confirming in the running app; @voidhash/paywalls is pre-bundled by Vite, so restart the dev server with --force.

🤖 Generated with Claude Code

KingDoxik and others added 2 commits August 11, 2026 10:02
`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>
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Too many files changed for review (133 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@github-actions

Copy link
Copy Markdown

Thank you for following the naming conventions for pull request titles! 🙏

@github-actions

Copy link
Copy Markdown

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/e56c891

voidhash-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/e56c891

Internal workspace deps (lib, generated-clients, shared, studio) are published at the same sha and resolved automatically.

Replace the sha with pr-127 or the branch name to always get the latest build of this PR.

@KingDoxik
KingDoxik merged commit 9875c5d into main Aug 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant