Fix #491: per-entry icon svgs #493

Merged
crueber merged 2 commits from fix/issue-491 into main 2026-09-13 20:43:06 +00:00
Owner

REGRESSION from #481: the shared-svg + imperative-body-insert shape mounted an EMPTY svg on every issue-list row but the last (module-level glyph JSX = one shared DOM node moved from row to row; pills unaffected — each renders once).

Prescribed fix, as specified in #491:

  • web/src/lib/icons.jsx: each of the fifteen ICONS entries is a factory returning its OWN complete inline (own xmlns, 1em, shipped viewBox, aria-hidden, class); Icon invokes the factory per render ({(entry) => entry()(cls())} — the extra () unwraps the non-keyed-Show accessor; calling the factory per render mints fresh nodes). No shared shape, no runtime composition. All path data byte-identical (diff-verified vs origin/main); all call sites unchanged; currentColor-only, no fetch/innerHTML, ICON_NAMES, unknown-renders-nothing, #465/#466/#481 attributions kept. The clone entry fragment was unwrapped (dom compiler rejects fragments under — caught by vite build).
  • Tests: icons-465 / issue-comment-481 / milestone-icons-484 / label-icon-485 / create-menu-466 pins updated to the new shape; NEW web/test/unit/icons-per-row-491.test.js (structural factory regression pins + SSR per-row harness over the REAL file compiled with the project's own babel-preset-solid — every row svg carries its path, all 15 names render, unknown renders nothing; no new deps).
  • Law-12 decision appended to docs/go/12_web_ui.md.

Verification: node --test web/test/unit/*.test.js 1081 total / 1079 pass / 2 fail — the 2 are pre-existing on pristine origin/main (identical smoke.test.js live-server subtests, verified head-to-head); vite build green with the issue-comment path in the bundle. Rendered-browser verification explicitly OPEN (shared obscura daemon blocks loopback — no private daemon per workspace rules); the SSR per-row harness is the closest available headless pin.

REGRESSION from #481: the shared-svg + imperative-body-insert shape mounted an EMPTY svg on every issue-list row but the last (module-level glyph JSX = one shared DOM node moved from row to row; pills unaffected — each renders once). Prescribed fix, as specified in #491: - web/src/lib/icons.jsx: each of the fifteen ICONS entries is a factory returning its OWN complete inline <svg> (own xmlns, 1em, shipped viewBox, aria-hidden, class); Icon invokes the factory per render (<Show when={make()}>{(entry) => entry()(cls())}</Show> — the extra () unwraps the non-keyed-Show accessor; calling the factory per render mints fresh nodes). No shared shape, no runtime composition. All path data byte-identical (diff-verified vs origin/main); all call sites unchanged; currentColor-only, no fetch/innerHTML, ICON_NAMES, unknown-renders-nothing, #465/#466/#481 attributions kept. The clone entry fragment was unwrapped (dom compiler rejects fragments under <svg> — caught by vite build). - Tests: icons-465 / issue-comment-481 / milestone-icons-484 / label-icon-485 / create-menu-466 pins updated to the new shape; NEW web/test/unit/icons-per-row-491.test.js (structural factory regression pins + SSR per-row <For> harness over the REAL file compiled with the project's own babel-preset-solid — every row svg carries its path, all 15 names render, unknown renders nothing; no new deps). - Law-12 decision appended to docs/go/12_web_ui.md. Verification: node --test web/test/unit/*.test.js 1081 total / 1079 pass / 2 fail — the 2 are pre-existing on pristine origin/main (identical smoke.test.js live-server subtests, verified head-to-head); vite build green with the issue-comment path in the bundle. Rendered-browser verification explicitly OPEN (shared obscura daemon blocks loopback — no private daemon per workspace rules); the SSR per-row harness is the closest available headless pin.
web/src/lib/icons.jsx: each of the fifteen ICONS entries is now a factory
returning its own complete inline <svg> (own xmlns/1em/viewBox/aria-hidden/
class); Icon invokes the factory per render - no shared shape, no runtime
composition. The #481 shared-svg + imperative body insert evaluated the
glyph once (one DOM node), so one Icon per <For> row moved the same node
and all but the last row mounted empty. All path data byte-identical; all
call sites unchanged. Decision appended to docs/go/12_web_ui.md.
Sign in to join this conversation.
No description provided.