Skip to content

Calendar Display: a second source, for the schedule beside the roll - #23

Merged
ZombieHDGaming merged 2 commits into
masterfrom
claude/calendar-display-feature-5teey3
Sep 4, 2026
Merged

Calendar Display: a second source, for the schedule beside the roll#23
ZombieHDGaming merged 2 commits into
masterfrom
claude/calendar-display-feature-5teey3

Conversation

@ZombieHDGaming

@ZombieHDGaming ZombieHDGaming commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Closing Time has drawn one thing: a credit roll that scrolls. This adds the other half of what a broadcast puts either side of its content — a board saying what is on, where, and when.

closing_time_calendar is a source of its own rather than a mode of the marquee, because what it edits is a different shape: a roll is an ordered list of sections that scrolls, a board is a schedule crossed with a layout that holds. What they share is everything below the layout — TextStyle, BackgroundPanel, GradientSpec, LogoRef and its cache, the font bundle, the machine-wide style library — shared by being written against the same pieces rather than by one of them growing a second personality. One house style dresses both.

The board

Three layouts, not five. A list, a grid, and stacked columns, crossed with what the time axis measures (real clock time, or named slots of equal size) and which way round it runs (time down the side, or across the top). The five presets the designer offers are settings over one engine — so a fix to how a lane header is measured lands once rather than in one of five places.

Preset Shape
Up Next Panel A list of rows, each a time and what happens at it
Day Column Board Hours down the side, a column per day
Channel Timeline A row per channel with time running across
Wave Grid Named waves down the side against a column per game
Stacked Blocks Columns of blocks in order, each printing its own time

A slot may carry a real time as well as a name. That is what keeps a wave board and a clock board from being two different products: the gutter prints WAVE C and 1:00 PM together, an event typed in with a clock time drops into the right slot, and every clock feature works on an axis that is not a clock.

An end that was never stated is resolved by the lane — until the next event in it, a fixed length, or the smallest span worth calling a duration — because a published schedule prints start times and leaves the rest to be inferred, and a lane that is one screen in one room wants a different answer from one standing for a whole platform.

Collisions are never hidden. Two events wanting one lane are split side by side or stacked, again per lane, and the designer names the pair either way.

Bands and continuations. A band (DOORS OPEN) crosses every lane and takes none of their room. A continuation links to the event it continues, which does two things at once: a list of what is next counts the pair once, and the style cascade hatches the second block.

The style cascade is five layers merged in order — board, lane, category, status, event — each contributing only the parts it sets, so a category that owns a color need not restate the typeface. Opacity multiplies rather than replaces, because it is the one part that means "less of whatever is underneath": a finished event in a half-present lane is a quarter present.

The free layer. Everything that is not the grid — a clock, a ticker, a chip row, a legend, an image, a panel — is an element anchored to one of nine points on the canvas, painted into a picture of its own so it neither scales with a fitted board nor scrolls with a moving one.

Animated artwork. A block's logo, a lane header's mark or an image element can be a GIF, APNG or animated WebP, through the same hole-and-overlay bargain the roll strikes. Simpler here in three ways — no shadow frames, no epoch, and placements reach the compositor already in canvas coordinates — and harder in two: a logo is decoded after the fit scale is known so it is decoded at the size it is really drawn at, and each placement carries a key so playback survives the rebuild that every clock refresh causes rather than restarting twice a minute. Animations run on the wall clock, which is the one place the two sources deliberately disagree.

Five independent clock switches rather than one live mode: a now-line, fading the finished, highlighting the current, dropping the finished, and how often to redraw. A board with all of them off is rasterized once and never again — it costs nothing per frame for the rest of its life.

One overflow setting, three answers: scale to fit, page through (cut at day boundaries, so a page never straddles two days), or scroll.

How it is built

The renderer plans then paints, where the strip renderer measures then draws with the same code. A board is drawn at three different scales and offsets, and a plan can be painted at each of them from one set of measurements — where a measure-and-draw walk would have to run once per page. The two orientations are one code path written against a major axis (time) and a minor one (lanes), so every feature works both ways round by construction.

now is passed into the renderer rather than read from the system clock, which is what lets the designer show the board as it will look at ten past seven, and lets the harness assert what a now-line does without waiting for a minute to pass.

Also in here

  • A designer window with a click-to-select preview, undo, and a "show the board at" time control.
  • A column-mapped CSV/TSV import that guesses the mapping from the headers and creates the days, lanes and categories the file names — so one paste of a published schedule produces a board rather than a pile of events with nowhere to sit.
  • Sixteen headless suites, written as relations (a two-hour block is twice a one-hour block; the transposed board is the transpose) plus a fixed instant for the clock and a round trip for persistence.
  • An ARCHITECTURE.md chapter covering the decisions and their reasons, five entries under Known limitations for what was deliberately left out, and a README section.

Verification

  • cmake --build clean, no new warnings.
  • closing-time-tests: 109 suites, 6047 checks, 0 failures — the 16 new calendar suites included, and no existing suite moved.
  • clang-format and gersemi clean.
  • Every preset rendered and inspected by eye through --artifacts.
  • The two animation checks were broken on purpose before being trusted, per the harness's own discipline: painting the artwork in anyway fails the hole check, and a volatile key fails the carry-across check.

Deliberately not here

No networking of any kind: no iCalendar feed, no bracket-site API, no polling. A board is typed in the designer or imported from a delimited file. No ending action or signals — a board does not finish. An animation restarts when the block under it is retimed, since that is a different placement of it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T1uxGMgLj1Qd8gyjY6KDwH

Closing Time has drawn one thing: a credit roll that scrolls. This adds the other
half of what a broadcast puts either side of its content -- a board saying what is
on, where, and when.

`closing_time_calendar` is a source of its own rather than a mode of the marquee,
because what it edits is a different shape: a roll is an ordered list of sections
that scrolls, a board is a schedule crossed with a layout that holds. What they
share is everything below the layout -- TextStyle, BackgroundPanel, GradientSpec,
LogoRef and its cache, the font bundle, the machine-wide style library -- shared by
being written against the same pieces rather than by one growing a second
personality. One house style dresses both.

The board:

- Three layouts (a list, a grid, stacked columns) crossed with what the time axis
  measures (real clock time, or named slots of equal size) and which way round it
  runs. The five presets the designer offers are settings over one engine, so a fix
  to how a lane header is measured lands once rather than in one of five places.
- A slot may carry a real time as well as a name, which is what keeps a wave board
  and a clock board from being two different products: the gutter prints both, an
  event typed in with a clock time drops into the right slot, and every clock
  feature works on an axis that is not a clock.
- An event with no end runs however its lane says -- until the next one, a fixed
  length, or the smallest span worth calling a duration -- because a published
  schedule prints start times and leaves the rest to be inferred.
- Two events wanting one lane are split or stacked, again per lane, and the
  designer names the pair either way: a board that silently drew one over the other
  is the one outcome nobody wants.
- Bands (DOORS OPEN) cross every lane and take none of their room. Continuations
  link to the event they continue, so a list of what is next counts the pair once
  and the style cascade can hatch the second block.
- A block's appearance is five layers merged in order -- board, lane, category,
  status, event -- each contributing only what it sets, so a category that owns a
  color need not restate the typeface. Opacity multiplies rather than replaces,
  because it is the one part that means "less of what is underneath".
- Everything that is not the grid -- a clock, a ticker, chips, a legend, an image --
  is an element anchored to one of nine points on the canvas, painted into a picture
  of its own so it neither scales with a fitted board nor scrolls with a moving one.
- Five independent clock switches rather than one live mode. A board with all of
  them off is rasterized once and never again.
- One overflow setting with three answers: scale to fit, page through (cut at day
  boundaries), or scroll.

The renderer plans then paints, where the strip renderer measures then draws with
the same code: a board is drawn at three different scales and offsets, and a plan
can be painted at each of them from one set of measurements.

Also: a designer window with a click-to-select preview that can show the board as it
will look at another moment; a column-mapped CSV/TSV import that creates the days,
lanes and categories the file names; fourteen headless suites; and an architecture
chapter covering the decisions and what was left out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1uxGMgLj1Qd8gyjY6KDwH
A block's logo, a lane header's mark and an image element animate, through the
same hole-and-overlay bargain the roll strikes: the artwork is not painted into
the page, the space it would have taken is left empty, and it is drawn over the
top from a texture of its own. The layout does not move for any of it -- an
animation occupies exactly the box its first frame would have as a still.

Three things are simpler than in the roll. No shadow frames, because a calendar
logo casts no drop shadow, so the blur-per-frame path is absent entirely. No
epoch, because a board has no restart. And placements reach the compositor in
canvas coordinates, carried through the fit scale and the page offset by the
same mapping the hit boxes already used -- so nothing on the graphics thread
learns that board space exists.

Two things are harder, and both come from a board being a thing that redraws
itself:

- Decoding happens after the fit scale is known, so a logo is decoded at the
  size it is really drawn at. The pass that finds animated artwork and marks its
  holes therefore runs inside the page loop rather than during the layout.
  Decoding at the layout's size means a blurred bug or a pile of frames being
  scaled down every time they are drawn.

- Playback survives a rebuild. A board with any clock feature on re-rasterizes
  every thirty seconds, and a naive port of the roll's arrangement would restart
  every animation on it twice a minute. Each placement carries a key -- the
  artwork, the page, the rectangle -- and the source keeps elapsed times against
  it, so a rebuild that moved nothing carries playback across. One that really
  moved a logo gets a new key and starts it again.

Animations advance on the wall clock, which is the one place the two sources
deliberately disagree: a roll ties its animations to the roll, so a paused roll
is a still frame; a board has no roll to tie them to, and a bug in the corner of
a schedule should keep moving whatever the schedule is doing.

A renderer built without an animation cache paints every logo as a still, as the
strip renderer's does -- which is what the harness gets and what the designer's
preview gets, so a board being typed into holds still.

Also fixes a hit box being reported on every page rather than the page it is
actually on, found while filtering placements the same way.

Two new suites, both broken on purpose before being trusted: painting the
artwork in anyway fails the hole check, and a volatile key fails the
carry-across check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1uxGMgLj1Qd8gyjY6KDwH
@ZombieHDGaming
ZombieHDGaming merged commit 63b006d into master Sep 4, 2026
6 checks passed
@ZombieHDGaming
ZombieHDGaming deleted the claude/calendar-display-feature-5teey3 branch September 4, 2026 17:37
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.

2 participants