mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
bwah
This commit is contained in:
@@ -214,6 +214,7 @@ function TtsControls({
|
||||
function ChatComposer({
|
||||
allowSpectatorInput = false,
|
||||
hideSpectatorNotice = false,
|
||||
inputTarget = 'panel',
|
||||
}) {
|
||||
const {
|
||||
sendMessage,
|
||||
@@ -310,7 +311,7 @@ function ChatComposer({
|
||||
setTypingActive(false);
|
||||
}
|
||||
}}
|
||||
ref={(el) => registerInputRef(el, { target: 'panel' })}
|
||||
ref={(el) => registerInputRef(el, { target: inputTarget })}
|
||||
placeholder={canChat ? 'Type a message…' : hideSpectatorNotice ? '' : 'Spectators cannot chat'}
|
||||
disabled={!canChat}
|
||||
/>
|
||||
@@ -346,6 +347,7 @@ export default function ChatPanel({
|
||||
allowSpectatorInput = false,
|
||||
title = 'Chat and speech',
|
||||
minimal = false,
|
||||
inputTarget = 'panel',
|
||||
}) {
|
||||
const effectiveHideInput = minimal || hideInput;
|
||||
const effectiveTitle = minimal ? '' : title;
|
||||
@@ -362,6 +364,7 @@ export default function ChatPanel({
|
||||
<MemoizedChatComposer
|
||||
allowSpectatorInput={allowSpectatorInput}
|
||||
hideSpectatorNotice={hideSpectatorNotice}
|
||||
inputTarget={inputTarget}
|
||||
/>
|
||||
)}
|
||||
</CardFrame>
|
||||
|
||||
@@ -347,13 +347,15 @@ function PtzMediaPane({ ptz, open }) {
|
||||
const turnModel = useMemo(() => buildPtzTurnModel(ptz, selfId), [ptz, selfId]);
|
||||
|
||||
return (
|
||||
<div className="relative h-full min-h-0 w-full overflow-hidden bg-black">
|
||||
{isOperator ? (
|
||||
<PtzLiveVideo enabled={open} startMuted={false} label={ptz?.name || 'PTZ Camera'} />
|
||||
) : (
|
||||
<PtzSnapshotPreview feed={snapshot} label={ptz?.name || 'PTZ Camera'} />
|
||||
)}
|
||||
<TurnsOverlay turnModel={turnModel} />
|
||||
<div className="flex h-full min-h-0 w-full items-center justify-center overflow-hidden bg-black">
|
||||
<div className="relative aspect-video max-h-full w-full max-w-full overflow-hidden bg-black">
|
||||
{isOperator ? (
|
||||
<PtzLiveVideo enabled={open} startMuted={false} label={ptz?.name || 'PTZ Camera'} />
|
||||
) : (
|
||||
<PtzSnapshotPreview feed={snapshot} label={ptz?.name || 'PTZ Camera'} />
|
||||
)}
|
||||
<TurnsOverlay turnModel={turnModel} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -366,6 +368,7 @@ function PtzDesktopFullscreen({ ptz, releasePending }) {
|
||||
<PtzMediaPane ptz={ptz} open />
|
||||
</main>
|
||||
<aside className="flex min-h-0 flex-col gap-0.5 overflow-y-auto bg-neutral-950 text-sm">
|
||||
<PtzQueueSummary ptz={ptz} />
|
||||
{ptz?.isOperator ? (
|
||||
<PtzLightingControls ptz={ptz} />
|
||||
) : (
|
||||
@@ -376,14 +379,13 @@ function PtzDesktopFullscreen({ ptz, releasePending }) {
|
||||
<PtzControlReference />
|
||||
<PtzStatePanel ptz={ptz} />
|
||||
<ReplaySourcesPanel panelId="ptz-controller-replay" />
|
||||
<CardFrame title="Position presets" bodyClassName="p-1 text-xs text-slate-500">
|
||||
Presets will live here.
|
||||
</CardFrame>
|
||||
</aside>
|
||||
</div>
|
||||
<div className="grid min-h-0 grid-cols-[minmax(0,1.6fr)_minmax(16rem,0.7fr)] gap-0.5 overflow-hidden">
|
||||
<ChatPanel fillHeight title="Chat" />
|
||||
<PtzQueueSummary ptz={ptz} />
|
||||
<ChatPanel fillHeight title="Chat" allowSpectatorInput inputTarget="overlay" />
|
||||
<CardFrame title="Position presets" fillHeight bodyClassName="p-1 text-xs text-slate-500">
|
||||
Presets will live here.
|
||||
</CardFrame>
|
||||
</div>
|
||||
{releasePending ? (
|
||||
<div className="pointer-events-none absolute bottom-1 right-1 rounded bg-black/80 px-2 py-1 text-xs text-slate-200">
|
||||
@@ -407,7 +409,7 @@ function PtzMobileFullscreen({ ptz, layout }) {
|
||||
<PtzMobileControlsPanel ptz={ptz} disabled={!ptz?.isOperator} />
|
||||
</section>
|
||||
<section className="grid gap-0.5 md:grid-cols-[minmax(0,1fr)_minmax(0,0.7fr)]">
|
||||
<ChatPanel title="Chat" />
|
||||
<ChatPanel title="Chat" allowSpectatorInput inputTarget="overlay" />
|
||||
<div className="space-y-0.5">
|
||||
<PtzQueueSummary ptz={ptz} />
|
||||
<PtzStatePanel ptz={ptz} compact />
|
||||
|
||||
@@ -61,6 +61,7 @@ export function ChatProvider({ children }) {
|
||||
const [isChatFocused, setIsChatFocused] = useState(false);
|
||||
const panelInputRef = useRef(null);
|
||||
const hudInputRef = useRef(null);
|
||||
const overlayInputRef = useRef(null);
|
||||
const audioRef = useRef(null);
|
||||
const typingRef = useRef(new Map());
|
||||
const typingAlertRef = useRef(new Map());
|
||||
@@ -236,7 +237,17 @@ export function ChatProvider({ children }) {
|
||||
);
|
||||
|
||||
const registerInputRef = useCallback((el, options = {}) => {
|
||||
const target = options?.target === 'hud' ? 'hud' : 'panel';
|
||||
const target = options?.target === 'overlay' ? 'overlay' : options?.target === 'hud' ? 'hud' : 'panel';
|
||||
/*
|
||||
Fullscreen surfaces such as PTZ need chat-key focus while they are mounted
|
||||
without stealing the normal HUD ref permanently. Keep overlay chat as a
|
||||
separate highest-priority slot so unmounting the portal only clears the
|
||||
overlay target and leaves the driver HUD/panel refs intact.
|
||||
*/
|
||||
if (target === 'overlay') {
|
||||
overlayInputRef.current = el;
|
||||
return;
|
||||
}
|
||||
if (target === 'hud') {
|
||||
hudInputRef.current = el;
|
||||
} else {
|
||||
@@ -246,11 +257,12 @@ export function ChatProvider({ children }) {
|
||||
|
||||
const focusChat = useCallback(() => {
|
||||
setIsChatFocused(true);
|
||||
(hudInputRef.current || panelInputRef.current)?.focus();
|
||||
(overlayInputRef.current || hudInputRef.current || panelInputRef.current)?.focus();
|
||||
}, []);
|
||||
|
||||
const blurChat = useCallback(() => {
|
||||
setIsChatFocused(false);
|
||||
overlayInputRef.current?.blur();
|
||||
hudInputRef.current?.blur();
|
||||
panelInputRef.current?.blur();
|
||||
}, []);
|
||||
|
||||
Reference in New Issue
Block a user