Commit Graph

1556 Commits

Author SHA1 Message Date
Claude e9289685fd refactor(navigation): suppress the Space hotkey by prop
Replaces the view hotkey store with a suppressSpaceHotkey prop on
ViewNavigationMenu, alongside the suppressSettings prop which already exists
for exactly this purpose: a view opting out of a navigation chrome behaviour.

The store had one advantage, that it covered the preset route without the
router naming the view. That is a single expression in PresetView, and paying
for it with a global claim was the wrong trade: the claim was lifecycle state
that had to be released on unmount to avoid stranding Space for the whole
client, where a prop simply describes the route.

Verified both routes: /teleprompter and a locked preset targeting the
teleprompter leave Space to the view, while the timer and a preset targeting
the timer still open the navigation menu with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf
2026-08-13 19:13:02 +00:00
Claude 323b30ea68 refactor(teleprompter): drop code that was not earning its keep
Review pass over the view.

Removed: estimateWordsPerLine and linesPerMinuteToWordsPerMinute, written for
a words-per-minute readout that was never built; play and pause on the
controller, which nothing outside the hook called; and three constants that
were exported but never imported.

The tick function was being reassigned to a ref on every render, which is a
side effect during render. It only ever touched refs and state setters, so it
is now a stable callback and the ref is gone.

The synthetic contentKey string is replaced by the memoised blocks array it
was standing in for. It was also a dependency of the ResizeObserver effect,
which tore the observer down and rebuilt it for no gain: the observer already
covers every reflow that changes the document.

ScriptBlock was memoised but never actually memoising, because the parent
built its ref callback inline and handed it a new identity every render. That
also churned the follow map, unregistering and re-registering every block. The
id is now bound inside the block against a stable callback.

Tests: dropped six that asserted arithmetic identities or wrapped clamps
rather than behaviour, and added three for branches that were untested,
group titles across and back into a group, and a heading with no cue.
The e2e rewind assertion waited 600ms for an eased scroll that needs about
900ms from a nudge and a second from the bottom of a long script; it now
polls. The navigation menu assertion raced app hydration and now waits for
the view, as the existing navigation tests do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf
2026-08-13 16:42:07 +00:00
Claude 15dac35748 feat(teleprompter): honour the shared Flip Screen toggle
The teleprompter was the only view ignoring the global Flip Screen option,
so it now composes with the per view flips.

The two turn out to be the same mechanism: the shared `.mirror` class is
rotate(180deg), which is the same matrix as scale(-1, -1), so Flip Screen
is exactly a flip on both axes at once. Folding it in with XOR means the
teleprompter matches every other view when Flip Screen is on, and there is
still only one transform on the element.

It cannot replace the per view flips. A rotation preserves handedness, so
it never produces the mirror image a beam splitter reflection needs, and it
cannot address one axis on its own. Those remain URL params, which also
lets a shared link to the talent screen carry the rig's setup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf
2026-08-13 05:25:44 +00:00
Claude f806244586 feat(teleprompter): add teleprompter view
Adds a teleprompter at /teleprompter which builds its script from the
rundown rather than from an uploaded file, so the read follows the show.

The script comes from a text custom field chosen per view, and the whole
rundown renders as one continuous document with a heading per segment.
That is how broadcast prompters work: the operator scrolls to the right
section as the show moves, so a hard cut on every event change would take
the tail of the line the talent is still reading. Following the loaded
event is a soft jump which releases when the user scrolls by hand, the
same interaction the operator view already uses.

Controls are local, and match the convention shared by prompter software:
space to run, arrows for speed and nudge, home to rewind, F to flip. That
convention doubles as the hardware protocol, since foot pedals and hand
controllers are USB HID devices emitting these keystrokes, so they work
with no setup. Space is claimed back from the navigation menu for the
lifetime of the view via a small store, since the router renders the menu
generically for presets and a prop would not reach it.

Scrolling uses native scrollTop on an overflow container, with the
animation frame loop as its only writer. Position is kept as a float in a
ref: at readable speeds the per frame movement is well under a pixel, so
rounding every frame would stall the scroll, and holding it in state would
re-render the document sixty times a second. Scroll anchoring and smooth
scroll behaviour are both disabled because each would be a second writer.

Notable details:
- flip applies to the view root, so a beam splitter inverts the scroll
  direction along with the text
- content padding is derived from the viewport height, not a percentage,
  which resolves against width and would strand the first line
- image custom fields are refused even when typed into the URL
- script text is rendered as a text node, never as markup

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf
2026-08-13 05:20:46 +00:00
Carlos Valente c6eccec30e refactor(settings): show new app indicator 2026-08-09 16:48:20 +02:00
Carlos Valente 5220c2c374 fix(settings): prevent loader overflow 2026-08-09 16:48:20 +02:00
Carlos Valente 4eeeb294f7 chore: update electron navigation 2026-08-09 16:48:20 +02:00
Alex Christoffer Rasmussen a006331fea Group duration context menu utils (#1748) 2026-08-09 16:45:47 +02:00
Carlos Valente ac0ef06459 bump version to 4.12.0 2026-08-09 10:44:13 +02:00
Carlos Valente fb559ed9da refactor(progress bar): reset when event changes 2026-08-05 15:30:22 +02:00
Carlos Valente 24be49ef66 refactor(settings): polish settings UI 2026-08-02 13:44:28 +02:00
Carlos Valente f0c284b708 refactor(link): add row presentation variant 2026-08-02 13:44:28 +02:00
Carlos Valente 9e42f18299 feat(settings): add searchable navigation 2026-08-02 13:44:28 +02:00
Carlos Valente bdd815678b refactor(ui): extend link and modal variants 2026-08-02 13:44:28 +02:00
Carlos Valente 12fc76b8ff refactor(op): simplify UI elements 2026-08-02 12:55:49 +02:00
Carlos Valente 8ed0a1689e refactor(op): clarify settings text 2026-08-02 12:55:49 +02:00
Carlos Valente ba18b8dba7 refactor(ui): rundown title UI polish 2026-08-02 12:55:49 +02:00
Carlos Valente 608247157b refactor(rundown): cap rundown names to 64 characters 2026-08-02 12:55:49 +02:00
Carlos Valente 514b6c3a02 refactor(navigation): custom params indicators only for current view 2026-08-02 12:55:49 +02:00
Carlos Valente 2050735014 refactor(ui): title alignment is logo aware 2026-08-02 12:55:49 +02:00
Carlos Valente 76b1341432 refactor(logo): upload on submit form 2026-08-02 10:39:58 +02:00
Carlos Valente c7422fc7c7 refactor(logo): improve flow for managing logo 2026-08-02 10:39:58 +02:00
Carlos Valente 94d54529ee refactor(views): consistent loading and empty states 2026-08-01 16:25:14 +02:00
Carlos Valente f7535651f6 refactor(info): improve UI consistency and polish 2026-08-01 14:41:39 +02:00
Carlos Valente 5cf36f049a refactor(modal): allow wider modals 2026-07-25 14:28:45 +02:00
Carlos Valente c6248b0c72 refactor(import): improve preview UI 2026-07-25 14:28:45 +02:00
Alex Christoffer Rasmussen 2a890cf2b3 fix: issue where a countToEnd would lead to incorrect expected times (#2149)
* 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
2026-07-25 11:22:24 +02:00
Carlos Valente a8c611911d refactor(rundown): new entries are appended to rundown 2026-07-22 07:17:39 +02:00
Carlos Valente 8363f06a5c chore(rundown): improve documentation of insert logic 2026-07-22 07:17:39 +02:00
will@t1d.dev 5f040092cb fix: timer-legacy view not served in desktop distributions
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.
2026-07-19 10:02:45 +02:00
Carlos Valente 347c748dd9 feat(rundown): add shortcut to jump to current element 2026-07-18 17:03:02 +02:00
Carlos Valente f234a1f892 fix(rundown): prevent skipped events from influencing day calculations 2026-07-18 16:52:36 +02:00
Carlos Valente 0c5e87b7e7 fix(ui): prevent warning indicator from overflowing container 2026-07-18 16:52:36 +02:00
Carlos Valente 64082ceac0 refactor(rundown): improve visibility of count to end 2026-07-18 16:45:55 +02:00
Carlos Valente f598e8dab1 refactor(time control): improve handling of overtime and count to end 2026-07-18 16:45:55 +02:00
Carlos Valente 0c84a3ff9e fix: delays account for midnight 2026-07-18 16:45:55 +02:00
Carlos Valente c8760b5e9c bump version to 4.11.0 2026-07-18 15:24:25 +02:00
Claude 34360a5b8f feat(import): add merge strategy and new-rundown destination to spreadsheet import
Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2026-07-17 22:26:19 +02:00
Claude 5e6debcce0 fix(views): center timer digits with flexbox for embedded browser compatibility
The Stage Timer and PiP Timer views relied on `align-content: center` to
vertically center the timer digits inside `.timer-container`, which is a
plain block element. Aligning children of a block container via
`align-content` only works in Chromium 123+ (Firefox 125+, Safari 17.4+).

Embedded browsers such as vMix Browser Input (CEF V115) and other older
CEF/Chromium-based production tools ignore the property, so the digits
fall back to the top of the container while every other element renders
correctly.

Make `.timer-container` a real flex column and center with
`justify-content: center`, which is universally supported and matches the
centering approach used elsewhere in the codebase. The removed
`justify-self`/`align-self` were no-ops on a full-width flex item.

Fixes #2126

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014T6ENZ3r6JXZb1fpYw2oNY
2026-07-17 20:59:49 +02:00
Carlos Valente c191786e50 refactor(navigation): clear params from editor drawer 2026-07-17 15:50:42 +02:00
Carlos Valente 6911e37c18 refactor(automation): improve automation UX 2026-07-17 10:53:45 +02:00
Claude 86b2132cdc feat(automation): expose group title and allow setting secondary text
Surface {{groupNow.*}} template variables (title, note, colour, times,
custom fields) in the automation template autocomplete so events inside a
group can reference their group. The runtime store already carries
groupNow, so substitution and filters worked already; this makes it
discoverable.

Extend the message-secondary action with an optional text field so an
automation can set the secondary message content, not just its source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZYPZVdZLWowU7DyzGkWyy
2026-07-17 10:53:45 +02:00
Carlos Valente daa032e92c ux(finder): add navigation shortcuts 2026-07-14 12:04:44 +02:00
Carlos Valente ae65ec97d6 fix(shortcut): correct shortcut to delete 2026-07-14 12:04:44 +02:00
Carlos Valente e7e4302100 refactor(shortcuts): improve readability of info element 2026-07-14 12:04:44 +02:00
Carlos Valente 7e7d35980a refactor(op): improve status recognition 2026-07-14 11:58:59 +02:00
Carlos Valente 5001dda284 feat(op): add group visual relation 2026-07-14 11:58:59 +02:00
Carlos Valente a75d35bdc0 refactor(op): improve delay indicator styles 2026-07-14 11:58:59 +02:00
Claude 2e631194e1 fix(ui): improve progress bar with local interpolation
Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2026-07-14 11:25:22 +02:00
Carlos Valente f39892dc6e feat(navigation): persist view params customisation 2026-07-13 18:44:38 +02:00