Skip to content

Fix two reconciler bugs surfaced by Step 6 smoke testing#22

Merged
avanelsas merged 1 commit intomainfrom
fix/reconciler-raw-html-and-named-lengths
May 1, 2026
Merged

Fix two reconciler bugs surfaced by Step 6 smoke testing#22
avanelsas merged 1 commit intomainfrom
fix/reconciler-raw-html-and-named-lengths

Conversation

@avanelsas
Copy link
Copy Markdown
Owner

Summary

Two pre-existing reconciler bugs surfaced while smoke-testing PR #21
(Step 6 inspector field-as-data). Both pre-date that refactor —
verified by tracing the original closure call paths to the same
broken outcomes. Held off PR #21 to keep its scope clean
(inspector field-as-data only); landing here as a focused fix
PR alongside.

One commit, all four CI gates green, 739 tests passing
(734 baseline + 5 new).

Bugs fixed

1. Stale SVG markup after clearing inspector field — canvas.cljs

patch-element! derived raw-html? from (some? (:inner-html node))
of the new node. When the user cleared an x-icon's SVG editor,
the new value became nil → raw-html? flipped to false → the
(when raw-html? (rec/set-inner-html! …)) branch was skipped →
the live DOM kept the old innerHTML. Visible as: clearing the
inspector textarea seemed to do nothing on the canvas.

Fix: derive raw-html? from the tag's :raw-html-slot? meta
(the meta/augment flag that distinguishes raw-html-slot tags
like x-icon from structured-slot tags). The reconciler now
manages inner-html for the lifetime of any raw-html-slot node,
regardless of whether the current value is set or cleared.

2. Bare numeric width / height ignored — reconcile.cljs

layout->css emitted named width / height via raw (str "width:" v)
without unit coercion. Typing 200 into an inspector layout
field landed in the doc as the string "200", the reconciler
emitted width:200;, the browser dropped it as invalid CSS.
Only 200px worked.

Meanwhile the :free placement's x/y/w/h have always gone
through as-length which adds px to numeric number values,
but as-length itself didn't recognise bare numeric strings.

Two coordinated fixes:

  • as-length now recognises a bare numeric string (matched by
    numeric-string-re — optionally negative integer or decimal)
    and applies the same px default the number branch uses.
    Unit'd strings (50%, 10rem, auto) still pass through.
  • layout->css's named branch routes width / height through
    as-length, so width \"200\"width:200px; and
    width 200width:200px; both produce valid CSS. Padding /
    margin keep their string-only behaviour because their multi-
    value forms (10px 20px, 1em 2em 3em 4em) don't fit
    as-length's single-value contract — left for a future
    variant if needed.

Test plan

  • clj-kondo --lint src test scripts — 0 errors, 0 warnings
  • cljfmt check — clean
  • npx shadow-cljs compile test — 739 tests, 0 failures, 0 errors
  • npx shadow-cljs release app — 0 warnings
  • Manual: drag x-icon onto canvas, paste an SVG, clear the
    textarea — icon should disappear (regression-tests bug 1).
  • Manual: select any element, type 200 in the layout width
    field — width should apply (regression-tests bug 2). Then
    type 200px, 50%, 10rem — all should still work.

Related

🤖 Generated with Claude Code

Both pre-date the inspector field-as-data refactor (verified by
tracing the original closure call paths to the same broken
outcomes). Surfaced when smoke-testing PR #21 against `x-icon` and
the layout dimension fields.

## 1. Stale SVG markup after clearing inspector field (canvas.cljs)

`patch-element!` derived `raw-html?` from `(some? (:inner-html node))`
of the *new* node. When the user cleared an x-icon's SVG editor,
the new value became nil → `raw-html?` flipped to false → the
`(when raw-html? (rec/set-inner-html! …))` branch was skipped →
the live DOM kept the old innerHTML. Visible as: clearing the
textarea in the inspector seemed to do nothing on the canvas.

Fix: derive `raw-html?` from the tag's `:raw-html-slot?` meta
(the `meta/augment` flag that distinguishes raw-html-slot tags
like x-icon from structured-slot tags). The reconciler now manages
inner-html for the lifetime of any raw-html-slot node, regardless
of whether the current value is set or cleared.

## 2. Bare numeric width / height ignored (reconcile.cljs)

`layout->css` emitted named width / height via raw `(str "width:" v)`
without unit coercion. Typing `200` into an inspector layout field
landed in the doc as the string `"200"`, the reconciler emitted
`width:200;`, the browser dropped it as invalid CSS. Only `200px`
worked. Meanwhile the `:free` placement's x/y/w/h have always
gone through `as-length` which adds `px` to numeric *number*
values, but `as-length` itself didn't recognise bare numeric
*strings*.

Two coordinated fixes:

  - `as-length` now recognises a bare numeric string (matched by
    `numeric-string-re` — optionally negative integer or decimal)
    and applies the same `px` default the number branch uses.
    Unit'd strings (`50%`, `10rem`, `auto`) still pass through.
  - `layout->css`'s named branch routes width / height through
    `as-length`, so `width "200"` → `width:200px;` and `width 200`
    → `width:200px;` both produce valid CSS. Padding / margin keep
    their string-only behaviour because their multi-value forms
    (`10px 20px`, `1em 2em 3em 4em`) don't fit `as-length`'s
    single-value contract — left for a future variant if needed.

Adds 5 reconciler tests covering bare-numeric-string, actual-
number, unit'd-string, and height-with-bare-numeric paths. The
existing layout-css tests pass unchanged (string-with-unit
behaviour is preserved).

CI gates green; 718 / 0 failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@avanelsas avanelsas merged commit b293d5e into main May 1, 2026
1 check passed
@avanelsas avanelsas deleted the fix/reconciler-raw-html-and-named-lengths branch May 1, 2026 13:35
avanelsas added a commit that referenced this pull request May 2, 2026
* Release v0.3.0 — minor release with multi-step actions, State
panel, inline binding chip, templates revamp + Hickey-style
export pipeline refactor

Promotes the [Unreleased] section in CHANGELOG.md to a tagged
[0.3.0] — 2026-05-02 entry, adds the standard ### Verified
block, and refreshes the comparison links at the bottom.
package.json's version bumps 0.2.0 → 0.3.0 to match.

This is a minor (not patch) release because PRs #16-#19, #21
add substantial editor surfaces (multi-step actions, live
State panel, inline binding chip, templates panel revamp with
five new starters, inspector field-as-data foundation), and
PRs #20 / #23 / #24 / #25 land the full Hickey-style refactor
of the export pipeline (clj-form data values for every codegen
path; lower-document as the canonical lowered model every
plugin consumes). PRs #15 and #22 fix three reconciler bugs
along the way. Saved project files are unchanged; every export
target stays at parity.

Verified locally:
- 747 tests / 2243 assertions / 0 failures / 0 errors.
- npx shadow-cljs release app — 0 warnings under Closure Advanced.
- clj-kondo --lint src test scripts — 0 errors, 0 warnings.
- cljfmt check — all files formatted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* CHANGELOG: tighten v0.3.0 entry

Drop the per-PR detail in favour of a brief summary. Each
bullet is one line / one short paragraph; the inline rationales
and namespace-level notes move out of the changelog (commit
messages and PR descriptions are the canonical record for those).

Net: 165 lines of v0.3.0 entry → 60 lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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