diff --git a/packages/webui/src/client/lib/Components/CounterComponents.tsx b/packages/webui/src/client/lib/Components/CounterComponents.tsx index 504969259d..fec18f2f66 100644 --- a/packages/webui/src/client/lib/Components/CounterComponents.tsx +++ b/packages/webui/src/client/lib/Components/CounterComponents.tsx @@ -9,7 +9,7 @@ export const OverUnderClockComponent = (props: OverUnderProps): JSX.Element => { return (
- {RundownUtils.formatDiffToTimecode(props.value, true, false, true, true, true, undefined, true, true)} + {RundownUtils.formatDiffToTimecodeOverUnder(props.value, true)}
) @@ -26,7 +26,7 @@ export const PlannedEndComponent = (props: OverUnderProps): JSX.Element => { export const TimeToPlannedEndComponent = (props: OverUnderProps): JSX.Element => { return ( - {RundownUtils.formatDiffToTimecode(props.value, true, false, true, true, true, undefined, true, true)} + {RundownUtils.formatDiffToTimecodeOverUnder(props.value, true)} ) } @@ -34,7 +34,7 @@ export const TimeToPlannedEndComponent = (props: OverUnderProps): JSX.Element => export const TimeSincePlannedEndComponent = (props: OverUnderProps): JSX.Element => { return ( - {RundownUtils.formatDiffToTimecode(props.value, true, false, true, true, true, undefined, true, true)} + {RundownUtils.formatDiffToTimecodeOverUnder(props.value, true)} ) } diff --git a/packages/webui/src/client/lib/rundown.ts b/packages/webui/src/client/lib/rundown.ts index 0e99d5d45a..700c506131 100644 --- a/packages/webui/src/client/lib/rundown.ts +++ b/packages/webui/src/client/lib/rundown.ts @@ -265,6 +265,36 @@ export namespace RundownUtils { ) } + /** Format a duration (ms) to a timecode string, showing hours only when needed. e.g. "23:45" or "1:23:45" */ + export function formatDiffToTimecodeHours(milliseconds: number): string { + return formatDiffToTimecode(milliseconds, false, true, true, false, true) + } + + /** + * Format a live countdown timer (ms). Shows "+" for positive, blank prefix when negative (rounds toward zero), + * using hard-floor rounding so the display doesn't jump ahead of the actual value. + */ + export function formatDiffToTimecodeCountdown(milliseconds: number): string { + return formatDiffToTimecode(milliseconds, true, false, true, false, true, '', false, true) + } + + /** + * Format a budget/remaining duration (ms) with a "+" prefix for positive values (time remaining) + * and an en-dash for negative values (over budget). + */ + export function formatDiffToTimecodeWithSign(milliseconds: number): string { + return formatDiffToTimecode(milliseconds, false, false, true, false, true, '+') + } + + /** + * Format an over/under diff (ms) with "+" for positive, en-dash for negative, smart-floor rounding, + * and smart-hours. Used for start-time diffs and over/under clocks. + * Pass `floorTime: true` to use hard-floor rounding (display won't jump ahead of the actual value). + */ + export function formatDiffToTimecodeOverUnder(milliseconds: number, floorTime?: boolean): string { + return formatDiffToTimecode(milliseconds, true, false, true, true, true, undefined, floorTime, floorTime) + } + export function isInsideViewport( scrollLeft: number, scrollWidth: number, diff --git a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx index 3ff4cc99ba..17bc1d0d07 100644 --- a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx +++ b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx @@ -18,7 +18,7 @@ export function TTimerDisplay({ timer }: Readonly): JSX.Elem const diff = calculateTTimerDiff(timer, now) const overUnder = calculateTTimerOverUnder(timer, now) - const timeStr = RundownUtils.formatDiffToTimecode(Math.abs(diff), false, true, true, false, true) + const timeStr = RundownUtils.formatDiffToTimecodeHours(Math.abs(diff)) const timerSign = diff >= 0 ? '' : '-' return ( diff --git a/packages/webui/src/client/ui/ClockView/Timediff.tsx b/packages/webui/src/client/ui/ClockView/Timediff.tsx index 2f6b93b265..928754add6 100644 --- a/packages/webui/src/client/ui/ClockView/Timediff.tsx +++ b/packages/webui/src/client/ui/ClockView/Timediff.tsx @@ -4,7 +4,7 @@ import { RundownUtils } from '../../lib/rundown.js' export function Timediff({ time: rawTime }: { time: number }): JSX.Element { const time = -rawTime const isNegative = Math.floor(time / 1000) > 0 - const timeString = RundownUtils.formatDiffToTimecode(time, true, false, true, false, true, '', false, true) + const timeString = RundownUtils.formatDiffToTimecodeCountdown(time) return ( {t('({{timecode}})', { - timecode: RundownUtils.formatDiffToTimecode(expectedDuration, false, true, true, false, true), + timecode: RundownUtils.formatDiffToTimecodeHours(expectedDuration), })}   ) : ( - RundownUtils.formatDiffToTimecode(expectedDuration, false, true, true, false, true) + RundownUtils.formatDiffToTimecodeHours(expectedDuration) ) ) : isOnlyRundownInPlaylist && isLoopDefined(playlist) ? ( {t('({{timecode}})', { - timecode: RundownUtils.formatDiffToTimecode(playlistExpectedDuration, false, true, true, false, true), + timecode: RundownUtils.formatDiffToTimecodeHours(playlistExpectedDuration), })}   ) : ( - RundownUtils.formatDiffToTimecode(playlistExpectedDuration, false, true, true, false, true) + RundownUtils.formatDiffToTimecodeHours(playlistExpectedDuration) )) return ( diff --git a/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx b/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx index 6b0551b8ec..2689a35790 100644 --- a/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx @@ -86,7 +86,7 @@ export function RundownDividerHeader({ rundown, playlist }: IProps): JSX.Element {expectedDuration ? (
{t('Planned Duration')}  - {RundownUtils.formatDiffToTimecode(expectedDuration, false, true, true, false, true)} + {RundownUtils.formatDiffToTimecodeHours(expectedDuration)}
) : null} {expectedEnd ? ( diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx index a1d97e76fd..08146caaeb 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx @@ -147,7 +147,7 @@ export const CurrentPartOrSegmentRemaining: React.FC = (pro className={ClassNames(props.className, Math.floor(displayTimecode / 1000) > 0 ? props.heavyClassName : undefined)} role="timer" > - {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)} + {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)}
) } @@ -166,7 +166,7 @@ export const RundownHeaderPartRemaining: React.FC = (props) label={props.label} className={ClassNames(props.className, Math.floor(displayTimecode / 1000) > 0 ? props.heavyClassName : undefined)} > - {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)} + {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)} ) } @@ -187,7 +187,7 @@ export const RundownHeaderSegmentBudget: React.FC<{ {label} 0 ? 'overtime' : undefined)}> - {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)} + {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)} ) diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx index 176eb08a25..65dd18919a 100644 --- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx @@ -40,7 +40,7 @@ function SingleTimer({ timer }: Readonly) { const diff = calculateTTimerDiff(timer, now) const overUnder = calculateTTimerOverUnder(timer, now) - const timeStr = RundownUtils.formatDiffToTimecode(Math.abs(diff), false, true, true, false, true) + const timeStr = RundownUtils.formatDiffToTimecodeHours(Math.abs(diff)) const isCountingDown = mode.type === 'countdown' && diff < 0 && isRunning return ( diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx index f69443b985..f69a2be1b5 100644 --- a/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx @@ -19,7 +19,7 @@ export function CurrentPartElapsed({ currentPartId, className }: IPartElapsedPro return ( - {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)} + {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)} ) } diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx index b3200d436b..1d7793465f 100644 --- a/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx @@ -48,15 +48,15 @@ export function PartDisplayDuration(props: IPartDurationProps): JSX.Element | nu {props.label} {props.fixed ? ( - {RundownUtils.formatDiffToTimecode(budget, false, false, true, false, true, '+')} + {RundownUtils.formatDiffToTimecodeWithSign(budget)} ) : props.countUp ? ( - {RundownUtils.formatDiffToTimecode(playedOut, false, false, true, false, true, '+')} + {RundownUtils.formatDiffToTimecodeWithSign(playedOut)} ) : ( - {RundownUtils.formatDiffToTimecode(duration, false, false, true, false, true, '+')} + {RundownUtils.formatDiffToTimecodeWithSign(duration)} )} diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx index 5cc865342d..12158d57fa 100644 --- a/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx @@ -55,7 +55,7 @@ export function PlaylistEndTiming({ role="timer" > {!hideDiffLabel && {t('Diff')}} - {RundownUtils.formatDiffToTimecode(overUnderClock, true, false, true, true, true, undefined, true, true)} + {RundownUtils.formatDiffToTimecodeOverUnder(overUnderClock, true)} ) : null ) : null} diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx index d41e581732..af16785576 100644 --- a/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx +++ b/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx @@ -76,17 +76,8 @@ export function RundownName({ )} {!hideDiff && rundownPlaylist.startedPlayback && rundownPlaylist.activationId && !rundownPlaylist.rehearsal - ? expectedStart && - RundownUtils.formatDiffToTimecode( - rundownPlaylist.startedPlayback - expectedStart, - true, - false, - true, - true, - true - ) - : expectedStart && - RundownUtils.formatDiffToTimecode(getCurrentTime() - expectedStart, true, false, true, true, true)} + ? expectedStart && RundownUtils.formatDiffToTimecodeOverUnder(rundownPlaylist.startedPlayback - expectedStart) + : expectedStart && RundownUtils.formatDiffToTimecodeOverUnder(getCurrentTime() - expectedStart)} ) } diff --git a/packages/webui/src/client/ui/SegmentList/LinePart.tsx b/packages/webui/src/client/ui/SegmentList/LinePart.tsx index 6a30af3d0a..2fc8994942 100644 --- a/packages/webui/src/client/ui/SegmentList/LinePart.tsx +++ b/packages/webui/src/client/ui/SegmentList/LinePart.tsx @@ -112,17 +112,7 @@ export function LinePart({ return ( <> {part.instance.part.expectedDuration !== undefined && part.instance.part.expectedDuration > 0 && ( - - {RundownUtils.formatDiffToTimecode( - part.instance.part.expectedDuration, - false, - false, - true, - false, - true, - '+' - )} - + {RundownUtils.formatDiffToTimecodeWithSign(part.instance.part.expectedDuration)} )} {(part.instance.part.expectedDuration === 0 || part.instance.part.expectedDuration === undefined) && ( ––:–– diff --git a/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx b/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx index d01d4752a7..aba1a82365 100644 --- a/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx +++ b/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx @@ -97,9 +97,7 @@ function VTThumbnailRendererWithTiming({ > - {contentLeft > 0 ? ( - {RundownUtils.formatDiffToTimecode(contentLeft, false, false, true, false, true, '+')} - ) : null} + {contentLeft > 0 ? {RundownUtils.formatDiffToTimecodeWithSign(contentLeft)} : null} ) : null } diff --git a/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx b/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx index ec1e4335e9..e5b3e8d55e 100644 --- a/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx +++ b/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx @@ -83,7 +83,7 @@ export function PieceCountdownPanel({ overtime: Math.floor(displayTimecode / 1000) > 0, })} > - {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)} + {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)} )