This commit is contained in:
legop3
2026-07-12 15:36:48 -04:00
parent 60eacf982c
commit e254eea9e4
16 changed files with 129 additions and 44 deletions
@@ -22,8 +22,6 @@ export default function PtzLiveVideo({
className = 'relative h-full w-full bg-black',
videoClassName = 'h-full w-full object-contain',
statusClassName = 'pointer-events-none absolute left-1 top-1 z-20 font-medium text-slate-100 text-[0.65rem]',
label = null,
labelClassName = 'pointer-events-none absolute left-0 top-0 bg-black/70 px-1 py-0.5 text-xs font-semibold text-white',
fallback = null,
}) {
const videoRef = useRef(null);
@@ -199,7 +197,6 @@ export default function PtzLiveVideo({
{source?.error || 'Waiting for PTZ video...'}
</div>
)}
{label ? <div className={labelClassName}>{label}</div> : null}
{/*
PTZ video uses the same low-profile diagnostic shape as the rover
players: no in-frame camera title, just a compact top-corner status.
+11 -6
View File
@@ -1,7 +1,7 @@
// Vip PTZ Camera Card
// Purpose: Provides the verified-user entry point and fullscreen controller for the single Reolink PTZ camera.
// Scope: Owns PTZ UI state only; server-side PTZ ownership, rover handoff, and command authorization remain authoritative.
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import ChatPanel from '../ChatPanel/index.jsx';
import CardFrame from '../CardFrame/index.jsx';
@@ -59,11 +59,16 @@ function PtzSnapshotPreview({ feed, label = 'PTZ Camera' }) {
) : (
<div className="flex h-full w-full items-center justify-center text-xs text-slate-400">Waiting for snapshot...</div>
)}
<div className="pointer-events-none absolute left-0 top-0 bg-black/70 px-1 py-0.5 text-xs font-semibold text-white">
{label}
</div>
<div className="pointer-events-none absolute bottom-0 left-0 m-1 rounded bg-black/70 px-1 py-0.5 text-[0.7rem] text-slate-100">
{feed?.error ? `Error: ${feed.error}` : feed?.status || 'connecting'}
{/*
Even on this legacy VIP surface, keep the video pane itself identical
to rover media panes: no camera title inside the frame, only the small
top-left stream status. Any PTZ name/context belongs to the card chrome
around the media, not the media player.
*/}
<div className="pointer-events-none absolute left-1 top-1 z-20 font-medium text-slate-100 text-[0.65rem]">
<div className="flex flex-col gap-0.5 leading-none">
<span>Status: {feed?.error ? `Error: ${feed.error}` : feed?.status || 'connecting'}</span>
</div>
</div>
</div>
);
@@ -65,11 +65,16 @@ function PtzSnapshotFallback({ label, source }) {
{snapshot?.error || source?.error || 'Waiting for PTZ snapshot...'}
</div>
)}
<div className="pointer-events-none absolute left-0 top-0 bg-black/70 px-1 py-0.5 text-xs font-semibold text-white">
{label}
</div>
<div className="pointer-events-none absolute bottom-0 left-0 m-1 rounded bg-black/70 px-1 py-0.5 text-[0.7rem] text-slate-100">
{snapshot?.status || 'snapshot'}
{/*
Keep the PTZ snapshot fallback visually aligned with RoverMediaPlayer:
the video surface owns only playback health, while the card around it
owns identity/context. That prevents a second in-frame camera title and
keeps fallback mode from looking different than live WHEP mode.
*/}
<div className="pointer-events-none absolute left-1 top-1 z-20 font-medium text-slate-100 text-[0.65rem]">
<div className="flex flex-col gap-0.5 leading-none">
<span>Status: {snapshot?.status || 'snapshot'}</span>
</div>
</div>
</div>
);
@@ -80,9 +85,7 @@ function PtzLiveOrSnapshot({ label }) {
<PtzLiveVideo
enabled
startMuted
label={label}
className="relative aspect-video w-full overflow-hidden rounded bg-black"
statusClassName="pointer-events-none absolute bottom-0 left-0 m-1 rounded bg-black/70 px-1 py-0.5 text-[0.7rem] text-slate-100"
fallback={({ source }) => <PtzSnapshotFallback label={label} source={source} />}
/>
);