fix(site-builder): final whole-branch review fixes

C1: HtmlBlock's PURIFY_CONFIG omitted 'style' from ALLOWED_ATTR, so the
toolbar colour picker added in this branch was silently deleted by
DOMPurify -- issue #2 was regressed, not fixed. Adds style/id plus table
tags, with tests pinning the markup path in both render and toHtml.

I3: PagesPanel's three confirmation states were not mutually exclusive;
cancelling delete revealed an unbidden reset prompt on a destructive action.

I5: orphan repair logged at console.warn, which the new console buffer
cannot see -- the reporter would never capture the most diagnostic signal
for the still-unreproduced drop bug. Also aligns useWhpApi's initial-load
failure handling with loadState's fallback.

I7: corrects comments (and the design spec) that asserted an orphan
"renders somewhere on the canvas", which a mid-plan audit disproved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-09 12:47:23 -07:00
co-authored by Claude Opus 5
parent 3dd6b54a35
commit 69e61ab4b2
12 changed files with 317 additions and 32 deletions
+17 -5
View File
@@ -3,11 +3,23 @@
*
* A node that exists in `SerializedNodes` but appears in no parent's `nodes`
* or `linkedNodes` list is invisible to the Layers tree AND to Craft's own
* selection machinery -- it renders somewhere on the canvas but can't be
* selected or deleted, which is exactly the "dropped outside the page"
* report. Reachability is computed from the PARENT'S child lists, not from
* each node's own `parent` pointer: a stale `parent: 'ROOT'` on a node ROOT
* never lists is precisely the broken case we're looking for.
* selection machinery -- but not because it "renders but can't be selected".
* Craft.js's `<Frame>` only instantiates nodes it can actually walk to via
* `data.nodes`/`linkedNodes` starting from ROOT, so an unreachable node is
* never rendered at all: it doesn't appear on the canvas, it's just inert
* data sitting in the serialized state. Reachability is computed from the
* PARENT'S child lists, not from each node's own `parent` pointer: a stale
* `parent: 'ROOT'` on a node ROOT never lists is precisely the broken case
* we're looking for.
*
* This is a DIFFERENT mechanism from the originally-reported symptom -- a
* *visible* element on the canvas that can't be selected or deleted. That
* symptom requires the node to be both rendered AND excluded from Craft's
* selection/interaction machinery, which is not what an unreachable-from-
* ROOT node produces (it isn't rendered at all). This repair fixes the
* "node is present in state but invisible/unrecoverable" case; the
* originally-reported "visible but unselectable" case is still
* unreproduced and is presumed to be a different bug.
*
* Pure functions over serialized state -- no React, no Craft instance.
*/