Three bugs in wheel.half, all found by looking at it:
The hub was centred on the edge, so the edge sliced it in half -- half a cancel
target is not a target. It is pushed inward by its own radius now, and the
wedges start beyond it. The ring is an inset shadow rather than a border,
because a border pushes every absolutely positioned child inward by its own
width, which put the hub a pixel past the screen edge.
Highlighting fired anywhere the pointer went, including well outside the wheel,
promising a pick that release would not deliver. The hit test is bounded by the
same rIn/rOut the wedges are drawn from, so the lit shape is the region.
An edge panel did not follow its edge: it kept the pixel position it opened at
and drifted inward when the window grew. It re-derives its anchor on resize,
and on visualViewport resize for pinch-zoom and mobile chrome.
One GEOMETRY object per variant now drives the wedges, the labels, the hub and
the hit test together, so they cannot disagree.
wheel.half pins the centre to a screen edge and fans the items inward, for a
control that lives at the edge and has nowhere to open a full wheel. The arc
geometry is now shared with the full wheel, so the two cannot disagree about
which item a direction means. The hub is a glyph sized to the dead zone: the
cancel target you see is the one you hit.
list.rows takes a real control per row. A bare number printed beside the reorder
arrows reads as a stepper for that number, so moving a LoRA looked like editing
its weight.
setBusy re-derived disabled from the constructor's value, so finishing a busy
spell re-enabled a button that had been disabled since; busy and disabled are
tracked separately now.
An emptied number field committed as 0 and snapped to min, discarding the last
good value -- a browser normalises invalid text to empty, and Number('') is
finite so it sailed past the isFinite guard.
A context menu anchors to a POINT, and that stand-in reached the dismiss stack,
which threw on the first click anywhere.
55 element groups across text, buttons, choice, input, slider, layout, status,
overlays, gallery and the wheel picker. Each lands in a registry-driven
catalogue that shows every variant in both themes, so an element that fails to
register is visibly absent rather than quietly missing.
rule_content_is_data iterates the REGISTRY, not a list: a new element is
attacked with markup the moment it calls define(), and one without a demo fails
the suite. That is announcement-not-enumeration applied to the tests.
Fixes found while building: checkbox silently ignored its label (inverted
logic left it with aria-label=toggle), the overlay root set z-index 0 and so
became a stacking context that trapped every overlay it was built to free, and
raising a window left its peers' z stale because z is derived from position --
claim() now notifies a rung when a peer moves.
The wheel highlights the actual sector rather than a box around the label: the
lit shape is the region that picks the item, which is what makes it aimable.