Initial commit

This commit is contained in:
monoadmin
2026-04-10 15:36:33 -07:00
commit d6d7338a39
134 changed files with 16232 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
'use client'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import {
Maximize2,
Minimize2,
Monitor,
Volume2,
VolumeX,
Settings,
X,
RefreshCw,
Signal,
SignalLow,
SignalMedium,
Keyboard,
MousePointer2,
Loader2,
CheckCircle2
} from 'lucide-react'
interface Session {
id: string
machine_id: string | null
machine_name: string | null
started_at: string
}
type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error'
interface ViewerToolbarProps {
session: Session | null
connectionState: ConnectionState
isFullscreen: boolean
quality: 'high' | 'medium' | 'low'
isMuted: boolean
onToggleFullscreen: () => void
onQualityChange: (quality: 'high' | 'medium' | 'low') => void
onToggleMute: () => void
onDisconnect: () => void
onReconnect: () => void
}
export function ViewerToolbar({
session,
connectionState,
isFullscreen,
quality,
isMuted,
onToggleFullscreen,
onQualityChange,
onToggleMute,
onDisconnect,
onReconnect,
}: ViewerToolbarProps) {
const getQualityIcon = () => {
switch (quality) {
case 'high': return Signal
case 'medium': return SignalMedium
case 'low': return SignalLow
}
}
const getConnectionBadge = () => {
switch (connectionState) {
case 'connecting':
return (
<span className="flex items-center gap-2 text-yellow-500">
<Loader2 className="h-3 w-3 animate-spin" />
Connecting
</span>
)
case 'connected':
return (
<span className="flex items-center gap-2 text-green-500">
<span className="h-2 w-2 rounded-full bg-green-500" />
Connected
</span>
)
case 'disconnected':
return (
<span className="flex items-center gap-2 text-muted-foreground">
<span className="h-2 w-2 rounded-full bg-muted-foreground" />
Disconnected
</span>
)
case 'error':
return (
<span className="flex items-center gap-2 text-destructive">
<span className="h-2 w-2 rounded-full bg-destructive" />
Error
</span>
)
}
}
const QualityIcon = getQualityIcon()
return (
<div className="flex items-center justify-between h-12 px-4 bg-card/95 backdrop-blur border-b border-border/50">
{/* Left section */}
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<Monitor className="h-4 w-4 text-primary" />
<span className="font-medium text-sm">
{session?.machine_name || 'Remote Machine'}
</span>
</div>
<div className="text-sm">
{getConnectionBadge()}
</div>
</div>
{/* Right section */}
<div className="flex items-center gap-1">
{/* Input indicators */}
<div className="flex items-center gap-2 px-3 border-r border-border/50 mr-2">
<MousePointer2 className="h-4 w-4 text-muted-foreground" />
<Keyboard className="h-4 w-4 text-muted-foreground" />
</div>
{/* Quality selector */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="h-8 w-8">
<QualityIcon className="h-4 w-4" />
<span className="sr-only">Quality</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Stream Quality</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => onQualityChange('high')}>
<Signal className="mr-2 h-4 w-4" />
High
{quality === 'high' && <CheckCircle2 className="ml-auto h-4 w-4 text-primary" />}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onQualityChange('medium')}>
<SignalMedium className="mr-2 h-4 w-4" />
Medium
{quality === 'medium' && <CheckCircle2 className="ml-auto h-4 w-4 text-primary" />}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onQualityChange('low')}>
<SignalLow className="mr-2 h-4 w-4" />
Low
{quality === 'low' && <CheckCircle2 className="ml-auto h-4 w-4 text-primary" />}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{/* Mute toggle */}
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggleMute}>
{isMuted ? (
<VolumeX className="h-4 w-4" />
) : (
<Volume2 className="h-4 w-4" />
)}
<span className="sr-only">{isMuted ? 'Unmute' : 'Mute'}</span>
</Button>
{/* Fullscreen toggle */}
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggleFullscreen}>
{isFullscreen ? (
<Minimize2 className="h-4 w-4" />
) : (
<Maximize2 className="h-4 w-4" />
)}
<span className="sr-only">{isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}</span>
</Button>
{/* Reconnect */}
{connectionState === 'disconnected' && (
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onReconnect}>
<RefreshCw className="h-4 w-4" />
<span className="sr-only">Reconnect</span>
</Button>
)}
{/* Disconnect */}
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={onDisconnect}
>
<X className="h-4 w-4" />
<span className="sr-only">Disconnect</span>
</Button>
</div>
</div>
)
}