…leaves
`registerStudioCanvasPreview` opts a type into a canvas that renders the
running app, not an editable draft — `StudioCanvasPreviewProps` carries no
`selection`/`onSelectionChange`/`onPatch`/`editing` by contract. Two
affordances beside such a leaf ignored that: the Design/Run switch was offered
though nothing reads the mode there, and the rail invited the author to click
blocks that cannot exist.
Gated on `StudioCanvas` — the same value that selects the canvas branch — and
deliberately not on `isEditable`, which is a conjunction that would also reach
the no-designer leaves pinned by part C of the earlier registry card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
Fixes#7121
The Interfaces pillar offered Design mode and a "Click a block on the canvas, and edit its properties right here." rail beside studio-canvas leaves that have no block canvas at all. Both instructions are impossible to follow there.
Leg 0 — reproduced on current
origin/main, NOT already repaired by #7120Measured on
220c18d05(which contains #7120 as994b73696), with the designer registry populated — the card's precondition:The canvas beside that rail is honest — it carries "Runtime list preview · edit fields / structure in the Data pillar" — while the rail contradicts it. So this is the ungated affordance, not the empty-registry class #6795 part C repaired.
Leaf population — enumerated before editing
registerStudioCanvasPreviewcall sites across the whole repo:studio-canvas-preview.tsx:97(built-in default)objectstudio-canvas-preview.test.tsx:43,48object(override, restored)Population: one (
object), confirmed at runtime bylistStudioCanvasPreviewTypes() === ["object"]. The registry is a public extension point, so the gate is written against the registry, not against the stringobject.isEditableis a proxy, and gating on it would have broken a landed pinThe card sketched gating on
isEditable. That is not the right discriminator, and this is measured rather than argued.isEditable = !!Preview && !StudioCanvas— a conjunction of two independent causes. It is false both for a studio-canvas leaf and for a leaf whose own type simply has no designer. The second is the state #6795 part C pinned as still deserving the ordinary rail, inStudioDesignSurface.designerRegistryPartial.test.tsx.Ablation, direction predicted before running — swap
StudioCanvasforisEditableat both gates, then run that landed pin. Predicted RED, observed RED:Restore proven by state: blob hash back to
5209c038d…,git diff HEADempty,git diff --cachedempty,!StudioCanvasgate count back to 1.⇒ the gate is
StudioCanvas— the same value that selects the canvas branch. Blast radius is exactly the studio-canvas leaves.The repair
Three surfaces in
StudioDesignSurface.tsx(InterfacesPillar), all reached through the sharedcanvasEl/inspectorBodyEl, so all three layouts — classic, folded-wide, folded-tabs — are covered by one change.!StudioCanvas.editingis handed to exactly one canvas branch (Preview);StudioCanvasPreviewPropscarries noeditingby contract, so the switch movedcanvasModeand reached no renderer.selectionbranch, and the header's "clear selection" button is gated with it. Without this, my own repair would have left the rail saying "no blocks here" beside a button offering to clear a selection.That third item is a fix for a defect I measured while here:
blk_1is a block on a different leaf's canvas —selectionoutlives a leaf change because the load effect'ssetSelection(null)sits inside theisEditableearly-return. The rail symptom is closed here; the underlying state leak reaches every non-editable leaf, so it is filed separately as #7137 rather than swept in.New string
engine.studio.inspector.studioCanvasNoBlocks, added toenandzh.engine.*strings do not live inpackages/i18n/src/locales/and are not covered byall-locales-key-parity. They are a flat dotted-key table withenandzhonly, inviews/metadata-admin/i18n.ts, whose header states the carve-out and whichcheck-i18n-call-site-keys.mjsskips by declaration.check:i18n-driftconfirms the packs did not move: "0 en value(s) changed".Pins
StudioDesignSurface.studioCanvasLeaf.test.tsx— 7 tests. Four pin the studio-canvas leaf; three are the regression fence on a leaf that does have a block canvas, which matters more: the switch is still offered and still round-trips (editingtrue → false → true, #5800's acceptance), the ordinary "click a block" rail is untouched, and the selection affordances still work.Ablation, direction predicted before running — revert the source fix, keep the tests. Predicted RED on the four, green on the fence:
Restore proven by state: blob back to
5209c038d…,git diff HEADandgit diff --cachedboth empty, markers back to 3 / 1.Gates — all run on
e462194d7, the final commitvitest packages/app-shell/src/views/studio-design/+ locale parityTest Files 43 passed (43) · Tests 257 passed (257)pnpm --filter @object-ui/app-shell run type-checktsc --noEmit && tsc -p tsconfig.test.json, 0error TSeslint(plain form, 3 changed files)220c18d05, none inside my hunks; new test file andi18n.tscontribute 0check-changeset-presence✅ 3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)check-changeset-fixed✅ All workspace packages are in the changeset fixed group.check-changeset-no-major✅ No changeset declares a major bump.check:i18n-keysEvery in-scope call-site key resolves against the en pack (2845 keys)check:i18n-driftNo en value changed in this range.check:control-bytes✅ OK (scanned 5909 tracked text file(s))check-vi-mock-specifiers✅ OK (771 relative specifier(s) resolved)vitest-invocation-guardcheck-designer-field-key-paritydesigner-field-key-parity: OKcheck-lucide-icon-record-namesOK lucide icon names: 182 …Gate set derived from the repo, not guessed: only
check-i18n-call-site-keys.mjsreferencesstudio-design/StudioDesignSurface/metadata-admin/i18namongscripts/*.mjs(control: 5 scripts do referencepackages/app-shell, so that zero is a reading). The remaining rows are the app-shell-reading gates plausibly touched by a JSX + i18n diff.typecheckgenuinely covers the new test:tsc -p tsconfig.test.json --listFileslistsstudioCanvasLeaf.test.tsx(1 hit; controlStudioDesignSurface.tsxalso 1; 4508 files total) — so the secondtscinvocation is not excluding tests.Observation, not changed here
The canvas hint below the grid is gated
!isEditable && current?.type === 'object'— hardcoded to the type rather than to the registry. A secondregisterStudioCanvasPreviewleaf would get the gating in this PR but no hint, because the hint's wording ("edit fields / structure in the Data pillar") is object-specific and generalising it is a wording decision. Latent only: population is 1 today. Left for triage.Base
Branched from
220c18d05;origin/mainhas since moved to2b3964aff. All measurements above are one462194d7.Generated by Claude Code