Skip to content

Timer Layout Engine v2: boxes, zones, per-orientation layouts - #19

Draft
Isorgcom wants to merge 8 commits into
mainfrom
GameNight-Timer-Bugalu
Draft

Timer Layout Engine v2: boxes, zones, per-orientation layouts#19
Isorgcom wants to merge 8 commits into
mainfrom
GameNight-Timer-Bugalu

Conversation

@Isorgcom

@Isorgcom Isorgcom commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Draft — phases 1–5 complete and dev-tested, phase 6 outstanding. Not ready to merge.

Rebuilds the tournament timer's layout system so elements stop overlapping and the display is genuinely customizable. Plan: .claude/plans/linked-squishing-hoare.md.

Done

  • Phase 1 — display ownership. syncVisibility() is now the single writer of style.display; renderAll() records availability in DATA_AVAIL instead. Fixes theme-hidden widgets reappearing on the next 2s poll. Adds a first-paint gate with a 2.5s failsafe so a JS error can never blank a remote display, and fixes the ends_at server/client drift.
  • Phase 2 — fit-text. Text auto-shrinks to its allotted space (batched measure/shrink, 8px floor), the ante sub-label moved in-flow so it is measurable, legacy point-positioned themes get soft extent caps, and a debounced reapplyLayout() finally re-runs on resize / rotate / fullscreen.
  • Phase 3 — free-mode boxes. Elements carry real {x,y,w,h} boxes on #layoutStage instead of center points: extent-aware drag clamps, 8-handle resize, AABB overlap detection with red outlines and a count badge, W/H steppers, and a live-data preview toggle. Legacy themes convert only on edit+save (measured drift: 0.03px).
  • Phase 4 — zones mode. Optional five-zone grid (top / rails / center / bottom) where overlap is impossible, with ghost drag-and-drop between zones, draggable zone boundaries, and per-zone size/align/gap. Reworked once already after owner feedback.
  • Phase 5 — per-orientation layouts. Separate landscape and portrait layouts per theme, auto-selected by viewport and swapped live on rotation, with a letterboxed editor preview for the orientation you are not on.

Remaining (phase 6)

Schema v2 export/import envelope, PHP-side migration + validation in timer_dl.php / _timer_theme.php, header TOC update, then the version bump and changelog entry.

Notes

Zones mode was reworked once and may need more; the owner's initial reaction was that it needed work on look, editing flow, and rigidity. Chromecast receiver (cast_receiver.php) is deliberately out of scope.

Isorgcom and others added 8 commits August 5, 2026 17:01
renderAll no longer writes style.display for the six data-driven widgets
(avg stack, reentries, chips in play, next break, ends at, payouts); it
records availability in DATA_AVAIL and new syncVisibility() is the only
writer of display for themable elements, combining theme visibility with
data availability and the edit-mode force-show rules. This kills the bug
where a theme-hidden widget reappeared on the next 2s poll. renderAll
writes go through change-guarded setText/setHtml helpers (groundwork for
fit-text). applyTheme's image/streaming blocks now manage only src and
availability. Server-side, _timer_theme.php gains the missing ends_at
defaults + color var and stops emitting visibility/order CSS (JS owns
them); a theme-pending class on <html> hides the stage until the first
applyTheme, with a 2.5s head-script failsafe so a JS error can never
blank a remote display.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Text now auto-shrinks to fit the space an element is allowed: fitAllText()
measures scrollWidth/Height overflow against clientWidth/Height in batched
clear-read-write passes (one reflow per cycle, 8px floor) and is scheduled
only when content changes (setText/setHtml dirty flag), the theme applies,
the viewport changes shape, or the clock string changes LENGTH. The ante
sub-label moves in-flow (inline-flex column) so measurement finally sees
it. Positioned elements get soft extent caps: max-width/height of twice
the distance from their center to the nearest viewport edge (divided by
transform scale, border-box), so legacy center-point themes stop hanging
off-screen with zero stored changes. A debounced reapplyLayout() wired to
resize/orientationchange/fullscreenchange re-applies layout + fit — the
re-layout hook the timer never had. The event-name font-size rule drops
its !important (double-class specificity instead) so fit-text's inline
size can win; flow children gain max-width:100% so overflow is measurable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Free-form layout graduates from center points to real boxes. Elements
carry {x,y,w,h} (top-left anchored, % of the new #layoutStage) stored in
layouts.landscape.free (schema 2); applyTheme prefers a box over legacy
pos, so untouched themes render exactly as before and convert only when
edited and saved. Entering edit mode measures the current rendered rects
into boxes (verified drift: 0.03px), drags are extent-aware (edges clamp
to the stage, snap candidates come from the box map with no DOM reads),
and a selection shows an 8-handle resize frame that lives in the stage so
handles never pollute fit-text measurement. checkOverlaps() runs an AABB
pass after every drag/resize, outlining colliding boxes in red with a
count badge in the pill — overlaps are now visible instead of shipped.
The inspector gains W/H steppers, the pill gains a Live toggle (position
against real data instead of placeholders, now realistic widths), Reset
re-seeds boxes from the default flow, and saves mirror box centers into
legacy pos for one release so stale open pages keep positioning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Themes can now run a structured layout where overlap is impossible: five
fixed zones (top bar / left rail / center stage / right rail / bottom
bar) on a CSS grid over #layoutStage, with elements assigned per zone in
an ordered list (layouts.landscape.zones.{opts,assign}). applyLayout()
reparents element nodes into their zones with minimal-move insertBefore
sync (the streaming iframe never reloads from unrelated changes) and
restores original DOM slots when leaving zones mode; both the free-box
and zones sub-trees persist so mode switching loses nothing. Zone items
compress (min-height:0 + shrink) and fit-text shrinks into the bound, so
a center stage holding clock+blinds+labels self-fits instead of clipping.
Empty zones collapse to zero outside the editor. Editor: Free/Zones
segmented switcher in the pill, dashed zone bounds with name labels,
click-to-select + wheel-resize on zone items, Objects panel grouped by
zone with per-row zone dropdown, in-zone reorder arrows, and an Unplaced
group; per-zone size/align/gap controls in the Page inspector. Grid gets
explicit align/justify stretch so the container's flex centering can't
leak in and size zones by content.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A theme now carries independent landscape and portrait layouts
(layouts.landscape / layouts.portrait, each with free + zones sub-trees).
The display picks the orientation matching the viewport via matchMedia
and falls back to the other when one is empty, re-applying live on
resize/rotate/fullscreen — verified end-to-end: distinct layouts saved
through update_theme swap correctly when the viewport flips, without a
reload. All box/zone readers and writers route through
currentLayoutOrientationKey(); in the editor an orientation segment
(desktop/phone) pins which layout is being edited, letterboxing the stage
to a simulated 16:9 or 9:16 rect (all geometry is stage-relative, so the
simulation is exact; viewport-fixed snap guides hide). First edit of an
undefined orientation seeds an in-memory copy of the defined one, and a
copy-from-other button (with confirm) does the same on demand — overlap
outlines immediately flag what doesn't survive the aspect change. The
QR, image, and streaming elements — previously body-level siblings whose
percentages resolved against the viewport — are adopted into #layoutStage
at init so boxed geometry is truly stage-relative.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner feedback on the first zones cut: ugly defaults, clunky Objects-
panel-only editing, too rigid, plus two bugs. Reworked: (1) zone items
are now GHOST-DRAGGABLE — pick one up, the zone under the pointer
highlights, drop to reassign, with in-zone ordering derived from the
drop position; the Objects panel dropdowns remain as the precise
fallback. (2) Zone boundaries are draggable dividers (thin blue lines
between zones, ns/ew cursors) that resize the top/bottom/left/right
zones live, replacing number-field-only sizing. (3) Softer look: dimmer
dashed bounds and labels, smaller default zone sizes (10/14/14/8).
Bugs: the edit pill now wraps within the viewport (max-width + flex-
wrap, centered) so its drag handle can never sit off-screen, and eye
icons inside zones anchor to their own item (items are position:
relative now; static items made every absolutely-positioned eye pile up
at the zone corner and clip to a sliver at the zone edge).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Isorgcom

Isorgcom commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

Shelved 2026-08-08. Parking this deliberately, not abandoning it. Everything is pushed; nothing is only local.

State: phases 1–5 complete and dev-verified, plus one zones rework. Branch is 8 commits ahead of main and 2 behind (v0.2059 and v0.2060 landed after it was last synced — merge main in before resuming).

Two things outstanding when it's picked up again:

  1. Zones needs a verdict. First cut drew "the zones is garbage" — ugly defaults, clunky Objects-panel-only editing, too rigid, plus an eye-button sliver and an off-screen toolbar handle. The rework added ghost drag-and-drop between zones, draggable zone boundaries, quieter chrome, and fixed both bugs. That rework was never reviewed — it went straight to the security work. So the open question is still: is zones good enough, does it need another pass, or does it come out entirely?

  2. Phase 6 depends on that answer. The schema v2 export/import envelope bakes the zones structure into the saved format, so building it before zones settles risks redoing part of it. Phase 6 also covers PHP-side migration and validation in timer_dl.php / _timer_theme.php, the header TOC, and the version bump.

Plan file: ~/.claude/plans/linked-squishing-hoare.md.

What already works on this branch (worth not losing): theme-hidden elements no longer reappear on the 2s poll; text auto-shrinks so nothing overflows; elements are real resizable boxes with red overlap outlines; the timer finally re-lays out on resize, rotation and fullscreen; and themes hold separate landscape/portrait layouts that swap live.

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