Files
microsoft--magentic-ui/frontend/src/components/browser/PlaybackControlsRow.tsx
T
wehub-resource-sync 25576b0be6
Checks (magui2.0) / python-lint (push) Failing after 1s
Checks (magui2.0) / python-test (push) Failing after 0s
Checks (magui2.0) / frontend-lint (push) Failing after 0s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
Checks (magui2.0) / python-format (push) Failing after 1s
CodeQL Advanced / Analyze (python) (push) Failing after 0s
Checks (magui2.0) / python-pyright (push) Failing after 1s
Checks (magui2.0) / frontend-format (push) Failing after 1s
Checks (magui2.0) / frontend-typecheck (push) Failing after 0s
Checks (magui2.0) / frontend-test (push) Failing after 2s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:24:32 +08:00

176 lines
5.6 KiB
TypeScript

/**
* PlaybackControlsRow Component
*
* Unified controls row used by both BrowserEmbed and BrowserExpanded.
*
* Layout depends on mode:
* - Live (VNC connected): [LIVE toggle (ON)] ............. [Control button]
* - History: [LIVE toggle (OFF)] | [ProgressBar] [StepBack] [StepForward]
* - VNC only (no history): [LIVE toggle (disabled, ON)] ... [Control button]
* - History only (no VNC): [LIVE toggle (disabled, OFF)] | [ProgressBar] [StepBack] [StepForward]
*
* The LIVE toggle is always visible when this component renders. It is disabled
* when there is nothing to toggle between (no VNC, or no screenshot history).
* Parent components gate rendering on isVncConnected || hasHistory.
*/
import { MousePointer2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Switch } from '@/components/ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { ProgressBar } from './ProgressBar'
import { PlaybackButtons } from './PlaybackButtons'
import { cn } from '@/lib/utils'
// =============================================================================
// Types
// =============================================================================
interface PlaybackControlsRowProps {
/** Additional classes (e.g. padding) */
className?: string
/** Use smaller buttons (h-8) for compact layouts like BrowserEmbed */
compact?: boolean
// -- VNC state --
/** Whether VNC is connected (determines if LIVE toggle is interactive) */
isVncConnected: boolean
// -- Control button (live mode only) --
/** Called when Control button is clicked; omit to hide the button entirely */
onControlClick?: () => void
// -- Playback state --
/** Whether display is in live mode (pre-computed by usePlaybackControls) */
displayIsLive: boolean
/** Set live mode on/off (matches Switch onCheckedChange signature) */
onSetLive: (isLive: boolean) => void
// -- ProgressBar (history mode only) --
/** Total number of screenshots */
total: number
/** Current screenshot index (clamped) */
validCurrentIndex: number
/** Called when user clicks/drags to a screenshot index */
onIndexChange: (index: number) => void
// -- PlaybackButtons (history mode only) --
/** Whether Prev button can be clicked */
canStepBack: boolean
/** Whether Next button can be clicked */
canStepForward: boolean
/** Handler for Prev button */
onStepBack: () => void
/** Handler for Next button */
onStepForward: () => void
/** Tooltip text for Next button */
nextTooltip: string
}
// =============================================================================
// Component
// =============================================================================
export function PlaybackControlsRow({
className,
compact,
isVncConnected,
onControlClick,
displayIsLive,
onSetLive,
total,
validCurrentIndex,
onIndexChange,
canStepBack,
canStepForward,
onStepBack,
onStepForward,
nextTooltip,
}: PlaybackControlsRowProps) {
// Toggle is interactive only when VNC is connected and there's history to switch between
const canToggle = isVncConnected && total > 0
return (
<div className={cn('flex items-center gap-3', className)}>
{/* LIVE toggle — always visible, disabled when not interactive */}
<div className="flex shrink-0 flex-col items-center gap-0.5">
<span
className={cn(
'text-[10px] leading-4 font-bold tracking-[0.2em]',
canToggle ? 'text-secondary-foreground' : 'text-muted-foreground'
)}
>
LIVE
</span>
<Tooltip>
<TooltipTrigger asChild>
<Switch
checked={displayIsLive}
onCheckedChange={onSetLive}
disabled={!canToggle}
aria-label="Toggle live view"
/>
</TooltipTrigger>
<TooltipContent>
{!canToggle
? displayIsLive
? 'Live view (no history yet)'
: 'No live connection'
: displayIsLive
? 'Switch to history view'
: 'Switch to live view'}
</TooltipContent>
</Tooltip>
</div>
{/* Live mode: Control button on far right */}
{displayIsLive && (
<>
{/* Spacer to push Control to the right */}
<div className="flex-1" />
{/* Control button */}
{onControlClick && (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
className={cn('shrink-0 gap-1.5 px-3 text-sm', compact && 'h-8')}
onClick={onControlClick}
>
<MousePointer2 className="size-3.5" />
Control
</Button>
</TooltipTrigger>
<TooltipContent>Take control of the browser</TooltipContent>
</Tooltip>
)}
</>
)}
{/* History mode: separator + progress bar + step buttons */}
{!displayIsLive && (
<>
{/* Vertical separator between toggle and progress bar */}
<div className="bg-border w-px shrink-0 self-stretch" />
<ProgressBar
total={total}
currentIndex={validCurrentIndex}
onIndexChange={onIndexChange}
/>
<PlaybackButtons
compact={compact}
canStepBack={canStepBack}
canStepForward={canStepForward}
onStepBack={onStepBack}
onStepForward={onStepForward}
nextTooltip={nextTooltip}
/>
</>
)}
</div>
)
}