From 8253f1c119e71949a037686f9de00d1b16d0f387 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 19:46:30 +0000 Subject: [PATCH] refactor(teleprompter): build the control overlay from the shared pieces The overlay is the on screen transport, for whoever is not at a keyboard, which on a tablet prompter is everyone. It was written from scratch and three things it needed already existed. Its buttons are now IconButton in the subtle-white variant, the same as the floating navigation uses over a dark viewer, which removes a block of bespoke button styling that had reinvented sizing, centring, radius and hover. The help overlay's close button likewise uses the shared Button. Visibility now comes from useFadeOutOnInactivity, the hook the floating navigation uses, so the overlay fades once the operator stops moving. It used to fade on whether the script was rolling, which meant a paused prompter kept a control bar in the talent's eyeline indefinitely. Following the shared pattern turned up a bug in the old one. The floating navigation stops taking clicks once faded, and this did not, so there was an invisible transport across the foot of the script where a stray tap would pause the read. It now matches. The operator view's FollowButton is deliberately not reused. It is a labelled pill fixed to the bottom centre, which is exactly where this group sits, so the two would land on top of each other. Same icon and same job, folded into the transport instead. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf --- .../src/views/teleprompter/Teleprompter.scss | 39 ++--------- .../control-overlay/ControlOverlay.tsx | 66 +++++++++++-------- .../teleprompter/help-overlay/HelpOverlay.tsx | 6 +- 3 files changed, 51 insertions(+), 60 deletions(-) diff --git a/apps/client/src/views/teleprompter/Teleprompter.scss b/apps/client/src/views/teleprompter/Teleprompter.scss index eb44f09f2..39c3f8395 100644 --- a/apps/client/src/views/teleprompter/Teleprompter.scss +++ b/apps/client/src/views/teleprompter/Teleprompter.scss @@ -178,35 +178,15 @@ opacity: 1; transition: opacity $viewer-transition-time; - /* stay out of the eyeline while the script is rolling, until pointed at */ + /** + * useFadeOutOnInactivity drives this, as it does the floating navigation, and + * the same rule applies: once faded the controls stop taking clicks. Leaving + * them live would put an invisible transport across the foot of the script, + * where a stray tap pauses the read. + */ &--idle { opacity: 0; - } - - &:hover, - &:focus-within { - opacity: 1; - } -} - -.teleprompter__control { - display: grid; - place-items: center; - width: 2em; - height: 2em; - font-size: clamp(14px, 1.4vw, 22px); - - color: $viewer-color; - background: rgba(white, 8%); - border-radius: $element-border-radius; - cursor: pointer; - - &:hover { - background: rgba(white, 16%); - } - - &--attention { - color: $accent-color; + pointer-events: none; } } @@ -270,9 +250,4 @@ .teleprompter__help-close { margin-top: $view-element-gap; - padding: 0.4em 1em; - color: $viewer-color; - background: rgba(white, 8%); - border-radius: $element-border-radius; - cursor: pointer; } diff --git a/apps/client/src/views/teleprompter/control-overlay/ControlOverlay.tsx b/apps/client/src/views/teleprompter/control-overlay/ControlOverlay.tsx index 3e29a4aa0..404abb9fd 100644 --- a/apps/client/src/views/teleprompter/control-overlay/ControlOverlay.tsx +++ b/apps/client/src/views/teleprompter/control-overlay/ControlOverlay.tsx @@ -1,5 +1,7 @@ -import { IoArrowUp, IoHelpCircleOutline, IoLocate, IoPause, IoPlay, IoRemove, IoAdd } from 'react-icons/io5'; +import { IoAdd, IoArrowUp, IoHelpCircleOutline, IoLocate, IoPause, IoPlay, IoRemove } from 'react-icons/io5'; +import IconButton from '../../../common/components/buttons/IconButton'; +import { useFadeOutOnInactivity } from '../../../common/hooks/useFadeOutOnInactivity'; import { cx } from '../../../common/utils/styleUtils'; import { SPEED_STEP } from '../teleprompter.scroll'; import type { TeleprompterController } from '../teleprompter.types'; @@ -14,8 +16,12 @@ interface ControlOverlayProps { } /** - * On screen transport, for when nobody is at a keyboard. - * It fades out while the script is rolling so it does not sit in the talent's eyeline. + * Transport for whoever is not at a keyboard, which on a tablet prompter is + * everyone. The keys remain the primary interface, and the way pedals reach it. + * + * Visibility follows the same rule as the rest of Ontime's floating chrome: it + * fades once the operator stops moving, so it leaves the talent's eyeline + * without needing to know whether the script happens to be rolling. */ export default function ControlOverlay({ isRunning, @@ -25,65 +31,73 @@ export default function ControlOverlay({ controller, onToggleHelp, }: ControlOverlayProps) { + const isActive = useFadeOutOnInactivity(true); + return ( -
- + - +
{speed} lpm
- + - + + {/* + The operator view solves this with its own FollowButton, a labelled pill + fixed to the bottom centre. That is exactly where this group sits, so the + two would land on top of each other. Same icon and same job, folded into + the transport rather than floating separately. + */} {followLocked && ( - + )} - +
); } diff --git a/apps/client/src/views/teleprompter/help-overlay/HelpOverlay.tsx b/apps/client/src/views/teleprompter/help-overlay/HelpOverlay.tsx index 844f9c0d3..aa2b786d0 100644 --- a/apps/client/src/views/teleprompter/help-overlay/HelpOverlay.tsx +++ b/apps/client/src/views/teleprompter/help-overlay/HelpOverlay.tsx @@ -1,3 +1,5 @@ +import Button from '../../../common/components/buttons/Button'; + interface HelpOverlayProps { onClose: () => void; } @@ -37,9 +39,9 @@ export default function HelpOverlay({ onClose }: HelpOverlayProps) {
Foot pedals and hand controllers which emit these keys work without any setup.
- + );