From 613919c30ccf98626a96d72f6c5d1d2354b87bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 2 Apr 2026 16:16:17 +0200 Subject: [PATCH 01/13] chore: common RundownStatusBar component between screens --- .../src/client/styles/countdown/director.scss | 83 +++------------- .../client/styles/countdown/presenter.scss | 94 +------------------ packages/webui/src/client/styles/main.scss | 2 + .../webui/src/client/styles/prompter.scss | 12 +++ .../src/client/styles/rundownStatusBar.scss | 73 ++++++++++++++ .../src/client/styles/tTimerDisplay.scss | 69 ++++++++++++++ .../DirectorScreen/DirectorScreen.tsx | 16 ++-- .../client/ui/ClockView/PresenterScreen.tsx | 23 +---- .../client/ui/ClockView/RundownStatusBar.tsx | 54 +++++++++++ .../src/client/ui/ClockView/TTimerDisplay.tsx | 2 +- .../src/client/ui/Prompter/PrompterView.tsx | 8 ++ 11 files changed, 241 insertions(+), 195 deletions(-) create mode 100644 packages/webui/src/client/styles/rundownStatusBar.scss create mode 100644 packages/webui/src/client/styles/tTimerDisplay.scss create mode 100644 packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx diff --git a/packages/webui/src/client/styles/countdown/director.scss b/packages/webui/src/client/styles/countdown/director.scss index 0c034624ee1..d17850fee6d 100644 --- a/packages/webui/src/client/styles/countdown/director.scss +++ b/packages/webui/src/client/styles/countdown/director.scss @@ -208,14 +208,14 @@ $hold-status-color: $liveline-timecode-color; text-align: center; width: 100vw; margin-left: -13vw; - + .director-screen__body__part__timeto-name { color: #888; font-size: 9.63em; text-transform: uppercase; margin-top: -2vh; } - + .director-screen__body__part__timeto-countdown { margin-top: 4vh; grid-row: inherit; @@ -480,75 +480,14 @@ $hold-status-color: $liveline-timecode-color; .clocks-counter-heavy { font-weight: 600; } - - .director-screen__body__t-timer { - position: absolute; - bottom: 0; - right: 0; - text-align: right; - font-size: 5vh; - z-index: 10; - line-height: 1; - - .t-timer-display { - display: flex; - align-items: stretch; - justify-content: flex-end; - font-weight: 500; - background: #333; - border-radius: 0; - overflow: hidden; - - &__label { - display: flex; - align-items: center; - color: #fff; - padding-left: 0.4em; - padding-right: 0.2em; - font-size: 1em; - text-transform: uppercase; - letter-spacing: 0.05em; - font-stretch: condensed; - } - - &__value { - display: flex; - align-items: center; - color: #fff; - font-variant-numeric: tabular-nums; - padding: 0 0.2em; - font-size: 1em; - - .t-timer-display__part { - &--dimmed { - color: #aaa; - } - } - } - - &__over-under { - display: flex; - align-items: center; - justify-content: center; - margin: 0 0 0 0.2em; - font-size: 1em; - font-variant-numeric: tabular-nums; - padding: 0 0.4em; - line-height: 1.1; - min-width: 3.5em; - border-radius: 1em; - - &--over { - background-color: $general-late-color; - color: #fff; - } - - &--under { - background-color: #ffe900; - color: #000; - } - } - } - } } } + +.director-screen__bottom-bar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 20; + font-size: 4.1667vh; +} diff --git a/packages/webui/src/client/styles/countdown/presenter.scss b/packages/webui/src/client/styles/countdown/presenter.scss index df9a20d66a0..c6ee85323c3 100644 --- a/packages/webui/src/client/styles/countdown/presenter.scss +++ b/packages/webui/src/client/styles/countdown/presenter.scss @@ -162,99 +162,6 @@ $hold-status-color: $liveline-timecode-color; } .presenter-screen__rundown-status-bar { - display: grid; - grid-template-columns: auto fit-content(20em) fit-content(5em); - grid-template-rows: fit-content(1em); - font-size: 6em; - color: #888; - padding: 0 0.2em; - - .presenter-screen__rundown-status-bar__rundown-name { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - line-height: 1.44em; - } - - .presenter-screen__rundown-status-bar__t-timer { - margin-right: 1em; - font-size: 0.8em; - align-self: center; - justify-self: end; - - .t-timer-display { - display: flex; - align-items: stretch; - justify-content: flex-end; - font-weight: 500; - background: #333; - border-radius: 0; - overflow: hidden; - - &__label { - display: flex; - align-items: center; - color: #fff; - padding-left: 0.4em; - padding-right: 0.2em; - font-size: 1em; - text-transform: uppercase; - letter-spacing: 0.05em; - font-stretch: condensed; - } - - &__value { - display: flex; - align-items: center; - color: #fff; - font-variant-numeric: tabular-nums; - padding: 0 0.2em; - font-size: 1em; - - .t-timer-display__part { - &--dimmed { - color: #aaa; - } - } - } - - &__over-under { - display: flex; - align-items: center; - justify-content: center; - margin: 0 0 0 0.2em; - font-size: 1em; - font-variant-numeric: tabular-nums; - padding: 0 0.4em; - line-height: 1.1; - min-width: 3.5em; - border-radius: 1em; - - &--over { - background-color: $general-late-color; - color: #fff; - } - - &--under { - background-color: #ffe900; - color: #000; - } - } - } - } - - .presenter-screen__rundown-status-bar__countdown { - white-space: nowrap; - - color: $general-countdown-to-next-color; - - font-weight: 600; - font-size: 1.2em; - - &.over { - color: $general-late-color; - } - } } .presenter-screen__part + .presenter-screen__part { @@ -412,3 +319,4 @@ $hold-status-color: $liveline-timecode-color; } } } + diff --git a/packages/webui/src/client/styles/main.scss b/packages/webui/src/client/styles/main.scss index 254f12efbec..05a421722f9 100644 --- a/packages/webui/src/client/styles/main.scss +++ b/packages/webui/src/client/styles/main.scss @@ -38,6 +38,8 @@ input { @import 'overflowingContainer'; @import 'pieceStatusIcon'; @import 'prompter'; +@import 'tTimerDisplay'; +@import 'rundownStatusBar'; @import 'rundownList'; @import 'rundownSystemStatus'; @import 'settings'; diff --git a/packages/webui/src/client/styles/prompter.scss b/packages/webui/src/client/styles/prompter.scss index c3ca335957f..50d9c3c3eea 100644 --- a/packages/webui/src/client/styles/prompter.scss +++ b/packages/webui/src/client/styles/prompter.scss @@ -309,6 +309,18 @@ body.prompter-scrollbar { } } +.prompter-rundown-status-bar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 1100; +} + +.prompter-rundown-status-bar.presenter-screen__rundown-status-bar { + font-size: 6vh; +} + .script-text-formatted { --background-color: #000; --foreground-color: #fff; diff --git a/packages/webui/src/client/styles/rundownStatusBar.scss b/packages/webui/src/client/styles/rundownStatusBar.scss new file mode 100644 index 00000000000..ab4164f1378 --- /dev/null +++ b/packages/webui/src/client/styles/rundownStatusBar.scss @@ -0,0 +1,73 @@ +/** + * Styling for `ui/ClockView/RundownStatusBar.tsx`. + * + * View-level styles (Presenter/Prompter) should only handle placement and scaling. + */ +@import 'colorScheme'; + +.presenter-screen__rundown-status-bar { + display: grid; + grid-template-columns: auto fit-content(1em); + grid-template-rows: fit-content(1em); + font-size: 6em; + color: #888; + padding: 0 0 0 0.2em; + background: rgba(0, 0, 0, 0.75); + + .presenter-screen__rundown-status-bar__rundown-name { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + line-height: 1.44em; + } + + .presenter-screen__rundown-status-bar__right { + display: flex; + align-items: center; + justify-content: flex-end; + justify-self: end; + // Match the exact background of `.t-timer-display` + background: #333; + } + + .presenter-screen__rundown-status-bar__t-timer { + font-size: 1.2em; + display: flex; + align-items: center; + } + + .presenter-screen__rundown-status-bar__countdown { + white-space: nowrap; + + font-weight: 600; + font-size: 1.2em; + align-self: center; + + &.heavy-light { + display: inline-block; + border-radius: 1em; + padding: 0 0.2em 0 0.1em; + + &.light { + // Match `.prompter-timing-clock.heavy-light.light` + background-color: #ffe900; + color: #000; + } + + &.heavy { + // Match `.prompter-timing-clock.heavy-light.heavy` + background-color: $general-fast-color; + color: #fff; + text-shadow: + 1px 1px 0px #000, + 1px -1px 0px #000, + -1px -1px 0px #000, + -1px 1px 0px #000; + } + } + } + + &.presenter-screen__rundown-status-bar--no-title { + background: transparent; + } +} diff --git a/packages/webui/src/client/styles/tTimerDisplay.scss b/packages/webui/src/client/styles/tTimerDisplay.scss new file mode 100644 index 00000000000..fa8ceeeb90e --- /dev/null +++ b/packages/webui/src/client/styles/tTimerDisplay.scss @@ -0,0 +1,69 @@ +@import 'colorScheme'; + +/** + * Default styling for `ui/ClockView/TTimerDisplay.tsx`. + * + * Layout/positioning/font-size should be applied by the embedding view + * (eg. status bar, director screen), but the core look of the timer itself + * is defined here. + */ +.t-timer-display { + font-family: Roboto Flex; + display: flex; + align-items: stretch; + justify-content: flex-end; + font-weight: 500; + background: #333; + border-radius: 0; + overflow: hidden; + + &__label { + display: flex; + align-items: center; + color: #fff; + padding-right: 0.2em; + font-weight: inherit; + font-size: 1em; + text-transform: uppercase; + letter-spacing: 0.05em; + font-stretch: condensed; + } + + &__value { + display: flex; + align-items: center; + color: #fff; + font-variant-numeric: tabular-nums; + padding: 0 0.2em; + font-size: 1em; + + .t-timer-display__part { + &--dimmed { + color: #aaa; + } + } + } + + &__over-under { + display: flex; + align-items: center; + justify-content: center; + margin: 0 0 0 0.2em; + font-size: 1em; + font-variant-numeric: tabular-nums; + padding: 0 0.4em; + line-height: 1.1; + min-width: 3.5em; + border-radius: 1em; + + &--over { + background-color: $general-late-color; + color: #fff; + } + + &--under { + background-color: #ffe900; + color: #000; + } + } +} diff --git a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreen.tsx b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreen.tsx index 0887c899a50..d715a3a6782 100644 --- a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreen.tsx +++ b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreen.tsx @@ -44,13 +44,12 @@ import { AdjustLabelFit } from '../../util/AdjustLabelFit.js' import { AutoNextStatus } from '../../RundownView/RundownTiming/AutoNextStatus.js' import { useTranslation } from 'react-i18next' import { DBShowStyleBase, UIShowStyleBase } from '@sofie-automation/corelib/dist/dataModel/ShowStyleBase' -import { TTimerDisplay } from '../TTimerDisplay.js' -import { getDefaultTTimer } from '../../../lib/tTimerUtils.js' import { PieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance.js' import { DirectorScreenTop } from './DirectorScreenTop.js' import { useTiming } from '../../RundownView/RundownTiming/withTiming.js' import { UIStudio } from '@sofie-automation/corelib/src/dataModel/Studio.js' import { PartInstance } from '@sofie-automation/corelib/src/dataModel/PartInstance.js' +import { RundownStatusBar } from '../RundownStatusBar.js' interface SegmentUi extends DBSegment { items: Array @@ -574,8 +573,6 @@ function DirectorScreenRender({ } } - const activeTTimer = getDefaultTTimer(playlist.tTimers) - return (
@@ -761,12 +758,13 @@ function DirectorScreenRender({ ) : null}
- {!!activeTTimer && ( -
- -
- )} + ) } diff --git a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx index d8932727be9..af377e24b26 100644 --- a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx +++ b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx @@ -35,7 +35,7 @@ import { RundownLayoutsAPI } from '../../lib/rundownLayouts.js' import { ShelfDashboardLayout } from '../Shelf/ShelfDashboardLayout.js' import { parse as queryStringParse } from 'query-string' import { calculatePartInstanceExpectedDurationWithTransition } from '@sofie-automation/corelib/dist/playout/timings' -import { getPlaylistTimingDiff, RundownTimingContext } from '../../lib/rundownTiming.js' +import { RundownTimingContext } from '../../lib/rundownTiming.js' import { UIShowStyleBases, UIStudios } from '../Collections.js' import { PieceInstances, @@ -50,8 +50,7 @@ import { useSetDocumentClass, useSetDocumentDarkTheme } from '../util/useSetDocu import { useRundownAndShowStyleIdsForPlaylist } from '../util/useRundownAndShowStyleIdsForPlaylist.js' import { RundownPlaylistClientUtil } from '../../lib/rundownPlaylistUtil.js' import { CurrentPartOrSegmentRemaining } from '../RundownView/RundownHeader/CurrentPartOrSegmentRemaining.js' -import { TTimerDisplay } from './TTimerDisplay.js' -import { getDefaultTTimer } from '../../lib/tTimerUtils.js' +import { RundownStatusBar } from './RundownStatusBar.js' import { UIShowStyleBase } from '@sofie-automation/corelib/src/dataModel/ShowStyleBase.js' import { UIStudio } from '@sofie-automation/corelib/src/dataModel/Studio.js' import { PartInstance } from '@sofie-automation/corelib/src/dataModel/PartInstance.js' @@ -496,8 +495,6 @@ function PresenterScreenContentDefaultLayout({ timingDurations.remainingBudgetOnCurrentSegment ?? timingDurations.remainingTimeOnCurrentPart ?? 0 const expectedStart = PlaylistTiming.getExpectedStart(playlist.timing) - const overUnderClock = getPlaylistTimingDiff(playlist, timingDurations) ?? 0 - const activeTTimer = getDefaultTTimer(playlist.tTimers) return (
@@ -599,21 +596,7 @@ function PresenterScreenContentDefaultLayout({ ) : null}
-
-
- {playlist ? playlist.name : 'UNKNOWN'} -
-
- {!!activeTTimer && } -
-
= 0, - })} - > - {RundownUtils.formatDiffToTimecode(overUnderClock, true, false, true, true, true, undefined, true, true)} -
-
+ ) } diff --git a/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx new file mode 100644 index 00000000000..8b0f8c34ca8 --- /dev/null +++ b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx @@ -0,0 +1,54 @@ +import ClassNames from 'classnames' +import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist' +import { RundownUtils } from '../../lib/rundown.js' +import { getPlaylistTimingDiff } from '../../lib/rundownTiming.js' +import { getDefaultTTimer } from '../../lib/tTimerUtils.js' +import { useTiming } from '../RundownView/RundownTiming/withTiming.js' +import { TTimerDisplay } from './TTimerDisplay.js' + +interface RundownStatusBarProps { + playlist?: DBRundownPlaylist + className?: string + showPlaylistName?: boolean + showDiff?: boolean +} + +export function RundownStatusBar({ + playlist, + className, + showPlaylistName = true, + showDiff = true, +}: Readonly): JSX.Element { + const timingDurations = useTiming() + + const overUnderClock = playlist ? (getPlaylistTimingDiff(playlist, timingDurations) ?? 0) : 0 + const activeTTimer = playlist ? getDefaultTTimer(playlist.tTimers) : undefined + + return ( +
+ {showPlaylistName && ( +
{playlist ? playlist.name : 'UNKNOWN'}
+ )} +
+
+ {!!activeTTimer && } +
+ {showDiff && ( +
= 0, + light: Math.floor(overUnderClock / 1000) < 0, + })} + > + {RundownUtils.formatDiffToTimecode(overUnderClock, true, false, true, true, true, undefined, true, true)} +
+ )} +
+
+ ) +} diff --git a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx index 0f05043ed44..1059fc80058 100644 --- a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx +++ b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx @@ -24,7 +24,7 @@ export function TTimerDisplay({ timer }: Readonly): JSX.Elem return (
- {timer.label} + {timer.label ? {timer.label} : null} {timerSign} {timerParts.map((p, i) => ( diff --git a/packages/webui/src/client/ui/Prompter/PrompterView.tsx b/packages/webui/src/client/ui/Prompter/PrompterView.tsx index d8cde5358e9..173807da6d3 100644 --- a/packages/webui/src/client/ui/Prompter/PrompterView.tsx +++ b/packages/webui/src/client/ui/Prompter/PrompterView.tsx @@ -32,6 +32,7 @@ import { RundownTimingProvider } from '../RundownView/RundownTiming/RundownTimin import { StudioScreenSaver } from '../StudioScreenSaver/StudioScreenSaver.js' import { PrompterControlManager } from './controller/manager.js' import { OverUnderTimer } from './OverUnderTimer.js' +import { RundownStatusBar } from '../ClockView/RundownStatusBar.js' import { PrompterAPI, PrompterData, PrompterDataPart, PrompterDataPiece } from './prompter.js' import { doUserAction, UserAction } from '../../lib/clientUserAction.js' import { MeteorCall } from '../../lib/meteorApi.js' @@ -76,6 +77,7 @@ interface PrompterConfig { showScroll: boolean debug: boolean showOverUnder: boolean + showPlaylistName: boolean addBlankLine: boolean } @@ -220,6 +222,7 @@ export class PrompterViewContent extends React.Component )} + {this.configOptions.debug ? (
Date: Thu, 2 Apr 2026 16:51:35 +0200 Subject: [PATCH 02/13] chore: remove legacy timing clock --- .../webui/src/client/styles/tTimerDisplay.scss | 1 + .../src/client/ui/ClockView/RundownStatusBar.tsx | 14 -------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/packages/webui/src/client/styles/tTimerDisplay.scss b/packages/webui/src/client/styles/tTimerDisplay.scss index fa8ceeeb90e..3ef2a711252 100644 --- a/packages/webui/src/client/styles/tTimerDisplay.scss +++ b/packages/webui/src/client/styles/tTimerDisplay.scss @@ -21,6 +21,7 @@ display: flex; align-items: center; color: #fff; + padding-left: 0.2em; padding-right: 0.2em; font-weight: inherit; font-size: 1em; diff --git a/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx index 8b0f8c34ca8..61a8b07717e 100644 --- a/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx +++ b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx @@ -19,9 +19,6 @@ export function RundownStatusBar({ showPlaylistName = true, showDiff = true, }: Readonly): JSX.Element { - const timingDurations = useTiming() - - const overUnderClock = playlist ? (getPlaylistTimingDiff(playlist, timingDurations) ?? 0) : 0 const activeTTimer = playlist ? getDefaultTTimer(playlist.tTimers) : undefined return ( @@ -37,17 +34,6 @@ export function RundownStatusBar({
{!!activeTTimer && }
- {showDiff && ( -
= 0, - light: Math.floor(overUnderClock / 1000) < 0, - })} - > - {RundownUtils.formatDiffToTimecode(overUnderClock, true, false, true, true, true, undefined, true, true)} -
- )}
) From 5f71143756bda05152b87b8aa29c58435a36a2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 2 Apr 2026 17:12:15 +0200 Subject: [PATCH 03/13] feat: reusable counter component --- packages/webui/src/client/collections/lib.ts | 17 +++- .../webui/src/client/styles/_colorScheme.scss | 1 + .../client/styles/countdown/presenter.scss | 38 ++++++--- .../src/client/styles/counterComponents.scss | 4 +- .../src/client/styles/defaultColors.scss | 1 + .../webui/src/client/styles/prompter.scss | 32 +++++--- .../src/client/styles/rundownStatusBar.scss | 11 +-- .../src/client/styles/tTimerDisplay.scss | 80 ++++++++++++------- .../DirectorScreen/DirectorScreenTop.tsx | 10 +-- .../client/ui/ClockView/PresenterScreen.tsx | 35 ++++++++ .../client/ui/ClockView/RundownStatusBar.tsx | 4 - .../src/client/ui/ClockView/TTimerDisplay.tsx | 18 +---- .../src/client/ui/Prompter/OverUnderTimer.tsx | 73 +++++++++++++---- .../src/client/ui/Prompter/PrompterView.tsx | 8 +- 14 files changed, 230 insertions(+), 102 deletions(-) diff --git a/packages/webui/src/client/collections/lib.ts b/packages/webui/src/client/collections/lib.ts index 0b0aa7d8ef6..49d1053f6df 100644 --- a/packages/webui/src/client/collections/lib.ts +++ b/packages/webui/src/client/collections/lib.ts @@ -10,7 +10,7 @@ import { PeripheralDevicePubSubCollections, PeripheralDevicePubSubCollectionsNames, } from '@sofie-automation/shared-lib/dist/pubsub/peripheralDevice' -import { +import type { MongoCollection, MongoReadOnlyCollection, MongoCursor, @@ -22,7 +22,20 @@ import { import { CustomCollectionName as CustomCorelibCollectionName } from '@sofie-automation/corelib/dist/dataModel/Collections' import { CorelibPubSubCustomCollections } from '@sofie-automation/corelib/dist/pubsub' -export * from '@sofie-automation/meteor-lib/dist/collections/lib' +export type { + FieldNames, + FindOneOptions, + FindOptions, + IndexSpecifier, + MongoCollection, + MongoCursor, + MongoLiveQueryHandle, + MongoReadOnlyCollection, + ObserveCallbacks, + ObserveChangesCallbacks, + UpdateOptions, + UpsertOptions, +} from '@sofie-automation/meteor-lib/dist/collections/lib' export const ClientCollections = new Map | WrappedMongoReadOnlyCollection>() function registerClientCollection( diff --git a/packages/webui/src/client/styles/_colorScheme.scss b/packages/webui/src/client/styles/_colorScheme.scss index eea945a755c..603a0dd3bc3 100644 --- a/packages/webui/src/client/styles/_colorScheme.scss +++ b/packages/webui/src/client/styles/_colorScheme.scss @@ -19,6 +19,7 @@ $general-live-remote-color: var(--general-live-remote-color); $general-live-guest-color: var(--general-live-guest-color); $general-late-color: var(--general-late-color); +$over-under-over-color: var(--over-under-over-color); $general-fast-color: var(--general-fast-color); $general-fast-color--shadow: var(--general-fast-color--shadow); $general-countdown-to-next-color: var(--general-countdown-to-next-color); diff --git a/packages/webui/src/client/styles/countdown/presenter.scss b/packages/webui/src/client/styles/countdown/presenter.scss index c6ee85323c3..19da73e17e8 100644 --- a/packages/webui/src/client/styles/countdown/presenter.scss +++ b/packages/webui/src/client/styles/countdown/presenter.scss @@ -15,10 +15,7 @@ $hold-status-color: $liveline-timecode-color; .presenter-screen { position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; + inset: 0; font-size: 1vh; @@ -28,6 +25,22 @@ $hold-status-color: $liveline-timecode-color; overflow: hidden; white-space: nowrap; + // Over/under timer (reuses `screen-timing-clock` class from prompter view), + // but presenter view has a tiny base font-size (1vh), so we need explicit sizing + layering. + .screen-timing-clock { + position: fixed; + left: auto; + font-size: 9.8vmin; + + z-index: 2000; + + padding: 0 0.3em; + border-radius: 1em; + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.5); + + margin: 0; + } + .presenter-screen__part { display: grid; grid-template: @@ -47,9 +60,17 @@ $hold-status-color: $liveline-timecode-color; font-weight: bold; &.live { - background: $general-live-color; + // Layer the director-style gradient over the live color + background: + linear-gradient( + 90deg, + rgba(223, 0, 0, 0) 0%, + rgba(223, 0, 0, 0) 50%, + rgba(116, 0, 0, 0.808) 69%, + rgba(0, 0, 0, 0.8) 74% + ), + $general-live-color; color: #fff; - border-top: 0.1em solid #fff; -webkit-text-stroke: black; -webkit-text-stroke-width: 0.025em; text-shadow: 0px 0px 20px #00000044; @@ -58,7 +79,6 @@ $hold-status-color: $liveline-timecode-color; &.next { background: $general-next-color; color: #000; - border-top: 0.1em solid #fff; } } @@ -161,9 +181,6 @@ $hold-status-color: $liveline-timecode-color; } } - .presenter-screen__rundown-status-bar { - } - .presenter-screen__part + .presenter-screen__part { border-top: solid 0.8em #454545; } @@ -319,4 +336,3 @@ $hold-status-color: $liveline-timecode-color; } } } - diff --git a/packages/webui/src/client/styles/counterComponents.scss b/packages/webui/src/client/styles/counterComponents.scss index 36b206f604b..585b19054c8 100644 --- a/packages/webui/src/client/styles/counterComponents.scss +++ b/packages/webui/src/client/styles/counterComponents.scss @@ -62,7 +62,7 @@ display: inline-block; } .over { - background-color: #ff5218; + background-color: $over-under-over-color; border-radius: 139px; align-self: stretch; gap: 10px; @@ -124,7 +124,7 @@ } .counter-component__time-since-planned-end { - color: #ff5218; + color: $over-under-over-color; text-align: right; margin-left: 1.2vw; diff --git a/packages/webui/src/client/styles/defaultColors.scss b/packages/webui/src/client/styles/defaultColors.scss index 41be88ec005..86612b5e352 100644 --- a/packages/webui/src/client/styles/defaultColors.scss +++ b/packages/webui/src/client/styles/defaultColors.scss @@ -22,6 +22,7 @@ --general-live-guest-color: #008a92; --general-late-color: #ff0000; + --over-under-over-color: #ff5218; --general-fast-color: #ff0000; --general-countdown-to-next-color: #ffff00; --general-freeze-color: #00bfff; diff --git a/packages/webui/src/client/styles/prompter.scss b/packages/webui/src/client/styles/prompter.scss index 50d9c3c3eea..dd9722c251d 100644 --- a/packages/webui/src/client/styles/prompter.scss +++ b/packages/webui/src/client/styles/prompter.scss @@ -90,7 +90,8 @@ body.prompter-scrollbar { .prompter-part { text-align: center; letter-spacing: 0.1em; - font-size: 50%; + font-size: 63%; + height: 12.2vh; } .overlay-fix { pointer-events: none; @@ -129,7 +130,16 @@ body.prompter-scrollbar { &.live { //border-bottom: 0.2em solid #f55; - background: $general-live-color; + // Layer the director-style gradient over the live color + background: + linear-gradient( + 90deg, + rgba(223, 0, 0, 0) 0%, + rgba(223, 0, 0, 0) 50%, + rgba(116, 0, 0, 0.808) 68%, + rgba(0, 0, 0, 0.8) 73% + ), + $general-live-color; color: #fff; -webkit-text-stroke: black; -webkit-text-stroke-width: 0.025em; @@ -277,16 +287,19 @@ body.prompter-scrollbar { } } -.prompter-timing-clock { +.screen-timing-clock { + font-family: Roboto Flex; position: fixed; display: block; top: 0; right: 0; left: auto; - font-size: 75%; + font-size: 9.8vmin; padding: 0.05em 0.3em; border-radius: 1em; box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.5); + line-height: 1; + margin: 0; &.heavy-light { font-weight: 600; @@ -298,13 +311,12 @@ body.prompter-scrollbar { } &.heavy { - background-color: $general-fast-color; - color: #fff; + color: black; text-shadow: - 1px 1px 0px #000, - 1px -1px 0px #000, - -1px -1px 0px #000, - -1px 1px 0px #000; + 0.02em 1px 0px #000, + 0.02em -1px 0px #000, + -0.02em -1px 0px #000, + -0.02em 1px 0px #000; } } } diff --git a/packages/webui/src/client/styles/rundownStatusBar.scss b/packages/webui/src/client/styles/rundownStatusBar.scss index ab4164f1378..6a9cd3a549a 100644 --- a/packages/webui/src/client/styles/rundownStatusBar.scss +++ b/packages/webui/src/client/styles/rundownStatusBar.scss @@ -49,20 +49,15 @@ padding: 0 0.2em 0 0.1em; &.light { - // Match `.prompter-timing-clock.heavy-light.light` + // Match `.screen-timing-clock.heavy-light.light` background-color: #ffe900; color: #000; } &.heavy { - // Match `.prompter-timing-clock.heavy-light.heavy` + // Match `.screen-timing-clock.heavy-light.heavy` background-color: $general-fast-color; - color: #fff; - text-shadow: - 1px 1px 0px #000, - 1px -1px 0px #000, - -1px -1px 0px #000, - -1px 1px 0px #000; + color: #000; } } } diff --git a/packages/webui/src/client/styles/tTimerDisplay.scss b/packages/webui/src/client/styles/tTimerDisplay.scss index 3ef2a711252..d9200ed84f8 100644 --- a/packages/webui/src/client/styles/tTimerDisplay.scss +++ b/packages/webui/src/client/styles/tTimerDisplay.scss @@ -8,35 +8,41 @@ * is defined here. */ .t-timer-display { - font-family: Roboto Flex; display: flex; align-items: stretch; justify-content: flex-end; - font-weight: 500; + background: #333; border-radius: 0; overflow: hidden; + font-family: Roboto Flex; + font-weight: bold; + font-size: 1.2em; + letter-spacing: -0.01em; + line-height: 1; + &__label { display: flex; align-items: center; + + padding-left: 0.1em; + padding-right: 0.1em; + color: #fff; - padding-left: 0.2em; - padding-right: 0.2em; font-weight: inherit; - font-size: 1em; text-transform: uppercase; - letter-spacing: 0.05em; font-stretch: condensed; } &__value { display: flex; align-items: center; + padding: 0 0.1em 0 0.2em; + color: #fff; font-variant-numeric: tabular-nums; - padding: 0 0.2em; - font-size: 1em; + letter-spacing: -0.04em; .t-timer-display__part { &--dimmed { @@ -44,27 +50,47 @@ } } } +} - &__over-under { - display: flex; - align-items: center; - justify-content: center; - margin: 0 0 0 0.2em; - font-size: 1em; - font-variant-numeric: tabular-nums; - padding: 0 0.4em; - line-height: 1.1; - min-width: 3.5em; - border-radius: 1em; +/** + * Shared over/under pill styling (used by `OverUnderTimer` in multiple screens). + * + * View-level placement/scaling should be applied by the embedding view (eg. prompter/presenter/director), + * but the core look of the pill itself is defined here. + */ +.over-under-timer { + display: flex; + align-items: center; + justify-content: center; - &--over { - background-color: $general-late-color; - color: #fff; - } + min-width: 3.5em; + border-radius: 1em; + margin: 0 0 0 0.2em; + padding: 0 0.25em; - &--under { - background-color: #ffe900; - color: #000; - } + font-variant-numeric: tabular-nums; + text-shadow: + 0.003em 0.5px 0px #000, + 0.003em -0.5px 0px #000, + -0.003em -0.5px 0px #000, + -0.003em 0.5px 0px #000; + + &--over { + background-color: $over-under-over-color; + color: black; } + + &--under { + background-color: #ffe900; + color: #000; + } +} + +/** + * When the over/under pill is used as a standalone screen overlay (eg. prompter/presenter), + * don't apply the spacing/min-width that the inline `TTimerDisplay` layout expects. + */ +.screen-timing-clock.over-under-timer { + min-width: auto; + margin: 0; } diff --git a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx index b1748a01285..82ff4172677 100644 --- a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx +++ b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx @@ -1,16 +1,16 @@ import { - OverUnderClockComponent, PlannedEndComponent, TimeSincePlannedEndComponent, TimeToPlannedEndComponent, } from '../../../lib/Components/CounterComponents' -import { useTiming } from '../../RundownView/RundownTiming/withTiming' -import { getPlaylistTimingDiff } from '../../../lib/rundownTiming' +import { useTiming } from '../../RundownView/RundownTiming/withTiming.js' +import { getPlaylistTimingDiff } from '../../../lib/rundownTiming.js' import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist/RundownPlaylist' -import { getCurrentTime } from '../../../lib/systemTime' +import { getCurrentTime } from '../../../lib/systemTime.js' import { useTranslation } from 'react-i18next' import { PlaylistTiming } from '@sofie-automation/corelib/dist/playout/rundownTiming' import { PartInstance } from '@sofie-automation/corelib/src/dataModel/PartInstance' +import { OverUnderTimer } from '../../Prompter/OverUnderTimer.js' export interface DirectorScreenTopProps { playlist: DBRundownPlaylist @@ -77,7 +77,7 @@ export function DirectorScreenTop({ )}
- +
{t('Over/Under')}
diff --git a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx index af377e24b26..629502261a6 100644 --- a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx +++ b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx @@ -3,6 +3,7 @@ import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment' import { PartUi } from '../SegmentTimeline/SegmentTimelineContainer.js' import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist/RundownPlaylist' import { Rundown } from '@sofie-automation/corelib/dist/dataModel/Rundown' +import type { CSSProperties } from 'react' import { useTiming } from '../RundownView/RundownTiming/withTiming.js' import { useSubscription, @@ -54,6 +55,7 @@ import { RundownStatusBar } from './RundownStatusBar.js' import { UIShowStyleBase } from '@sofie-automation/corelib/src/dataModel/ShowStyleBase.js' import { UIStudio } from '@sofie-automation/corelib/src/dataModel/Studio.js' import { PartInstance } from '@sofie-automation/corelib/src/dataModel/PartInstance.js' +import { OverUnderTimer } from '../Prompter/OverUnderTimer.js' // TODO: We have another definition of this in the Director screen, and there is also another SegmentUI type. We should look into clearing this up. interface SegmentUi extends DBSegment { @@ -87,6 +89,8 @@ export interface PresenterScreenTrackedProps { rundownIds: RundownId[] rundownLayouts?: Array presenterLayoutId: RundownLayoutId | undefined + margin: number | undefined + fontSize: number | undefined } function getShowStyleBaseIdSegmentPartUi( @@ -217,6 +221,18 @@ export const getPresenterScreenReactive = ( const params = queryStringParse(location.search) const presenterLayoutId = protectString((params['presenterLayout'] as string) || '') + const margin = (() => { + // Support both `margin` (PrompterView) and `margins` / `m` (legacy/typos in URLs) + const raw = (params['margin'] ?? params['margins'] ?? params['m']) as string + const val = Number.parseInt(raw, 10) + return Number.isNaN(val) ? undefined : val + })() + const fontSize = (() => { + // Support both `fontsize` (PrompterView) and `fontSize` (camelCase URLs) + const raw = (params['fontsize'] ?? params['fontSize']) as string + const val = Number.parseInt(raw, 10) + return Number.isNaN(val) ? undefined : val + })() if (playlist) { rundowns = RundownPlaylistCollectionUtil.getRundownsOrdered(playlist) @@ -300,6 +316,8 @@ export const getPresenterScreenReactive = ( rundownLayouts: rundowns.length > 0 ? RundownLayouts.find({ showStyleBaseId: rundowns[0].showStyleBaseId }).fetch() : undefined, presenterLayoutId, + margin, + fontSize, } } @@ -378,6 +396,8 @@ export function PresenterScreen({ playlistId, studioId }: PresenterScreenProps): rundowns={presenterScreenProps?.rundowns ?? []} segments={presenterScreenProps?.segments ?? []} showStyleBaseIds={presenterScreenProps?.showStyleBaseIds ?? []} + margin={presenterScreenProps?.margin} + fontSize={presenterScreenProps?.fontSize} studio={presenterScreenProps?.studio} studioId={studioId} timingDurations={timing} @@ -489,8 +509,18 @@ function PresenterScreenContentDefaultLayout({ nextPartInstance, nextSegment, rundownIds, + margin, + fontSize, }: Readonly) { if (playlist && playlistId && segments) { + const overUnderStyle: CSSProperties = { + marginTop: margin ? `${margin}vh` : undefined, + marginBottom: margin ? `${margin}vh` : undefined, + marginRight: margin ? `${margin}vw` : undefined, + marginLeft: margin ? `${margin}vw` : undefined, + fontSize: (fontSize ?? 0) > 12 ? `12vmin` : undefined, + } + const currentPartOrSegmentCountdown = timingDurations.remainingBudgetOnCurrentSegment ?? timingDurations.remainingTimeOnCurrentPart ?? 0 @@ -499,6 +529,11 @@ function PresenterScreenContentDefaultLayout({ return (
+
): JSX.Element { const activeTTimer = playlist ? getDefaultTTimer(playlist.tTimers) : undefined diff --git a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx index 1059fc80058..f36eb0efa24 100644 --- a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx +++ b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx @@ -1,8 +1,9 @@ import { RundownTTimer } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist/TTimers' import { RundownUtils } from '../../lib/rundown.js' -import { calculateTTimerDiff, calculateTTimerOverUnder } from '../../lib/tTimerUtils' -import { useTiming } from '../RundownView/RundownTiming/withTiming' +import { calculateTTimerDiff, calculateTTimerOverUnder } from '../../lib/tTimerUtils.js' +import { useTiming } from '../RundownView/RundownTiming/withTiming.js' import classNames from 'classnames' +import { OverUnderTimer } from '../Prompter/OverUnderTimer.js' interface TTimerDisplayProps { timer: RundownTTimer @@ -17,7 +18,6 @@ export function TTimerDisplay({ timer }: Readonly): JSX.Elem const diff = calculateTTimerDiff(timer, now) const overUnder = calculateTTimerOverUnder(timer, now) - const timerStr = RundownUtils.formatDiffToTimecode(Math.abs(diff), false, true, true, false, true) const timerParts = timerStr.split(':') const timerSign = diff >= 0 ? '' : '-' @@ -39,17 +39,7 @@ export function TTimerDisplay({ timer }: Readonly): JSX.Elem ))} - {overUnder !== undefined && ( - 0, - 't-timer-display__over-under--under': overUnder <= 0, - })} - > - {overUnder > 0 ? '+' : '\u2013'} - {RundownUtils.formatDiffToTimecode(Math.abs(overUnder), false, true, true, false, true)} - - )} +
) } diff --git a/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx b/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx index ff7323b499f..7b1a23e1b55 100644 --- a/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx +++ b/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx @@ -1,32 +1,71 @@ -import * as React from 'react' -import { useTiming } from '../RundownView/RundownTiming/withTiming.js' -import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist/RundownPlaylist' +import React from 'react' +import classNames from 'classnames' +import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist' import { RundownUtils } from '../../lib/rundown.js' -import ClassNames from 'classnames' +import { useTiming } from '../RundownView/RundownTiming/withTiming.js' import { getPlaylistTimingDiff } from '../../lib/rundownTiming.js' -interface IProps { - rundownPlaylist: DBRundownPlaylist - style?: React.CSSProperties | undefined +type OverUnderTimerBaseProps = { + /** Optional wrapper around the badge, useful for screens that style via container font-size (eg. director). */ + containerClassName?: string + className?: string + style?: React.CSSProperties } +type OverUnderTimerValueProps = + | { + valueMs: number | undefined + rundownPlaylist?: never + } + | { + valueMs?: never + rundownPlaylist: DBRundownPlaylist + } + +type OverUnderTimerInnerProps = OverUnderTimerBaseProps & { valueMs: number | undefined } + /** - * Shows an over/under timer for the rundownPlaylist. Requires a RundownTimingContext from the RundownTimingProvider + * Over/under "pill" timer. + * Can either take a direct `valueMs` or a `rundownPlaylist` (requires RundownTiming context). */ -export function OverUnderTimer({ rundownPlaylist, style }: IProps): JSX.Element { +export function OverUnderTimer(props: Readonly): JSX.Element | null { + if ('valueMs' in props) { + return + } else { + return + } +} + +function OverUnderTimerFromPlaylist( + props: Readonly +): JSX.Element | null { const timingDurations = useTiming() + const valueMs = getPlaylistTimingDiff(props.rundownPlaylist, timingDurations) ?? 0 + return +} - const overUnderClock = getPlaylistTimingDiff(rundownPlaylist, timingDurations) ?? 0 +function OverUnderTimerInner(props: Readonly): JSX.Element | null { + const valueMs = props.valueMs + if (valueMs === undefined) return null - return ( + const isOver = valueMs > 0 + + const badge = ( = 0, - })} + style={props.style} + className={classNames( + 'over-under-timer', + { + 'over-under-timer--over': isOver, + 'over-under-timer--under': !isOver, + }, + props.className + )} > - {RundownUtils.formatDiffToTimecode(overUnderClock, true, false, true, true, true, undefined, true, true)} + {isOver ? '+' : '\u2013'} + {RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, true, true, false, true)} ) + + return props.containerClassName ?
{badge}
: badge } diff --git a/packages/webui/src/client/ui/Prompter/PrompterView.tsx b/packages/webui/src/client/ui/Prompter/PrompterView.tsx index 173807da6d3..611267c648c 100644 --- a/packages/webui/src/client/ui/Prompter/PrompterView.tsx +++ b/packages/webui/src/client/ui/Prompter/PrompterView.tsx @@ -31,13 +31,13 @@ import { UIStudios } from '../Collections.js' import { RundownTimingProvider } from '../RundownView/RundownTiming/RundownTimingProvider.js' import { StudioScreenSaver } from '../StudioScreenSaver/StudioScreenSaver.js' import { PrompterControlManager } from './controller/manager.js' -import { OverUnderTimer } from './OverUnderTimer.js' import { RundownStatusBar } from '../ClockView/RundownStatusBar.js' import { PrompterAPI, PrompterData, PrompterDataPart, PrompterDataPiece } from './prompter.js' import { doUserAction, UserAction } from '../../lib/clientUserAction.js' import { MeteorCall } from '../../lib/meteorApi.js' import { MdDisplay } from './Formatted/MdDisplay.js' import { UIStudio } from '@sofie-automation/corelib/src/dataModel/Studio.js' +import { OverUnderTimer } from './OverUnderTimer.js' const DEFAULT_UPDATE_THROTTLE = 250 //ms const PIECE_MISSING_UPDATE_THROTTLE = 2000 //ms @@ -613,7 +613,11 @@ export class PrompterViewContent extends React.Component {this.configOptions.showOverUnder && ( - + )} Date: Thu, 9 Apr 2026 12:31:54 +0200 Subject: [PATCH 04/13] fix: director screen rehearsal counter --- .../src/client/styles/countdown/director.scss | 23 ++++--- .../DirectorScreen/DirectorScreenTop.tsx | 68 +++++++++---------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/packages/webui/src/client/styles/countdown/director.scss b/packages/webui/src/client/styles/countdown/director.scss index d17850fee6d..af29abd8b96 100644 --- a/packages/webui/src/client/styles/countdown/director.scss +++ b/packages/webui/src/client/styles/countdown/director.scss @@ -17,6 +17,12 @@ $hold-status-color: $liveline-timecode-color; font-family: Roboto Flex; font-style: normal; + // Over/under timer overlay (reuses `screen-timing-clock` from prompter view) + // Ensure it layers above the fixed top bar. + .screen-timing-clock { + z-index: 2000; + } + .director-screen__top { position: fixed; top: 0; @@ -35,24 +41,19 @@ $hold-status-color: $liveline-timecode-color; padding: 0 0.2em; text-transform: uppercase; - .director-screen__top__planned-end { - text-align: left; + .director-screen__top__spacer { + flex-grow: 4; } - .director-screen__top__time-to { - text-align: center; + .director-screen__top__planned-end { + flex-grow: 2; + text-align: left; } + .director-screen__top__center, .director-screen__top__planned-to { text-align: center; } - .director-screen__top__planned-since { - margin-left: -50px; - } - - .director-screen__top__over-under { - margin-left: 5vw; - } } .director-screen__body { diff --git a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx index 82ff4172677..488ede9a7b0 100644 --- a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx +++ b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx @@ -41,46 +41,44 @@ export function DirectorScreenTop({ const { t } = useTranslation() return ( -
- {expectedEnd ? ( -
-
- + <> +
+ {expectedEnd ? ( +
+
+ +
+ {t('Planned End')}
- {t('Planned End')} -
- ) : null} - {expectedEnd && expectedEnd > now ? ( -
-
- + ) : null} + {expectedEnd && expectedEnd > now ? ( +
+
+ +
+ + {rehearsalInProgress ? t('Time to rehearsal end') : t('Time to planned end')} +
- - {rehearsalInProgress ? t('Time to rehearsal end') : t('Time to planned end')} - -
- ) : ( -
-
- - + ) : ( +
+
+ +
+ {rehearsalInProgress ? t('Time since rehearsal end') : t('Time since planned end')}
-
- )} -
-
- -
- {t('Over/Under')} + )} +
-
+ + ) } From ff7b79b017f754d26e4a7decea960c607daf62a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Tue, 14 Apr 2026 14:41:01 +0200 Subject: [PATCH 05/13] chore: fix broken import --- packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx | 2 +- packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx index b35cfe6abf1..0952600453f 100644 --- a/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx +++ b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx @@ -1,7 +1,7 @@ import ClassNames from 'classnames' -import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist' import { getDefaultTTimer } from '../../lib/tTimerUtils.js' import { TTimerDisplay } from './TTimerDisplay.js' +import { DBRundownPlaylist } from '@sofie-automation/corelib/src/dataModel/RundownPlaylist/RundownPlaylist.js' interface RundownStatusBarProps { playlist?: DBRundownPlaylist diff --git a/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx b/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx index 7b1a23e1b55..c2253282c29 100644 --- a/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx +++ b/packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx @@ -1,9 +1,9 @@ import React from 'react' import classNames from 'classnames' -import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist' import { RundownUtils } from '../../lib/rundown.js' import { useTiming } from '../RundownView/RundownTiming/withTiming.js' import { getPlaylistTimingDiff } from '../../lib/rundownTiming.js' +import { DBRundownPlaylist } from '@sofie-automation/corelib/src/dataModel/RundownPlaylist/RundownPlaylist.js' type OverUnderTimerBaseProps = { /** Optional wrapper around the badge, useful for screens that style via container font-size (eg. director). */ @@ -28,7 +28,9 @@ type OverUnderTimerInnerProps = OverUnderTimerBaseProps & { valueMs: number | un * Over/under "pill" timer. * Can either take a direct `valueMs` or a `rundownPlaylist` (requires RundownTiming context). */ -export function OverUnderTimer(props: Readonly): JSX.Element | null { +export function OverUnderTimer( + props: Readonly +): JSX.Element | null { if ('valueMs' in props) { return } else { From ddfe67af1017b70e9502a02e48ddf4d28e206954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 16 Apr 2026 09:43:22 +0200 Subject: [PATCH 06/13] chore: unify rundown header chips --- .../RundownHeader/RundownHeader.scss | 98 ++++++++----------- .../RundownHeaderOverUnderChip.tsx | 41 ++++++++ .../RundownHeader/RundownHeaderTimers.tsx | 11 +-- .../RundownHeaderTimingDisplay.tsx | 14 +-- 4 files changed, 87 insertions(+), 77 deletions(-) create mode 100644 packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss index f53d2a17f7d..60a6cd288c7 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss @@ -233,12 +233,20 @@ 'YTUC' 712; } - .rundown-header__clocks-diff__chip--under { + } + + .rundown-header__over-under-chip { + display: inline-block; + border-radius: 999px; + white-space: nowrap; + letter-spacing: -0.02em; + font-variant-numeric: tabular-nums; + color: #000; + + &.rundown-header__over-under-chip--default { font-size: 1.3em; padding: 0em 0.3em; line-height: 1em; - border-radius: 999px; - letter-spacing: -0.02em; font-variation-settings: 'wdth' 25, 'wght' 500, @@ -254,18 +262,19 @@ 'YTDE' -203, 'YTUC' 712; } - .rundown-header__clocks-diff__chip--over { - font-size: 1.3em; - padding: 0em 0.3em; - line-height: 1em; - border-radius: 999px; - letter-spacing: -0.02em; + + &.rundown-header__over-under-chip--compact { + font-size: 0.75em; + padding: 0.05em 0.25em; + margin-left: 0.25em; + margin-top: 0em; + line-height: 1; font-variation-settings: 'wdth' 25, - 'wght' 700, + 'wght' 600, 'slnt' 0, 'GRAD' 0, - 'opsz' 25, + 'opsz' 14, 'XOPQ' 96, 'XTRA' 468, 'YOPQ' 79, @@ -276,18 +285,29 @@ 'YTUC' 712; } - &.rundown-header__clocks-diff--under { - .rundown-header__clocks-diff__chip--under { - background-color: #ff0; // Should probably be changed to $general-fast-color; - color: #000; - } + &.rundown-header__over-under-chip--over { + background-color: $general-late-color; } - &.rundown-header__clocks-diff--over { - .rundown-header__clocks-diff__chip--over { - background-color: $general-late-color; - color: #000000; - } + &.rundown-header__over-under-chip--under { + background-color: #ff0; // Should probably be changed to $general-fast-color; + } + + &.rundown-header__over-under-chip--default.rundown-header__over-under-chip--over { + font-variation-settings: + 'wdth' 25, + 'wght' 700, + 'slnt' 0, + 'GRAD' 0, + 'opsz' 25, + 'XOPQ' 96, + 'XTRA' 468, + 'YOPQ' 79, + 'YTAS' 750, + 'YTFI' 738, + 'YTLC' 548, + 'YTDE' -203, + 'YTUC' 712; } } @@ -336,42 +356,6 @@ color: #fff; margin-right: 0em; } - - .rundown-header__clocks-timers__timer__over-under { - display: inline-block; - font-size: 0.75em; - padding: 0.05em 0.25em; - border-radius: 999px; - white-space: nowrap; - letter-spacing: -0.02em; - margin-left: 0.25em; - margin-top: 0em; - font-variant-numeric: tabular-nums; - font-variation-settings: - 'wdth' 25, - 'wght' 600, - 'slnt' 0, - 'GRAD' 0, - 'opsz' 14, - 'XOPQ' 96, - 'XTRA' 468, - 'YOPQ' 79, - 'YTAS' 750, - 'YTFI' 738, - 'YTLC' 548, - 'YTDE' -203, - 'YTUC' 712; - - &.rundown-header__clocks-timers__timer__over-under--over { - background-color: $general-late-color; - color: #000; - } - - &.rundown-header__clocks-timers__timer__over-under--under { - background-color: #ff0; - color: #000; - } - } } } diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx new file mode 100644 index 00000000000..70c9f3d77b2 --- /dev/null +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import classNames from 'classnames' +import { RundownUtils } from '../../../lib/rundown.js' + +export type RundownHeaderOverUnderChipVariant = 'default' | 'compact' +export type RundownHeaderOverUnderChipFormat = 'playlistDiff' | 'timerPostfix' + +export interface IRundownHeaderOverUnderChipProps { + valueMs: number + variant?: RundownHeaderOverUnderChipVariant + format?: RundownHeaderOverUnderChipFormat + className?: string +} + +export function RundownHeaderOverUnderChip({ + valueMs, + variant = 'default', + format = 'playlistDiff', + className, +}: Readonly): JSX.Element { + const isUnder = valueMs <= 0 + const timeStr = + format === 'timerPostfix' + ? RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, false, true) + : RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, true, true) + + return ( + + {isUnder ? '−' : '+'} + {timeStr} + + ) +} + diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx index c0c44556fad..84e39158364 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx @@ -6,6 +6,7 @@ import { calculateTTimerDiff, calculateTTimerOverUnder } from '../../../lib/tTim import classNames from 'classnames' import { getCurrentTime } from '../../../lib/systemTime' import { Countdown } from './Countdown' +import { RundownHeaderOverUnderChip } from './RundownHeaderOverUnderChip' interface IProps { tTimers: [RundownTTimer, RundownTTimer, RundownTTimer] @@ -61,15 +62,7 @@ function SingleTimer({ timer }: Readonly) { ms={mode.type === 'timeOfDay' ? undefined : diff} postfix={ overUnder ? ( - 0, - 'rundown-header__clocks-timers__timer__over-under--under': overUnder < 0, - })} - > - {overUnder > 0 ? '+' : '−'} - {RundownUtils.formatDiffToTimecode(Math.abs(overUnder), false, false, true, false, true)} - + ) : undefined } > diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx index 28c5c89c4b9..21540ba5b8e 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx @@ -3,7 +3,7 @@ import { PlaylistTiming } from '@sofie-automation/corelib/dist/playout/rundownTi import { useTranslation } from 'react-i18next' import { useTiming } from '../RundownTiming/withTiming' import { getPlaylistTimingDiff } from '../../../lib/rundownTiming' -import { RundownUtils } from '../../../lib/rundown.js' +import { RundownHeaderOverUnderChip } from './RundownHeaderOverUnderChip' export interface IRundownHeaderTimingDisplayProps { playlist: DBRundownPlaylist @@ -26,21 +26,13 @@ export function RundownHeaderTimingDisplay({ playlist }: IRundownHeaderTimingDis return null } - const timeStr = RundownUtils.formatDiffToTimecode(Math.abs(overUnderClock), false, false, true, true, true) const isUnder = overUnderClock <= 0 return (
- + {isUnder ? t('Under') : t('Over')} - - {isUnder ? '−' : '+'} - {timeStr} - +
) From 9bd57eba1df4eb4a775a89d184cb3374728019c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 16 Apr 2026 09:57:13 +0200 Subject: [PATCH 07/13] chore: simplify css --- .../RundownHeader/RundownHeader.scss | 101 ++++++++---------- .../RundownHeaderOverUnderChip.tsx | 9 +- .../RundownHeader/RundownHeaderTimers.tsx | 2 +- .../RundownHeaderTimingDisplay.tsx | 2 +- 4 files changed, 49 insertions(+), 65 deletions(-) diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss index 60a6cd288c7..4ac1e84202b 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss @@ -214,6 +214,16 @@ font-variant-numeric: tabular-nums; white-space: nowrap; + // Over/under chip defaults for the playlist diff context + .rundown-header__over-under-chip { + font-size: 1.3em; + --rhChipPaddingY: 0em; + --rhChipPaddingX: 0.3em; + --rhChipLineHeight: 1em; + --rhChipOpsz: 25; + --rhChipWdth: 25; + } + .rundown-header__clocks-diff__label { @extend %hoverable-label; font-size: 0.75em; @@ -242,73 +252,36 @@ letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: #000; + padding: var(--rhChipPaddingY, 0.05em) var(--rhChipPaddingX, 0.25em); + margin-left: var(--rhChipMarginLeft, 0em); + margin-top: var(--rhChipMarginTop, 0em); + line-height: var(--rhChipLineHeight, 1); + font-size: 1em; - &.rundown-header__over-under-chip--default { - font-size: 1.3em; - padding: 0em 0.3em; - line-height: 1em; - font-variation-settings: - 'wdth' 25, - 'wght' 500, - 'slnt' 0, - 'GRAD' 0, - 'opsz' 25, - 'XOPQ' 96, - 'XTRA' 468, - 'YOPQ' 79, - 'YTAS' 750, - 'YTFI' 738, - 'YTLC' 548, - 'YTDE' -203, - 'YTUC' 712; - } - - &.rundown-header__over-under-chip--compact { - font-size: 0.75em; - padding: 0.05em 0.25em; - margin-left: 0.25em; - margin-top: 0em; - line-height: 1; - font-variation-settings: - 'wdth' 25, - 'wght' 600, - 'slnt' 0, - 'GRAD' 0, - 'opsz' 14, - 'XOPQ' 96, - 'XTRA' 468, - 'YOPQ' 79, - 'YTAS' 750, - 'YTFI' 738, - 'YTLC' 548, - 'YTDE' -203, - 'YTUC' 712; - } + font-variation-settings: + 'wdth' var(--rhChipWdth, 25), + 'wght' var(--rhChipWght, 600), + 'slnt' var(--rhChipSlnt, 0), + 'GRAD' var(--rhChipGrad, 0), + 'opsz' var(--rhChipOpsz, 14), + 'XOPQ' var(--rhChipXopq, 96), + 'XTRA' var(--rhChipXtra, 468), + 'YOPQ' var(--rhChipYopq, 79), + 'YTAS' var(--rhChipYtas, 750), + 'YTFI' var(--rhChipYtfi, 738), + 'YTLC' var(--rhChipYtlc, 548), + 'YTDE' var(--rhChipYtde, -203), + 'YTUC' var(--rhChipYtuc, 712); &.rundown-header__over-under-chip--over { + --rhChipWght: 700; background-color: $general-late-color; } &.rundown-header__over-under-chip--under { + --rhChipWght: 500; background-color: #ff0; // Should probably be changed to $general-fast-color; } - - &.rundown-header__over-under-chip--default.rundown-header__over-under-chip--over { - font-variation-settings: - 'wdth' 25, - 'wght' 700, - 'slnt' 0, - 'GRAD' 0, - 'opsz' 25, - 'XOPQ' 96, - 'XTRA' 468, - 'YOPQ' 79, - 'YTAS' 750, - 'YTFI' 738, - 'YTLC' 548, - 'YTDE' -203, - 'YTUC' 712; - } } .rundown-header__clocks-timers { @@ -329,6 +302,18 @@ white-space: nowrap; line-height: 1.25; + // Over/under chip defaults for the timer postfix context + .rundown-header__over-under-chip { + font-size: 0.75em; + --rhChipPaddingY: 0.05em; + --rhChipPaddingX: 0.25em; + --rhChipMarginLeft: 0.25em; + --rhChipMarginTop: 0em; + --rhChipLineHeight: 1; + --rhChipOpsz: 14; + --rhChipWdth: 25; + } + .countdown__label { @extend %hoverable-label; margin-left: 0; diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx index 70c9f3d77b2..766329f1ef6 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx @@ -1,22 +1,21 @@ -import React from 'react' +import React, { type CSSProperties } from 'react' import classNames from 'classnames' import { RundownUtils } from '../../../lib/rundown.js' -export type RundownHeaderOverUnderChipVariant = 'default' | 'compact' export type RundownHeaderOverUnderChipFormat = 'playlistDiff' | 'timerPostfix' export interface IRundownHeaderOverUnderChipProps { valueMs: number - variant?: RundownHeaderOverUnderChipVariant format?: RundownHeaderOverUnderChipFormat className?: string + style?: CSSProperties } export function RundownHeaderOverUnderChip({ valueMs, - variant = 'default', format = 'playlistDiff', className, + style, }: Readonly): JSX.Element { const isUnder = valueMs <= 0 const timeStr = @@ -28,10 +27,10 @@ export function RundownHeaderOverUnderChip({ {isUnder ? '−' : '+'} {timeStr} diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx index 84e39158364..8404dcbe0c8 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx @@ -62,7 +62,7 @@ function SingleTimer({ timer }: Readonly) { ms={mode.type === 'timeOfDay' ? undefined : diff} postfix={ overUnder ? ( - + ) : undefined } > diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx index 21540ba5b8e..075fd36585e 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx @@ -32,7 +32,7 @@ export function RundownHeaderTimingDisplay({ playlist }: IRundownHeaderTimingDis
{isUnder ? t('Under') : t('Over')} - +
) From 99a4a0bfb7b4f0a529f0847e7cfcd74dfa1f0985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 16 Apr 2026 10:25:29 +0200 Subject: [PATCH 08/13] chore: extract rundown header chip into generic component --- .../client/lib/Components/OverUnderChip.scss | 42 ++++++++++++ .../Components/OverUnderChip.tsx} | 19 +++--- .../RundownHeader/RundownHeader.scss | 67 ++++--------------- .../RundownHeader/RundownHeaderTimers.tsx | 4 +- .../RundownHeaderTimingDisplay.tsx | 4 +- 5 files changed, 68 insertions(+), 68 deletions(-) create mode 100644 packages/webui/src/client/lib/Components/OverUnderChip.scss rename packages/webui/src/client/{ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx => lib/Components/OverUnderChip.tsx} (51%) diff --git a/packages/webui/src/client/lib/Components/OverUnderChip.scss b/packages/webui/src/client/lib/Components/OverUnderChip.scss new file mode 100644 index 00000000000..c3f5181b7a4 --- /dev/null +++ b/packages/webui/src/client/lib/Components/OverUnderChip.scss @@ -0,0 +1,42 @@ +@import '../../styles/colorScheme'; + +.over-under-chip { + display: inline-block; + border-radius: 999px; + white-space: nowrap; + letter-spacing: -0.02em; + font-variant-numeric: tabular-nums; + color: #000; + + padding: var(--overUnderChipPaddingY, 0.05em) var(--overUnderChipPaddingX, 0.25em); + margin-left: var(--overUnderChipMarginLeft, 0em); + margin-top: var(--overUnderChipMarginTop, 0em); + line-height: var(--overUnderChipLineHeight, 1); + font-size: 1em; + + font-variation-settings: + 'wdth' var(--overUnderChipWdth, 25), + 'wght' var(--overUnderChipWght, 600), + 'slnt' var(--overUnderChipSlnt, 0), + 'GRAD' var(--overUnderChipGrad, 0), + 'opsz' var(--overUnderChipOpsz, 14), + 'XOPQ' var(--overUnderChipXopq, 96), + 'XTRA' var(--overUnderChipXtra, 468), + 'YOPQ' var(--overUnderChipYopq, 79), + 'YTAS' var(--overUnderChipYtas, 750), + 'YTFI' var(--overUnderChipYtfi, 738), + 'YTLC' var(--overUnderChipYtlc, 548), + 'YTDE' var(--overUnderChipYtde, -203), + 'YTUC' var(--overUnderChipYtuc, 712); + + &.over-under-chip--over { + --overUnderChipWght: 700; + background-color: $general-late-color; + } + + &.over-under-chip--under { + --overUnderChipWght: 500; + background-color: #ff0; // Should probably be changed to $general-fast-color; + } +} + diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx b/packages/webui/src/client/lib/Components/OverUnderChip.tsx similarity index 51% rename from packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx rename to packages/webui/src/client/lib/Components/OverUnderChip.tsx index 766329f1ef6..33b1247342c 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderOverUnderChip.tsx +++ b/packages/webui/src/client/lib/Components/OverUnderChip.tsx @@ -1,22 +1,23 @@ import React, { type CSSProperties } from 'react' import classNames from 'classnames' -import { RundownUtils } from '../../../lib/rundown.js' +import { RundownUtils } from '../rundown.js' +import './OverUnderChip.scss' -export type RundownHeaderOverUnderChipFormat = 'playlistDiff' | 'timerPostfix' +export type OverUnderChipFormat = 'playlistDiff' | 'timerPostfix' -export interface IRundownHeaderOverUnderChipProps { +export interface IOverUnderChipProps { valueMs: number - format?: RundownHeaderOverUnderChipFormat + format?: OverUnderChipFormat className?: string style?: CSSProperties } -export function RundownHeaderOverUnderChip({ +export function OverUnderChip({ valueMs, format = 'playlistDiff', className, style, -}: Readonly): JSX.Element { +}: Readonly): JSX.Element { const isUnder = valueMs <= 0 const timeStr = format === 'timerPostfix' @@ -25,11 +26,7 @@ export function RundownHeaderOverUnderChip({ return ( {isUnder ? '−' : '+'} diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss index 4ac1e84202b..f729c050a8b 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeader.scss @@ -215,13 +215,13 @@ white-space: nowrap; // Over/under chip defaults for the playlist diff context - .rundown-header__over-under-chip { + .over-under-chip { font-size: 1.3em; - --rhChipPaddingY: 0em; - --rhChipPaddingX: 0.3em; - --rhChipLineHeight: 1em; - --rhChipOpsz: 25; - --rhChipWdth: 25; + --overUnderChipPaddingY: 0em; + --overUnderChipPaddingX: 0.3em; + --overUnderChipLineHeight: 1em; + --overUnderChipOpsz: 25; + --overUnderChipWdth: 25; } .rundown-header__clocks-diff__label { @@ -245,45 +245,6 @@ } - .rundown-header__over-under-chip { - display: inline-block; - border-radius: 999px; - white-space: nowrap; - letter-spacing: -0.02em; - font-variant-numeric: tabular-nums; - color: #000; - padding: var(--rhChipPaddingY, 0.05em) var(--rhChipPaddingX, 0.25em); - margin-left: var(--rhChipMarginLeft, 0em); - margin-top: var(--rhChipMarginTop, 0em); - line-height: var(--rhChipLineHeight, 1); - font-size: 1em; - - font-variation-settings: - 'wdth' var(--rhChipWdth, 25), - 'wght' var(--rhChipWght, 600), - 'slnt' var(--rhChipSlnt, 0), - 'GRAD' var(--rhChipGrad, 0), - 'opsz' var(--rhChipOpsz, 14), - 'XOPQ' var(--rhChipXopq, 96), - 'XTRA' var(--rhChipXtra, 468), - 'YOPQ' var(--rhChipYopq, 79), - 'YTAS' var(--rhChipYtas, 750), - 'YTFI' var(--rhChipYtfi, 738), - 'YTLC' var(--rhChipYtlc, 548), - 'YTDE' var(--rhChipYtde, -203), - 'YTUC' var(--rhChipYtuc, 712); - - &.rundown-header__over-under-chip--over { - --rhChipWght: 700; - background-color: $general-late-color; - } - - &.rundown-header__over-under-chip--under { - --rhChipWght: 500; - background-color: #ff0; // Should probably be changed to $general-fast-color; - } - } - .rundown-header__clocks-timers { margin-left: auto; display: grid; @@ -303,15 +264,15 @@ line-height: 1.25; // Over/under chip defaults for the timer postfix context - .rundown-header__over-under-chip { + .over-under-chip { font-size: 0.75em; - --rhChipPaddingY: 0.05em; - --rhChipPaddingX: 0.25em; - --rhChipMarginLeft: 0.25em; - --rhChipMarginTop: 0em; - --rhChipLineHeight: 1; - --rhChipOpsz: 14; - --rhChipWdth: 25; + --overUnderChipPaddingY: 0.05em; + --overUnderChipPaddingX: 0.25em; + --overUnderChipMarginLeft: 0.25em; + --overUnderChipMarginTop: 0em; + --overUnderChipLineHeight: 1; + --overUnderChipOpsz: 14; + --overUnderChipWdth: 25; } .countdown__label { diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx index 8404dcbe0c8..5453d2032f2 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx @@ -6,7 +6,7 @@ import { calculateTTimerDiff, calculateTTimerOverUnder } from '../../../lib/tTim import classNames from 'classnames' import { getCurrentTime } from '../../../lib/systemTime' import { Countdown } from './Countdown' -import { RundownHeaderOverUnderChip } from './RundownHeaderOverUnderChip' +import { OverUnderChip } from '../../../lib/Components/OverUnderChip' interface IProps { tTimers: [RundownTTimer, RundownTTimer, RundownTTimer] @@ -62,7 +62,7 @@ function SingleTimer({ timer }: Readonly) { ms={mode.type === 'timeOfDay' ? undefined : diff} postfix={ overUnder ? ( - + ) : undefined } > diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx index 075fd36585e..14462d966a2 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimingDisplay.tsx @@ -3,7 +3,7 @@ import { PlaylistTiming } from '@sofie-automation/corelib/dist/playout/rundownTi import { useTranslation } from 'react-i18next' import { useTiming } from '../RundownTiming/withTiming' import { getPlaylistTimingDiff } from '../../../lib/rundownTiming' -import { RundownHeaderOverUnderChip } from './RundownHeaderOverUnderChip' +import { OverUnderChip } from '../../../lib/Components/OverUnderChip' export interface IRundownHeaderTimingDisplayProps { playlist: DBRundownPlaylist @@ -32,7 +32,7 @@ export function RundownHeaderTimingDisplay({ playlist }: IRundownHeaderTimingDis
{isUnder ? t('Under') : t('Over')} - +
) From 061b562dfbc96f941489289854bf0ee84886a443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 16 Apr 2026 10:43:37 +0200 Subject: [PATCH 09/13] chore: use the global component on the director screen --- packages/webui/src/client/styles/countdown/director.scss | 3 +++ .../client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/webui/src/client/styles/countdown/director.scss b/packages/webui/src/client/styles/countdown/director.scss index af29abd8b96..91d43931234 100644 --- a/packages/webui/src/client/styles/countdown/director.scss +++ b/packages/webui/src/client/styles/countdown/director.scss @@ -21,6 +21,9 @@ $hold-status-color: $liveline-timecode-color; // Ensure it layers above the fixed top bar. .screen-timing-clock { z-index: 2000; + font-size: 9.8vmin; + --overUnderChipPaddingY: 0em; + --overUnderChipPaddingX: 0.2em; } .director-screen__top { diff --git a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx index 488ede9a7b0..04f7d3c491d 100644 --- a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx +++ b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx @@ -11,6 +11,7 @@ import { useTranslation } from 'react-i18next' import { PlaylistTiming } from '@sofie-automation/corelib/dist/playout/rundownTiming' import { PartInstance } from '@sofie-automation/corelib/src/dataModel/PartInstance' import { OverUnderTimer } from '../../Prompter/OverUnderTimer.js' +import { OverUnderChip } from '../../../lib/Components/OverUnderChip.js' export interface DirectorScreenTopProps { playlist: DBRundownPlaylist @@ -78,7 +79,7 @@ export function DirectorScreenTop({ )}
- + ) } From a25342ca8d580a61c2e7ce77df1361a00c935dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 16 Apr 2026 11:26:17 +0200 Subject: [PATCH 10/13] chore: use global component on the presenter and prompter screens --- .../client/lib/Components/OverUnderChip.scss | 3 +- .../client/lib/Components/OverUnderChip.tsx | 67 +++++++++++++++---- .../src/client/styles/countdown/director.scss | 2 +- .../client/styles/countdown/presenter.scss | 6 +- .../webui/src/client/styles/prompter.scss | 28 +------- .../DirectorScreen/DirectorScreenTop.tsx | 1 - .../client/ui/ClockView/PresenterScreen.tsx | 18 +---- .../src/client/ui/Prompter/PrompterView.tsx | 7 +- 8 files changed, 61 insertions(+), 71 deletions(-) diff --git a/packages/webui/src/client/lib/Components/OverUnderChip.scss b/packages/webui/src/client/lib/Components/OverUnderChip.scss index c3f5181b7a4..ff273a5c02a 100644 --- a/packages/webui/src/client/lib/Components/OverUnderChip.scss +++ b/packages/webui/src/client/lib/Components/OverUnderChip.scss @@ -1,6 +1,7 @@ @import '../../styles/colorScheme'; .over-under-chip { + font-family: Roboto Flex; display: inline-block; border-radius: 999px; white-space: nowrap; @@ -12,7 +13,6 @@ margin-left: var(--overUnderChipMarginLeft, 0em); margin-top: var(--overUnderChipMarginTop, 0em); line-height: var(--overUnderChipLineHeight, 1); - font-size: 1em; font-variation-settings: 'wdth' var(--overUnderChipWdth, 25), @@ -39,4 +39,3 @@ background-color: #ff0; // Should probably be changed to $general-fast-color; } } - diff --git a/packages/webui/src/client/lib/Components/OverUnderChip.tsx b/packages/webui/src/client/lib/Components/OverUnderChip.tsx index 33b1247342c..5e6b52d30b1 100644 --- a/packages/webui/src/client/lib/Components/OverUnderChip.tsx +++ b/packages/webui/src/client/lib/Components/OverUnderChip.tsx @@ -2,27 +2,66 @@ import React, { type CSSProperties } from 'react' import classNames from 'classnames' import { RundownUtils } from '../rundown.js' import './OverUnderChip.scss' +import { useTiming } from '../../ui/RundownView/RundownTiming/withTiming.js' +import { getPlaylistTimingDiff } from '../rundownTiming.js' +import { DBRundownPlaylist } from '@sofie-automation/corelib/src/dataModel/RundownPlaylist/RundownPlaylist.js' -export type OverUnderChipFormat = 'playlistDiff' | 'timerPostfix' +export type OverUnderChipFormat = 'playlistDiff' | 'timerPostfix' | 'screenOverlay' -export interface IOverUnderChipProps { - valueMs: number - format?: OverUnderChipFormat +type OverUnderChipBaseProps = { className?: string style?: CSSProperties + format?: OverUnderChipFormat } -export function OverUnderChip({ - valueMs, - format = 'playlistDiff', - className, - style, -}: Readonly): JSX.Element { +type OverUnderChipValueProps = + | { + valueMs: number | undefined + rundownPlaylist?: never + } + | { + valueMs?: never + rundownPlaylist: DBRundownPlaylist + } + +type OverUnderChipInnerProps = OverUnderChipBaseProps & { valueMs: number | undefined } + +/** + * Over/under "chip" display. + * Can either take a direct `valueMs` or a `rundownPlaylist` (requires RundownTiming context). + */ +export function OverUnderChip(props: Readonly): JSX.Element | null { + if ('valueMs' in props) { + return + } else { + return + } +} + +function OverUnderChipFromPlaylist( + props: Readonly +): JSX.Element | null { + const timingDurations = useTiming() + const valueMs = getPlaylistTimingDiff(props.rundownPlaylist, timingDurations) ?? 0 + return +} + +function OverUnderChipInner({ valueMs, format = 'playlistDiff', className, style }: Readonly) { + if (valueMs === undefined) return null + const isUnder = valueMs <= 0 - const timeStr = - format === 'timerPostfix' - ? RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, false, true) - : RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, true, true) + const timeStr = (() => { + switch (format) { + case 'timerPostfix': + return RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, false, true) + case 'screenOverlay': + // Match legacy `OverUnderTimer` formatting used by prompter/presenter/director overlays + return RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, true, true, false, true) + case 'playlistDiff': + default: + return RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, true, true) + } + })() return ( ) { if (playlist && playlistId && segments) { - const overUnderStyle: CSSProperties = { - marginTop: margin ? `${margin}vh` : undefined, - marginBottom: margin ? `${margin}vh` : undefined, - marginRight: margin ? `${margin}vw` : undefined, - marginLeft: margin ? `${margin}vw` : undefined, - fontSize: (fontSize ?? 0) > 12 ? `12vmin` : undefined, - } - const currentPartOrSegmentCountdown = timingDurations.remainingBudgetOnCurrentSegment ?? timingDurations.remainingTimeOnCurrentPart ?? 0 @@ -529,11 +519,7 @@ function PresenterScreenContentDefaultLayout({ return (
- +
{this.configOptions.showOverUnder && ( - + )} Date: Thu, 16 Apr 2026 13:05:37 +0200 Subject: [PATCH 11/13] chore: use global component in the status bar --- .../src/client/styles/countdown/director.scss | 2 +- .../client/styles/countdown/presenter.scss | 3 ++ .../src/client/styles/tTimerDisplay.scss | 46 +------------------ .../src/client/ui/ClockView/TTimerDisplay.tsx | 4 +- .../src/client/ui/Prompter/PrompterView.tsx | 9 ---- 5 files changed, 7 insertions(+), 57 deletions(-) diff --git a/packages/webui/src/client/styles/countdown/director.scss b/packages/webui/src/client/styles/countdown/director.scss index c506f7a8d7b..1714e502e61 100644 --- a/packages/webui/src/client/styles/countdown/director.scss +++ b/packages/webui/src/client/styles/countdown/director.scss @@ -493,5 +493,5 @@ $hold-status-color: $liveline-timecode-color; right: 0; bottom: 0; z-index: 20; - font-size: 4.1667vh; + font-size: 4.95vh; } diff --git a/packages/webui/src/client/styles/countdown/presenter.scss b/packages/webui/src/client/styles/countdown/presenter.scss index 3a2b123c99b..3e397b454f2 100644 --- a/packages/webui/src/client/styles/countdown/presenter.scss +++ b/packages/webui/src/client/styles/countdown/presenter.scss @@ -331,4 +331,7 @@ $hold-status-color: $liveline-timecode-color; } } } + .presenter-screen__rundown-status-bar { + font-size: 7.5vmin; + } } diff --git a/packages/webui/src/client/styles/tTimerDisplay.scss b/packages/webui/src/client/styles/tTimerDisplay.scss index d9200ed84f8..5ee8428bd61 100644 --- a/packages/webui/src/client/styles/tTimerDisplay.scss +++ b/packages/webui/src/client/styles/tTimerDisplay.scss @@ -17,8 +17,7 @@ overflow: hidden; font-family: Roboto Flex; - font-weight: bold; - font-size: 1.2em; + font-size: 1em; letter-spacing: -0.01em; line-height: 1; @@ -51,46 +50,3 @@ } } } - -/** - * Shared over/under pill styling (used by `OverUnderTimer` in multiple screens). - * - * View-level placement/scaling should be applied by the embedding view (eg. prompter/presenter/director), - * but the core look of the pill itself is defined here. - */ -.over-under-timer { - display: flex; - align-items: center; - justify-content: center; - - min-width: 3.5em; - border-radius: 1em; - margin: 0 0 0 0.2em; - padding: 0 0.25em; - - font-variant-numeric: tabular-nums; - text-shadow: - 0.003em 0.5px 0px #000, - 0.003em -0.5px 0px #000, - -0.003em -0.5px 0px #000, - -0.003em 0.5px 0px #000; - - &--over { - background-color: $over-under-over-color; - color: black; - } - - &--under { - background-color: #ffe900; - color: #000; - } -} - -/** - * When the over/under pill is used as a standalone screen overlay (eg. prompter/presenter), - * don't apply the spacing/min-width that the inline `TTimerDisplay` layout expects. - */ -.screen-timing-clock.over-under-timer { - min-width: auto; - margin: 0; -} diff --git a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx index f36eb0efa24..95e667df3fa 100644 --- a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx +++ b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx @@ -3,7 +3,7 @@ import { RundownUtils } from '../../lib/rundown.js' import { calculateTTimerDiff, calculateTTimerOverUnder } from '../../lib/tTimerUtils.js' import { useTiming } from '../RundownView/RundownTiming/withTiming.js' import classNames from 'classnames' -import { OverUnderTimer } from '../Prompter/OverUnderTimer.js' +import { OverUnderChip } from '../../lib/Components/OverUnderChip.js' interface TTimerDisplayProps { timer: RundownTTimer @@ -39,7 +39,7 @@ export function TTimerDisplay({ timer }: Readonly): JSX.Elem ))} - +
) } diff --git a/packages/webui/src/client/ui/Prompter/PrompterView.tsx b/packages/webui/src/client/ui/Prompter/PrompterView.tsx index 24b41f81d05..d010aa2c045 100644 --- a/packages/webui/src/client/ui/Prompter/PrompterView.tsx +++ b/packages/webui/src/client/ui/Prompter/PrompterView.tsx @@ -37,7 +37,6 @@ import { doUserAction, UserAction } from '../../lib/clientUserAction.js' import { MeteorCall } from '../../lib/meteorApi.js' import { MdDisplay } from './Formatted/MdDisplay.js' import { UIStudio } from '@sofie-automation/corelib/src/dataModel/Studio.js' -import { OverUnderTimer } from './OverUnderTimer.js' import { OverUnderChip } from '../../lib/Components/OverUnderChip.js' const DEFAULT_UPDATE_THROTTLE = 250 //ms @@ -591,14 +590,6 @@ export class PrompterViewContent extends React.Component 12 ? `12vmin` : undefined, - } - return ( {!this.props.subsReady ? ( From f23633c976e35f986a5799b93ad754229ec0838b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 16 Apr 2026 16:35:38 +0200 Subject: [PATCH 12/13] chore: use global countdown component for TTimerDisplay --- .../client/lib/Components/OverUnderChip.scss | 7 ++ .../client/lib/Components/OverUnderChip.tsx | 6 +- .../src/client/styles/countdown/director.scss | 19 +++-- .../client/styles/countdown/presenter.scss | 4 +- .../webui/src/client/styles/prompter.scss | 4 +- .../src/client/styles/rundownStatusBar.scss | 16 ++--- .../src/client/styles/tTimerDisplay.scss | 69 +++++++++++++------ .../client/ui/ClockView/PresenterScreen.tsx | 1 - .../client/ui/ClockView/RundownStatusBar.tsx | 12 ++-- .../src/client/ui/ClockView/TTimerDisplay.tsx | 29 +++----- 10 files changed, 88 insertions(+), 79 deletions(-) diff --git a/packages/webui/src/client/lib/Components/OverUnderChip.scss b/packages/webui/src/client/lib/Components/OverUnderChip.scss index ff273a5c02a..913c0e2642f 100644 --- a/packages/webui/src/client/lib/Components/OverUnderChip.scss +++ b/packages/webui/src/client/lib/Components/OverUnderChip.scss @@ -29,6 +29,13 @@ 'YTDE' var(--overUnderChipYtde, -203), 'YTUC' var(--overUnderChipYtuc, 712); + &.screen-timing-clock { + --overUnderChipWght: 700; + --overUnderChipOpsz: 20; + --overUnderChipYopq: 92; + --overUnderChipYtlc: 514; + } + &.over-under-chip--over { --overUnderChipWght: 700; background-color: $general-late-color; diff --git a/packages/webui/src/client/lib/Components/OverUnderChip.tsx b/packages/webui/src/client/lib/Components/OverUnderChip.tsx index 5e6b52d30b1..98b0446756c 100644 --- a/packages/webui/src/client/lib/Components/OverUnderChip.tsx +++ b/packages/webui/src/client/lib/Components/OverUnderChip.tsx @@ -1,4 +1,4 @@ -import React, { type CSSProperties } from 'react' +import { type CSSProperties } from 'react' import classNames from 'classnames' import { RundownUtils } from '../rundown.js' import './OverUnderChip.scss' @@ -54,9 +54,6 @@ function OverUnderChipInner({ valueMs, format = 'playlistDiff', className, style switch (format) { case 'timerPostfix': return RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, false, true) - case 'screenOverlay': - // Match legacy `OverUnderTimer` formatting used by prompter/presenter/director overlays - return RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, true, true, false, true) case 'playlistDiff': default: return RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, false, true, true, true) @@ -73,4 +70,3 @@ function OverUnderChipInner({ valueMs, format = 'playlistDiff', className, style ) } - diff --git a/packages/webui/src/client/styles/countdown/director.scss b/packages/webui/src/client/styles/countdown/director.scss index 1714e502e61..8de4093591a 100644 --- a/packages/webui/src/client/styles/countdown/director.scss +++ b/packages/webui/src/client/styles/countdown/director.scss @@ -22,8 +22,6 @@ $hold-status-color: $liveline-timecode-color; .screen-timing-clock { z-index: 2000; font-size: 9vmin; - --overUnderChipPaddingY: 0em; - --overUnderChipPaddingX: 0.2em; } .director-screen__top { @@ -485,13 +483,12 @@ $hold-status-color: $liveline-timecode-color; font-weight: 600; } } -} - -.director-screen__bottom-bar { - position: fixed; - left: 0; - right: 0; - bottom: 0; - z-index: 20; - font-size: 4.95vh; + .director-screen__bottom-bar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 20; + font-size: 4.58vh; + } } diff --git a/packages/webui/src/client/styles/countdown/presenter.scss b/packages/webui/src/client/styles/countdown/presenter.scss index 3e397b454f2..82ee8a483c7 100644 --- a/packages/webui/src/client/styles/countdown/presenter.scss +++ b/packages/webui/src/client/styles/countdown/presenter.scss @@ -331,7 +331,7 @@ $hold-status-color: $liveline-timecode-color; } } } - .presenter-screen__rundown-status-bar { - font-size: 7.5vmin; + .rundown-status-bar { + font-size: 6.95vmin; } } diff --git a/packages/webui/src/client/styles/prompter.scss b/packages/webui/src/client/styles/prompter.scss index 562b2e839ee..c9a281e6383 100644 --- a/packages/webui/src/client/styles/prompter.scss +++ b/packages/webui/src/client/styles/prompter.scss @@ -303,8 +303,8 @@ body.prompter-scrollbar { z-index: 1100; } -.prompter-rundown-status-bar.presenter-screen__rundown-status-bar { - font-size: 6vh; +.prompter-rundown-status-bar.rundown-status-bar { + font-size: 6.95vmin; } .script-text-formatted { diff --git a/packages/webui/src/client/styles/rundownStatusBar.scss b/packages/webui/src/client/styles/rundownStatusBar.scss index 6a9cd3a549a..27a772772eb 100644 --- a/packages/webui/src/client/styles/rundownStatusBar.scss +++ b/packages/webui/src/client/styles/rundownStatusBar.scss @@ -5,7 +5,7 @@ */ @import 'colorScheme'; -.presenter-screen__rundown-status-bar { +.rundown-status-bar { display: grid; grid-template-columns: auto fit-content(1em); grid-template-rows: fit-content(1em); @@ -14,14 +14,14 @@ padding: 0 0 0 0.2em; background: rgba(0, 0, 0, 0.75); - .presenter-screen__rundown-status-bar__rundown-name { + .rundown-status-bar__rundown-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; line-height: 1.44em; } - .presenter-screen__rundown-status-bar__right { + .rundown-status-bar__right { display: flex; align-items: center; justify-content: flex-end; @@ -30,13 +30,7 @@ background: #333; } - .presenter-screen__rundown-status-bar__t-timer { - font-size: 1.2em; - display: flex; - align-items: center; - } - - .presenter-screen__rundown-status-bar__countdown { + .rundown-status-bar__countdown { white-space: nowrap; font-weight: 600; @@ -62,7 +56,7 @@ } } - &.presenter-screen__rundown-status-bar--no-title { + &.rundown-status-bar--no-title { background: transparent; } } diff --git a/packages/webui/src/client/styles/tTimerDisplay.scss b/packages/webui/src/client/styles/tTimerDisplay.scss index 5ee8428bd61..1e59a61f4eb 100644 --- a/packages/webui/src/client/styles/tTimerDisplay.scss +++ b/packages/webui/src/client/styles/tTimerDisplay.scss @@ -18,34 +18,61 @@ font-family: Roboto Flex; font-size: 1em; - letter-spacing: -0.01em; line-height: 1; - &__label { - display: flex; - align-items: center; - - padding-left: 0.1em; - padding-right: 0.1em; + height: 1.46em; - color: #fff; - font-weight: inherit; - text-transform: uppercase; - font-stretch: condensed; - } - - &__value { + &__countdown { display: flex; align-items: center; - padding: 0 0.1em 0 0.2em; + justify-content: flex-end; + gap: 0; - color: #fff; - font-variant-numeric: tabular-nums; - letter-spacing: -0.04em; + .countdown__label { + font-variation-settings: + 'wdth' 25, + 'wght' 400, + 'slnt' 0, + 'GRAD' 0, + 'opsz' 30, + 'XOPQ' 96, + 'XTRA' 468, + 'YOPQ' 79, + 'YTAS' 750, + 'YTFI' 738, + 'YTLC' 548, + 'YTDE' -203, + 'YTUC' 712; + letter-spacing: 0.0001em; + font-size: 1.3em; + padding-left: 0.125em; + color: #fff; + } - .t-timer-display__part { - &--dimmed { - color: #aaa; + .countdown__counter { + display: flex; + align-items: center; + font-variation-settings: + 'wdth' 25, + 'wght' 500, + 'slnt' 0, + 'GRAD' 0, + 'opsz' 20, + 'XOPQ' 96, + 'XTRA' 468, + 'YOPQ' 79, + 'YTAS' 750, + 'YTFI' 738, + 'YTLC' 548, + 'YTDE' -203, + 'YTUC' 712; + padding: 0 0.1em; + .over-under-chip { + position: relative; + left: 0.1em; + --overUnderChipOpsz: 20; + --overUnderChipYtlc: 514; + --overUnderChipMarginLeft: 0.15em; } } } diff --git a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx index f2bac7d889e..c7e82edbd15 100644 --- a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx +++ b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx @@ -3,7 +3,6 @@ import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment' import { PartUi } from '../SegmentTimeline/SegmentTimelineContainer.js' import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist/RundownPlaylist' import { Rundown } from '@sofie-automation/corelib/dist/dataModel/Rundown' -import type { CSSProperties } from 'react' import { useTiming } from '../RundownView/RundownTiming/withTiming.js' import { useSubscription, diff --git a/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx index 0952600453f..78022aa5027 100644 --- a/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx +++ b/packages/webui/src/client/ui/ClockView/RundownStatusBar.tsx @@ -19,17 +19,15 @@ export function RundownStatusBar({ return (
{showPlaylistName && ( -
{playlist ? playlist.name : 'UNKNOWN'}
+
{playlist ? playlist.name : 'UNKNOWN'}
)} -
-
- {!!activeTTimer && } -
+
+
{!!activeTTimer && }
) diff --git a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx index 95e667df3fa..817ea3eb739 100644 --- a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx +++ b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx @@ -2,8 +2,8 @@ import { RundownTTimer } from '@sofie-automation/corelib/dist/dataModel/RundownP import { RundownUtils } from '../../lib/rundown.js' import { calculateTTimerDiff, calculateTTimerOverUnder } from '../../lib/tTimerUtils.js' import { useTiming } from '../RundownView/RundownTiming/withTiming.js' -import classNames from 'classnames' import { OverUnderChip } from '../../lib/Components/OverUnderChip.js' +import { Countdown } from '../RundownView/RundownHeader/Countdown' interface TTimerDisplayProps { timer: RundownTTimer @@ -18,28 +18,19 @@ export function TTimerDisplay({ timer }: Readonly): JSX.Elem const diff = calculateTTimerDiff(timer, now) const overUnder = calculateTTimerOverUnder(timer, now) - const timerStr = RundownUtils.formatDiffToTimecode(Math.abs(diff), false, true, true, false, true) - const timerParts = timerStr.split(':') + const timeStr = RundownUtils.formatDiffToTimecode(Math.abs(diff), false, true, true, false, true) const timerSign = diff >= 0 ? '' : '-' return (
- {timer.label ? {timer.label} : null} - - {timerSign} - {timerParts.map((p, i) => ( - - {p} - {i < timerParts.length - 1 && :} - - ))} - - + } + > + {`${timerSign}${timeStr}`} +
) } From b5e52f0fa59d87688f061f49d3989d8053cb273b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 16 Apr 2026 17:05:24 +0200 Subject: [PATCH 13/13] chore: clean up --- .../client/lib/Components/OverUnderChip.scss | 15 ++-- .../client/lib/Components/OverUnderChip.tsx | 2 +- .../src/client/styles/rundownStatusBar.scss | 26 +------ .../DirectorScreen/DirectorScreenTop.tsx | 2 +- .../client/ui/ClockView/PresenterScreen.tsx | 2 +- .../src/client/ui/Prompter/OverUnderTimer.tsx | 73 ------------------- .../src/client/ui/Prompter/PrompterView.tsx | 5 +- .../RundownHeader/RundownHeaderTimers.tsx | 6 +- 8 files changed, 19 insertions(+), 112 deletions(-) delete mode 100644 packages/webui/src/client/ui/Prompter/OverUnderTimer.tsx diff --git a/packages/webui/src/client/lib/Components/OverUnderChip.scss b/packages/webui/src/client/lib/Components/OverUnderChip.scss index 913c0e2642f..33302864636 100644 --- a/packages/webui/src/client/lib/Components/OverUnderChip.scss +++ b/packages/webui/src/client/lib/Components/OverUnderChip.scss @@ -29,13 +29,6 @@ 'YTDE' var(--overUnderChipYtde, -203), 'YTUC' var(--overUnderChipYtuc, 712); - &.screen-timing-clock { - --overUnderChipWght: 700; - --overUnderChipOpsz: 20; - --overUnderChipYopq: 92; - --overUnderChipYtlc: 514; - } - &.over-under-chip--over { --overUnderChipWght: 700; background-color: $general-late-color; @@ -46,3 +39,11 @@ background-color: #ff0; // Should probably be changed to $general-fast-color; } } + +// Optional preset for when the chip is used as a large screen overlay. +.over-under-chip--overlay { + --overUnderChipWght: 700; + --overUnderChipOpsz: 20; + --overUnderChipYopq: 92; + --overUnderChipYtlc: 514; +} diff --git a/packages/webui/src/client/lib/Components/OverUnderChip.tsx b/packages/webui/src/client/lib/Components/OverUnderChip.tsx index 98b0446756c..5f57cdc34d6 100644 --- a/packages/webui/src/client/lib/Components/OverUnderChip.tsx +++ b/packages/webui/src/client/lib/Components/OverUnderChip.tsx @@ -6,7 +6,7 @@ import { useTiming } from '../../ui/RundownView/RundownTiming/withTiming.js' import { getPlaylistTimingDiff } from '../rundownTiming.js' import { DBRundownPlaylist } from '@sofie-automation/corelib/src/dataModel/RundownPlaylist/RundownPlaylist.js' -export type OverUnderChipFormat = 'playlistDiff' | 'timerPostfix' | 'screenOverlay' +export type OverUnderChipFormat = 'playlistDiff' | 'timerPostfix' type OverUnderChipBaseProps = { className?: string diff --git a/packages/webui/src/client/styles/rundownStatusBar.scss b/packages/webui/src/client/styles/rundownStatusBar.scss index 27a772772eb..d3445d4dd95 100644 --- a/packages/webui/src/client/styles/rundownStatusBar.scss +++ b/packages/webui/src/client/styles/rundownStatusBar.scss @@ -30,30 +30,10 @@ background: #333; } - .rundown-status-bar__countdown { + .rundown-status-bar__t-timer { + display: flex; + align-items: center; white-space: nowrap; - - font-weight: 600; - font-size: 1.2em; - align-self: center; - - &.heavy-light { - display: inline-block; - border-radius: 1em; - padding: 0 0.2em 0 0.1em; - - &.light { - // Match `.screen-timing-clock.heavy-light.light` - background-color: #ffe900; - color: #000; - } - - &.heavy { - // Match `.screen-timing-clock.heavy-light.heavy` - background-color: $general-fast-color; - color: #000; - } - } } &.rundown-status-bar--no-title { diff --git a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx index ea77f07f5a1..1aead9fd627 100644 --- a/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx +++ b/packages/webui/src/client/ui/ClockView/DirectorScreen/DirectorScreenTop.tsx @@ -78,7 +78,7 @@ export function DirectorScreenTop({ )}
- + ) } diff --git a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx index c7e82edbd15..6784c923967 100644 --- a/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx +++ b/packages/webui/src/client/ui/ClockView/PresenterScreen.tsx @@ -518,7 +518,7 @@ function PresenterScreenContentDefaultLayout({ return (
- +
-): JSX.Element | null { - if ('valueMs' in props) { - return - } else { - return - } -} - -function OverUnderTimerFromPlaylist( - props: Readonly -): JSX.Element | null { - const timingDurations = useTiming() - const valueMs = getPlaylistTimingDiff(props.rundownPlaylist, timingDurations) ?? 0 - return -} - -function OverUnderTimerInner(props: Readonly): JSX.Element | null { - const valueMs = props.valueMs - if (valueMs === undefined) return null - - const isOver = valueMs > 0 - - const badge = ( - - {isOver ? '+' : '\u2013'} - {RundownUtils.formatDiffToTimecode(Math.abs(valueMs), false, true, true, false, true)} - - ) - - return props.containerClassName ?
{badge}
: badge -} diff --git a/packages/webui/src/client/ui/Prompter/PrompterView.tsx b/packages/webui/src/client/ui/Prompter/PrompterView.tsx index d010aa2c045..1e6feba518f 100644 --- a/packages/webui/src/client/ui/Prompter/PrompterView.tsx +++ b/packages/webui/src/client/ui/Prompter/PrompterView.tsx @@ -605,7 +605,10 @@ export class PrompterViewContent extends React.Component {this.configOptions.showOverUnder && ( - + )} ) { mode.type === 'countdown' && timer.state !== null && diff >= 0, })} ms={mode.type === 'timeOfDay' ? undefined : diff} - postfix={ - overUnder ? ( - - ) : undefined - } + postfix={overUnder ? : undefined} > {timeStr}