The panel absorbs whatever height the playback control above it leaves, and
its content was a fixed stack taller than that space on a 1280x800 screen -
with the container set to hide overflow, the secondary message input was
silently clipped off the bottom.
The stage preview is now the only elastic element and the controls are the
fixed floor, which is the way round an operator needs it. The control stack
also gets shorter: blink and blackout share a row, and the secondary source
select moves next to the secondary text input.
That regrouping removes the duelling owners of `secondarySource`. The select
picks the source and the eye toggles the line on and off, so the "Show
secondary" button - which wrote the same field from a second place, and made
picking an aux read as "secondary message hidden" - is gone along with the
local mirror of the remote state it needed.
Also: blackout now uses the destructive button variant, the toggles expose
`aria-pressed`, and the input rows use the shared editor label.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CHxSQ6nWHuyar8feieRra
The icon rail was absolutely positioned over the same box as the preview
content, so at narrow widths it collided with the render - and now that the
preview shows a real timer, it also sat under the blackout overlay. It moves
into the options column as a compact row, where it reads as editor chrome
rather than part of the stage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CHxSQ6nWHuyar8feieRra
The preview showed the word "Timer" where the timer goes, so it could not
tell an operator what the audience will actually see. It now embeds the same
component the picture-in-picture window uses.
Making PipTimer embeddable also fixes the pop-out window, which rendered a
running timer while the stage was blacked out and did not blink with the
stage. The timer font size, the message padding and the overtime outline move
from viewport to container units: inside the preview they resolve against the
stage frame, and in the pip window - which has no ancestor query container -
they resolve against its viewport exactly as before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CHxSQ6nWHuyar8feieRra
The blink modifier in the timer preview referenced a CSS module class that
does not exist, so `style.blink` resolved to undefined and the preview never
blinked. `.blink` is a global animation class, matching how the timer view
applies it.
The "message is live" label highlight used `??` where `&&` was meant: since
`visible` is always a boolean, the active style was unreachable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CHxSQ6nWHuyar8feieRra
* fix: issue where a count-to-end would lead to incorrect expected times
* fix: include add time in overtime when countToEnd
* fix: ui and server use same calculation for expected end
The server serves html/timer-legacy.html and html/login.html from disk
at runtime, relative to the bundled server. The Docker image copies
these files but the electron packaging did not, so the view 404ed in
all desktop distributions.
Additionally, AppImages mount at /tmp/.mount_*, a hidden directory.
Express sendFile refuses paths containing dot-segments by default
(returns 404 without touching disk), so the view failed on Linux even
with the file packaged. Allow dotfiles for this route; the request
path is fixed so no user input is affected.