Commit Graph

1551 Commits

Author SHA1 Message Date
Claude b21385ec4a refactor(report): a run becomes a report only when finished
Progress is kept in memory while a show runs and written once, as a
complete report, when the operator finishes the run. The stored history
therefore never contains a partial run.

- Nothing writes on the cue path. triggerReportEntry only updates memory,
  so the sidecar is untouched between the first event and finish.
- Removes the write coalescing added for the previous model: with a
  single write per run there is nothing to debounce, so the timer, the
  dirty flag, the flush export and the shutdown hook all go, and app.ts
  returns to its previous state.
- ShowRun.endedAt is no longer nullable. A stored run is finished by
  definition, so recovery of runs left open by a crash is gone along with
  the concept, and the run list drops its ongoing state.
- Adds GET /report/runs/open for the editor indicator. /runs now means
  finished reports only, which is also the contract Cloud sees.
- A run whose rundown was deleted before it finished now reports the
  events that actually ran as its planned count, rather than zero.

Known gap: RestorePoint carries playback state only, so a crash mid show
loses the in progress report. The stored history is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nr3FbLbM8gB8Jm771YgTV
2026-08-08 21:35:52 +00:00
Claude faca1a1c76 refactor(report): reduce footprint and take writes off the cue path
Cuts the risk surface of the run history feature while keeping what it
delivers.

Stability:
- Sidecar writes were happening on every event stop, and each write
  serialised the project's entire history. That put work proportional to
  everything that ever ran onto the show critical path, growing without
  bound. Writes during a run are now coalesced, with immediate writes for
  anything a user did and a flush on finish, project change and shutdown.
- The editor no longer carries any of this feature's code. The per event
  last run chip is gone, so RundownEventChip, RundownEventInner and
  useProjectRundowns are back to their previous state. Each rundown row
  had gained two extra query subscriptions, one of them polling.
- closeRun is no longer called from runtime.service, so the timer path is
  untouched. Ending a run is now explicit, which also means a mid show
  stop and restart no longer splits one show across two runs.

Discovery:
- A run indicator in the editor overview appears only while a run is
  being recorded. It shows when recording started and carries the Finish
  action, then links to the report. One component with one subscription,
  rather than anything per row.

Smaller:
- report.parser drops from exhaustive validation of a file we write
  ourselves to a shallow shape check; the failure mode is unchanged.
- getCombinedReport returns to its original shape, keeping only the
  snapshot read that report accuracy depends on.
- Removes getLatestRun and GET /runs/latest, which only existed for the
  chip, and hand rolled refetches the api layer already covers.

Production diff is down from ~1460 to ~1290 lines, and the four floating
promises the previous revision introduced are gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nr3FbLbM8gB8Jm771YgTV
2026-08-08 21:25:03 +00:00
Claude 8551c161f1 fix(report): correct run timestamps, scoping and recovery
Review of the run history feature turned up four defects, all in the
code that feature introduced.

- Runs were dated with a time of day rather than a wall clock instant.
  `clock` and `rundown.actualStart` are millis since midnight, so every
  run in the history list rendered as 1 January 1970, and getLatestRun
  ordered runs incorrectly across days: a run at 09:00 today sorted below
  one at 20:00 last week, so the "last run" chip and /runs/latest could
  return an older run. Runs now take `_startEpoch`, the instant the show
  began, and the type documents the invariant.
- A stop arriving with no run open is ignored. Loading a project stops
  playback and reinitialises reporting in the same tick, and the trailing
  stop was attributing the previous project's event to the new one.
- Run summaries counted planned events from the loaded rundown rather
  than the rundown the run belongs to, so switching rundowns mid-run gave
  the summary a denominator from a different rundown. A run whose rundown
  has since been deleted now keeps its last known count instead of
  dropping to zero.
- Recovery of runs left open by a crash only closed the first one. Any
  others stayed dangling forever: permanently shown as ongoing and never
  eligible as the latest run.

Also replaces the raw ISO default run label with a readable local date
and time, and adds a batching upsertRuns to the store so recovery writes
the sidecar once rather than racing one write per run.

Each fix has a regression test, verified to fail without the change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nr3FbLbM8gB8Jm771YgTV
2026-08-08 16:21:00 +00:00
Claude 3c2875a956 feat(report): persistent show run history
Turns the reporter from a live-only, single-run curiosity into a
persistent, per-project, per-rundown history of runs.

- Extend OntimeEventReport with a schedule snapshot (scheduledStart,
  scheduledDuration) taken when an event starts, plus playCount. Reports
  are now a record that survives later rundown edits, rather than a live
  join against the current rundown.
- Add a run lifecycle: a run opens on the first event start and closes on
  a full stop, archiving the previous run to history so the next start
  begins fresh.
- Persist runs to a per-project sidecar file (services/report-service),
  patterned on the existing restore-service, so a crash or restart no
  longer loses the whole show's record. Runs are scoped by rundownId for
  multi-rundown projects, cascade-deleted with their rundown or project,
  renamed alongside a project rename, and deliberately not copied when a
  project is duplicated.
- Extract the over/under/on-time variance and run summary maths shared by
  the rundown chip, the report settings panel, and the server into
  ontime-utils (getEventVariance, getRunSummary, countPlannedEvents).
- Extend the report API with run history endpoints (list, get, latest,
  rename, delete) while keeping GET /report's existing shape so Companion
  and HTTP automations are unaffected.
- Replace the settings report table with a run browser: a list of runs
  with a rundown filter, inline rename, delete, and a detail view with
  per-run summary stats and CSV export.
- Add a third, muted state to the rundown event chip: an event with
  nothing in the current run yet previews how it went last time.

Fixes a real bug found while testing the new store: emptyStore() was a
shared object, so its runs array leaked mutations across project loads.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nr3FbLbM8gB8Jm771YgTV
2026-08-08 11:08:28 +00: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
Carlos Valente 6bcfd87851 refactor(rundown): show locked duration group indicator 2026-07-13 17:34:11 +02:00
Claude 4addb50ca4 feat(countdown): allow counting down to a group 2026-07-13 17:29:56 +02:00
Carlos Valente 161ab3fe13 refactor(timer): elapsed is active time from start 2026-07-13 15:34:31 +02:00
Carlos Valente 4f4626a163 refactor: allow larger settings panels 2026-07-13 15:33:23 +02:00
Claude 82d5f02ea0 refactor(mcp): guide agents through creating clean rundowns 2026-07-13 15:33:23 +02:00