@@ -12,6 +12,10 @@ import {
1212} from "react" ;
1313
1414import { BrowserSurfaceSlot } from "~/browser/BrowserSurfaceSlot" ;
15+ import {
16+ findActiveBrowserRecordingRuntimeTabId ,
17+ useActiveBrowserRecordingTabIds ,
18+ } from "~/browser/browserRecording" ;
1519import { useBrowserSurfaceStore } from "~/browser/browserSurfaceStore" ;
1620import type { BrowserViewportResizeDirection } from "~/browser/browserViewportLayout" ;
1721import { previewRuntimeTabId } from "~/browser/previewRuntimeTabId" ;
@@ -151,6 +155,10 @@ function BrowserMiniPlayer({
151155 const previewState = useThreadPreviewState ( threadRef ) ;
152156 const snapshot = previewState . sessions [ tabId ] ?? null ;
153157 const runtimeTabId = previewRuntimeTabId ( threadRef , previewState . serverEpoch , tabId ) ;
158+ const recordingTabIds = useActiveBrowserRecordingTabIds ( ) ;
159+ const recording =
160+ recordingTabIds . has ( runtimeTabId ) ||
161+ findActiveBrowserRecordingRuntimeTabId ( threadRef , tabId ) !== null ;
154162 const desktopOverlay = previewState . desktopByTabId [ tabId ] ?? null ;
155163 const fittedSourceContent = useBrowserSurfaceStore (
156164 ( state ) => state . byTabId [ runtimeTabId ] ?. fittedSourceContent ?? null ,
@@ -189,6 +197,7 @@ function BrowserMiniPlayer({
189197 sourceSize = { sourceSize }
190198 composerOverlayElement = { composerOverlayElement }
191199 label = "Floating browser preview"
200+ recording = { recording }
192201 onOpenInPanel = { openInPanel }
193202 pillActions = {
194203 < Tooltip >
@@ -314,6 +323,7 @@ function MiniPlayerShell({
314323 label,
315324 onOpenInPanel,
316325 pillActions,
326+ recording = false ,
317327 cornerRadius = frameCornerRadius ,
318328 children,
319329} : {
@@ -324,6 +334,7 @@ function MiniPlayerShell({
324334 readonly label : string ;
325335 readonly onOpenInPanel : ( ) => void ;
326336 readonly pillActions ?: ReactNode ;
337+ readonly recording ?: boolean ;
327338 /** The clip radius for a given frame; the pill stays inside the curve. */
328339 readonly cornerRadius ?: ( frame : PreviewMiniPlayerSize ) => number ;
329340 readonly children : ( frame : PreviewMiniPlayerFrame ) => ReactNode ;
@@ -443,16 +454,30 @@ function MiniPlayerShell({
443454 style = { { right : pillInset , top : pillInset } }
444455 >
445456 < div
446- aria-hidden = "true"
447- className = "absolute right-0 top-0 size-2 rounded-full bg-foreground/25 shadow-sm ring-1 ring-background/70 transition-opacity group-hover:opacity-0 group-focus-within:opacity-0"
448- />
457+ role = { recording ? "status" : undefined }
458+ aria-label = { recording ? "Recording preview" : undefined }
459+ aria-hidden = { ! recording }
460+ className = "absolute right-0 top-0 size-2 transition-opacity group-hover:opacity-0 group-focus-within:opacity-0"
461+ >
462+ < span
463+ className = { cn (
464+ "block size-2 rounded-full shadow-sm ring-1 ring-background/70" ,
465+ recording ? "bg-red-500 motion-safe:animate-status-pulse" : "bg-foreground/25" ,
466+ ) }
467+ />
468+ </ div >
449469 < div
450470 className = "pointer-events-none absolute right-0 top-0 flex h-8 cursor-grab items-center gap-0.5 rounded-lg border border-border/80 bg-popover/92 p-0.5 opacity-0 shadow-lg/20 backdrop-blur-xl transition-opacity group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100 active:cursor-grabbing"
451471 onPointerDown = { ( event ) => beginGesture ( event , null ) }
452472 onPointerMove = { handlePointerMove }
453473 onPointerUp = { endGesture }
454474 onPointerCancel = { endGesture }
455475 >
476+ { recording ? (
477+ < span aria-hidden className = "flex size-6 shrink-0 items-center justify-center" >
478+ < span className = "size-2 rounded-full bg-red-500 motion-safe:animate-status-pulse" />
479+ </ span >
480+ ) : null }
456481 < Tooltip >
457482 < TooltipTrigger
458483 render = {
0 commit comments