better /display ptz operator popup

This commit is contained in:
legop3
2026-07-16 17:42:55 -04:00
parent efae430d65
commit e28ccc5e66
8 changed files with 36 additions and 33 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -78,8 +78,8 @@
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
<title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-Dqo9kxw3.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-ZpgWUPKf.css">
<script type="module" crossorigin src="/assets/index-C8ryXPtn.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-PKUQHBrm.css">
</head>
<body>
<div id="root"></div>
@@ -36,8 +36,14 @@ export default function ServerDisplayContent() {
return (
<div className="display-page flex h-screen w-screen flex-col overflow-hidden bg-black text-slate-100">
<div className="h-[8vh] min-h-[4rem] shrink-0">
<div className="flex h-[8vh] min-h-[4rem] shrink-0 overflow-hidden">
<OnlinePeopleStrip users={session?.users || []} />
{/* The PTZ operator belongs in the same information band as the people
strip because it is another "who is active right now" signal. Making
it a flex sibling lets the badge reserve real layout space when it
appears, which pushes the scrolling strip left instead of covering
the rover or chat areas. */}
<DisplayPtzOperatorBadge />
</div>
<div className="min-h-0 flex-[0.72]">
<DisplayRoverGrid roster={session?.roster || []} session={session} />
@@ -45,11 +51,6 @@ export default function ServerDisplayContent() {
<div className="min-h-0 flex-[1.28]">
<DisplayChatFeed />
</div>
{/* Keep the PTZ operator visible on the room board without changing the
existing rover/chat layout. The badge is self-hiding when nobody owns
the camera, so the display remains exactly as sparse as before between
PTZ turns. */}
<DisplayPtzOperatorBadge />
<DisplayNoticeOverlay />
<RewardRunOverlay />
{/* Display is spectator-like: every Discord-hosted replay should take over
@@ -8,32 +8,31 @@ import { useSessionSelector } from '../../../context/SessionContext.jsx';
export default function DisplayPtzOperatorBadge() {
const ptz = useSessionSelector((state) => state.session?.ptzCamera || null);
const operatorLabel = String(ptz?.operatorLabel || '').trim();
if (!ptz?.enabled || !operatorLabel) {
/*
The display should stay clean when nobody has the camera. Returning null
instead of showing "none" makes the badge behave like a popup: it appears
only for an active PTZ operator and disappears as soon as the turn ends.
*/
return null;
}
const visible = Boolean(ptz?.enabled && operatorLabel);
return (
<aside
className="pointer-events-none fixed bottom-[2vh] right-[2vw] z-[90] max-w-[42vw] border-4 border-sky-200 bg-sky-700 px-[1.4vw] py-[1vh] text-center"
aria-label={`PTZ operator ${operatorLabel}`}
className={`pointer-events-none h-full shrink-0 overflow-hidden border-b border-l border-sky-200 bg-sky-700 transition-[width,opacity] duration-300 ease-out ${
visible ? 'w-[min(34vw,34rem)] opacity-100' : 'w-0 opacity-0'
}`}
aria-hidden={!visible}
aria-label={visible ? `PTZ operator ${operatorLabel}` : undefined}
>
{/*
The label is deliberately short because /display is a room board, not a
control panel. The large name is the useful information from across the
room, while the smaller prefix prevents the blue box from being mistaken
for a rover driver or chat message.
This is a flex-row segment instead of a fixed overlay so the online
people marquee loses width when PTZ is active. That makes the badge feel
like it enters from the right edge of the top bar while avoiding the
previous problem where it covered content in the bottom-right corner.
*/}
<div className="text-7xl font-black tracking-normal text-sky-100">
PTZ camera
</div>
<div className="truncate text-9xl font-black leading-none text-white">
{operatorLabel}
<div className="flex h-full min-w-0 items-center justify-center gap-[1vw] px-[1.2vw] text-[clamp(2.1rem,5.1vh,5.6rem)] font-black leading-none tracking-normal text-white">
{/*
The user explicitly requested uppercase "PTZ" here because the room
display needs a terse, instantly recognizable camera marker. The name
remains the larger variable part, and truncation prevents a long
nickname from resizing the bar or overlapping the scrolling strip.
*/}
<span className="shrink-0 text-sky-100">PTZ</span>
<span className="min-w-0 truncate">{operatorLabel}</span>
</div>
</aside>
);
@@ -68,7 +68,10 @@ export default function OnlinePeopleStrip({ users = [] }) {
));
return (
<div ref={viewportRef} className="relative h-full min-w-0 overflow-hidden border-b border-slate-800/80 bg-black">
<div
ref={viewportRef}
className="relative h-full min-w-0 flex-1 overflow-hidden border-b border-slate-800/80 bg-black"
>
<div
ref={trackRef}
className={classNames(