Four commits landed on this branch from review while the layout wiring was in
progress, and the two solved overlapping problems differently.
Theirs made elementKeys() append every layout-only key, matched by exact name.
Against football that also lists `score` and `odds` -- entries the alias
resolution already pairs with score_text and odds_text -- so those offsets
would get two controls posting the same field. The row split here
(styleRows via core's x-layout-key, positionRows for what nothing claims)
replaces that append; elementKeys() is back to listing style elements only.
Theirs also covered cases this side had missed, all kept:
- A leaf straight under layout (a show_logo toggle, no x/y object) now gets a
position row with a self-keyed column bound to customization.layout.<key>.
Core no longer lets an element claim a leaf as its offsets.
- Leaf columns keep their namespaced id, so a leaf sharing a name with another
entry's axis cannot be shadowed.
- columnsFor(schema) still works with one argument (every row), so their
column tests run unchanged.
- The owned key is CSS.escape()'d before it becomes a selector.
test_style_editor_element_keys.js is rewritten for the new contract, and says
why the old one had to go. All six JS suites pass; in a browser football's 92
layout fields each render once, and a synthetic leaf -- including one named
`scale` alongside a `scale` axis -- gets exactly one correctly named control.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The editor took the whole layout section over but matched offsets to style
rows by exact key. A hand-written schema's two blocks were never named alike --
football styles score_text but positions score -- so of football's eleven
positionable things only status_text had a control. Score, odds, both logos,
timeouts, possession, down-and-distance, date, time and records were options
the schema advertised and the renderer reads, reachable nowhere in the UI.
Core now resolves each style element's layout key through alias_keys, the map
the resolver already reads offsets with, and records it as x-layout-key. The
widget reads that rather than carrying a second copy of the rules, and posts
under the key the schema declares: football's own offset reader looks up
layout.score, so a value saved as layout.score_text would be kept and never
drawn. Layout entries no style element claims get an "Other positions" table
with its own columns, in every mode panel as well as the base one, in the order
the plugin declared them.
Verified in a browser against football's real schema: 92 of 92 layout fields
(23 base, 23 per mode) rendered exactly once under their declared names, none
posted under a style key, no duplicated field names, favorite_result_colors
still editable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
container.dataset.ownedKeys round-trips schema property keys through a
DOM dataset attribute, and the takeover handoff spliced each one
straight into '[data-child-key="' + k + '"]' with no escaping --
inconsistent with this codebase's own convention elsewhere
(plugin-file-manager.js, app-shell.js's escapeCssSelector) for building
a selector from a dynamic value. A key containing a quote or backslash
would break the selector or be steerable; Codacy's static analysis
flagged this pattern (1 high ErrorProne finding on PR #569, current
head at the time) as a new issue, though its dashboard is unreachable
from this sandbox (egress to app.codacy.com is blocked) and the
check-run API returned no detail text -- verified and fixed by reading
the diff directly rather than the tool's own description.
Added a source-assertion regression test alongside this file's
existing ones (this behavior lives in an inline script no Python test
executes).
Full suite: 4888 passed, 62 skipped, 2 failed -- both the pre-existing
Europe/Kiev/Asia/Calcutta tzdata-alias gap in this sandbox, identical
on origin/main, unrelated to this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two defects, both found by rendering the real partial in a browser rather than
by reading the code.
It was losing a race to its own fields. The hand-off guard asked "do any
fallback controls differ from their server-rendered defaults?" as a proxy for
"is someone editing this?". But the fallback holds this block's own font
fields, and the font-selector widget populates them on the same 50ms timer --
so a plain page load, with nobody touching anything, raced into "dirty" and the
editor removed itself, leaving the 701-line accordion form it exists to
replace. Measured: seven customization.*.font selects dirty ~60ms after
injection, clean again by 400ms. The question is whether a *person* typed, and
event.isTrusted answers exactly that; the listeners now go on synchronously,
because the edit worth protecting can happen before initWidget runs.
It took over too much. Taking over removed the whole fallback section, but a
customization block can hold more than styling -- football keeps
favorite_result_colors there -- so that removed the only UI those fields had,
and the editor also rendered them as an element, giving every row an "enabled"
and three colour columns. Core now marks the blocks it recognises as styling
(the compact declaration already did; hand-written adoption did not), the
widget renders only those, and the template drops only the children the widget
reports owning.
Verified on football's real schema: 28 rows across four mode tabs, columns
Element/Font/Size/Colour/X/Y, favorite_result_colors still editable with its
ten inputs, no duplicated field names, no console errors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>