From 0d5228f1e5a60d90a5a25063743766dafd70bef6 Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Wed, 26 Aug 2026 13:04:23 +0000 Subject: [PATCH] fix(ui): make the message preview match the stage it stands in for Three things measured wrong once the preview showed a real render: - the pip timer kept leading zeros where the timer view drops them by default, so the preview read 00:59:07 against the stage's 59:07 - and its own secondary line already dropped them - the extracted /messagecontrol route has no flex parent for the panel to grow into, so the stage collapsed to its minimum instead of filling the window - a tall panel caps the stage at its own width, and the slack it leaves now sits either side of the stage rather than all below it Covers the screen state controls in the e2e spec: the secondary source select puts the typed text on the stage, blackout reaches the timer view, and clear screen resets the state while keeping the text. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011CHxSQ6nWHuyar8feieRra --- .../message/MessageControlExport.module.scss | 5 ++++ .../control/message/MessageControlExport.tsx | 6 ++++- .../control/message/TimerPreview.module.scss | 2 ++ .../src/views/editor/pip-timer/PipTimer.tsx | 3 ++- .../features/201-message-control.spec.ts | 26 +++++++++++++++++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/client/src/features/control/message/MessageControlExport.module.scss b/apps/client/src/features/control/message/MessageControlExport.module.scss index 9937fcdd3..1fa4dde5a 100644 --- a/apps/client/src/features/control/message/MessageControlExport.module.scss +++ b/apps/client/src/features/control/message/MessageControlExport.module.scss @@ -6,6 +6,11 @@ overflow: hidden; } +/* the extracted route has no flex parent to grow into */ +.extractedPanel { + height: 100%; +} + .contentLayout { flex: 1; min-height: 0; diff --git a/apps/client/src/features/control/message/MessageControlExport.tsx b/apps/client/src/features/control/message/MessageControlExport.tsx index 2a54d2bc8..c8337c513 100644 --- a/apps/client/src/features/control/message/MessageControlExport.tsx +++ b/apps/client/src/features/control/message/MessageControlExport.tsx @@ -5,6 +5,7 @@ import ErrorBoundary from '../../../common/components/error-boundary/ErrorBounda import ViewNavigationMenu from '../../../common/components/navigation-menu/ViewNavigationMenu'; import ProtectRoute from '../../../common/components/protect-route/ProtectRoute'; import { handleLinks } from '../../../common/utils/linkUtils'; +import { cx } from '../../../common/utils/styleUtils'; import { getIsNavigationLocked } from '../../../externals'; import MessageControl from './MessageControl'; @@ -16,7 +17,10 @@ function MessageControlExport() { return ( - + {!isExtracted && handleLinks('messagecontrol', event)} />} {isExtracted && } diff --git a/apps/client/src/features/control/message/TimerPreview.module.scss b/apps/client/src/features/control/message/TimerPreview.module.scss index 8fcd756bd..aa4c26658 100644 --- a/apps/client/src/features/control/message/TimerPreview.module.scss +++ b/apps/client/src/features/control/message/TimerPreview.module.scss @@ -3,6 +3,8 @@ min-height: 0; display: flex; flex-direction: column; + /* the stage caps at the panel width, so centre whatever slack a tall panel leaves */ + justify-content: center; gap: $element-inner-spacing; } diff --git a/apps/client/src/views/editor/pip-timer/PipTimer.tsx b/apps/client/src/views/editor/pip-timer/PipTimer.tsx index 818bbfff7..a8a977947 100644 --- a/apps/client/src/views/editor/pip-timer/PipTimer.tsx +++ b/apps/client/src/views/editor/pip-timer/PipTimer.tsx @@ -41,9 +41,10 @@ export function PipTimer({ viewSettings }: PipTimerProps) { // gather timer data const totalTime = getTotalTime(time.duration, time.addedTime); const stageTimer = getTimerByType(false, timerTypeNow, clock, time, timerTypeNow); + // match the defaults of the timer view, which is what the preview is standing in for const display = getFormattedTimer(stageTimer, timerTypeNow, 'min', { removeSeconds: false, - removeLeadingZero: false, + removeLeadingZero: true, }); const currentAux = (() => { diff --git a/e2e/tests/features/201-message-control.spec.ts b/e2e/tests/features/201-message-control.spec.ts index 2bb033c55..2e8fdb520 100644 --- a/e2e/tests/features/201-message-control.spec.ts +++ b/e2e/tests/features/201-message-control.spec.ts @@ -19,3 +19,29 @@ test('message control sends messages to screens', async ({ context }) => { await expect(featurePage.getByText('TIME NOW')).toBeVisible(); }); + +test('message control drives the stage screen state', async ({ context }) => { + const editorPage = await context.newPage(); + const featurePage = await context.newPage(); + + await editorPage.goto('/messagecontrol'); + await featurePage.goto('/timer'); + await featurePage.waitForLoadState('load', { timeout: 5000 }); + + // the secondary line defaults to an aux timer, the select is what puts our text on screen + await editorPage.getByPlaceholder('Message shown as secondary text in stage timer').fill('testing secondary'); + await editorPage.getByRole('combobox').click(); + await editorPage.getByRole('option', { name: 'Message' }).click(); + await expect(featurePage.getByText('testing secondary')).toBeVisible(); + + await editorPage.getByTestId('toggle timer blackout').click(); + await expect(featurePage.locator('.blackout')).toHaveClass(/blackout--active/); + + // clearing returns the screen to normal, but keeps what the operator typed + await editorPage.getByTestId('clear screen').click(); + await expect(featurePage.locator('.blackout')).not.toHaveClass(/blackout--active/); + await expect(featurePage.getByText('testing secondary')).toHaveCount(0); + await expect(editorPage.getByPlaceholder('Message shown as secondary text in stage timer')).toHaveValue( + 'testing secondary', + ); +});