| 'use client' |
|
|
| import { motion } from 'framer-motion' |
| import { |
| Coffee, |
| Image as ImageIcon, |
| Type, |
| AlertTriangle, |
| Zap, |
| StopCircle |
| } from 'lucide-react' |
|
|
| interface Scene { |
| brb_active: boolean |
| logo_active: boolean |
| overlay_text: string |
| } |
|
|
| interface SceneControlProps { |
| scene?: Scene |
| onToggleBRB: () => void |
| onToggleLogo: () => void |
| } |
|
|
| export default function SceneControl({ scene, onToggleBRB, onToggleLogo }: SceneControlProps) { |
| return ( |
| <motion.div |
| initial={{ opacity: 0, y: 20 }} |
| animate={{ opacity: 1, y: 0 }} |
| className="glass rounded-xl p-6" |
| > |
| <h2 className="text-lg font-semibold flex items-center gap-2 mb-4"> |
| <Zap className="w-5 h-5 text-yellow-400" /> |
| Scene Controls |
| </h2> |
| |
| <div className="grid grid-cols-2 md:grid-cols-3 gap-4"> |
| {/* BRB Toggle */} |
| <button |
| onClick={onToggleBRB} |
| className={`relative p-4 rounded-xl border-2 transition-all duration-300 ${ |
| scene?.brb_active |
| ? 'bg-yellow-500/20 border-yellow-500 shadow-lg shadow-yellow-500/20' |
| : 'bg-white/5 border-white/10 hover:border-white/30' |
| }`} |
| > |
| <div className="flex flex-col items-center gap-2"> |
| <div className={`p-3 rounded-full ${ |
| scene?.brb_active ? 'bg-yellow-500/30' : 'bg-white/10' |
| }`}> |
| <Coffee className={`w-6 h-6 ${ |
| scene?.brb_active ? 'text-yellow-400' : 'text-gray-400' |
| }`} /> |
| </div> |
| <span className={`font-medium ${ |
| scene?.brb_active ? 'text-yellow-400' : 'text-gray-300' |
| }`}> |
| BRB |
| </span> |
| <span className="text-xs text-gray-500"> |
| {scene?.brb_active ? 'ACTIVE' : 'OFF'} |
| </span> |
| </div> |
| {scene?.brb_active && ( |
| <motion.div |
| layoutId="active-indicator" |
| className="absolute inset-0 border-2 border-yellow-500 rounded-xl" |
| initial={false} |
| transition={{ type: "spring", stiffness: 500, damping: 30 }} |
| /> |
| )} |
| </button> |
| |
| {/* Logo Toggle */} |
| <button |
| onClick={onToggleLogo} |
| className={`relative p-4 rounded-xl border-2 transition-all duration-300 ${ |
| scene?.logo_active |
| ? 'bg-cyan-500/20 border-cyan-500 shadow-lg shadow-cyan-500/20' |
| : 'bg-white/5 border-white/10 hover:border-white/30' |
| }`} |
| > |
| <div className="flex flex-col items-center gap-2"> |
| <div className={`p-3 rounded-full ${ |
| scene?.logo_active ? 'bg-cyan-500/30' : 'bg-white/10' |
| }`}> |
| <ImageIcon className={`w-6 h-6 ${ |
| scene?.logo_active ? 'text-cyan-400' : 'text-gray-400' |
| }`} /> |
| </div> |
| <span className={`font-medium ${ |
| scene?.logo_active ? 'text-cyan-400' : 'text-gray-300' |
| }`}> |
| LIVE Badge |
| </span> |
| <span className="text-xs text-gray-500"> |
| {scene?.logo_active ? 'ACTIVE' : 'OFF'} |
| </span> |
| </div> |
| </button> |
| |
| {/* Emergency Stop */} |
| <button |
| onClick={() => { |
| if (confirm('Stop stream immediately?')) { |
| fetch('/api/stop', { method: 'POST' }) |
| } |
| }} |
| className="relative p-4 rounded-xl border-2 bg-red-500/10 border-red-500/50 |
| hover:bg-red-500/20 transition-all duration-300 group" |
| > |
| <div className="flex flex-col items-center gap-2"> |
| <div className="p-3 rounded-full bg-red-500/30 group-hover:bg-red-500/50 transition-colors"> |
| <StopCircle className="w-6 h-6 text-red-400" /> |
| </div> |
| <span className="font-medium text-red-400"> |
| EMERGENCY STOP |
| </span> |
| <span className="text-xs text-gray-500"> |
| Click to stop |
| </span> |
| </div> |
| </button> |
| </div> |
| |
| {/* Overlay Text Input */} |
| <div className="mt-4 p-4 bg-white/5 rounded-lg"> |
| <div className="flex items-center gap-2 mb-2"> |
| <Type className="w-4 h-4 text-gray-400" /> |
| <span className="text-sm text-gray-400">Custom Overlay Text</span> |
| </div> |
| <div className="flex gap-2"> |
| <input |
| type="text" |
| placeholder="Short overlay text..." |
| className="flex-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 |
| text-sm text-white placeholder-gray-500 focus:outline-none |
| focus:border-cyan-500/50" |
| onKeyDown={(e) => { |
| if (e.key === 'Enter') { |
| fetch('/api/scene/overlay?text=' + encodeURIComponent(e.currentTarget.value), { |
| method: 'POST' |
| }) |
| e.currentTarget.value = '' |
| } |
| }} |
| /> |
| <button |
| onClick={(e) => { |
| const input = e.currentTarget.previousElementSibling as HTMLInputElement |
| if (input.value) { |
| fetch('/api/scene/overlay?text=' + encodeURIComponent(input.value), { |
| method: 'POST' |
| }) |
| input.value = '' |
| } |
| }} |
| className="px-4 py-2 bg-cyan-500/20 hover:bg-cyan-500/30 text-cyan-400 |
| rounded-lg text-sm transition-colors" |
| > |
| Set |
| </button> |
| </div> |
| {scene?.overlay_text && ( |
| <p className="mt-2 text-sm text-cyan-400"> |
| Current: {scene.overlay_text} |
| </p> |
| )} |
| </div> |
| |
| {/* Warning */} |
| <div className="mt-4 flex items-start gap-2 p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg"> |
| <AlertTriangle className="w-5 h-5 text-yellow-400 flex-shrink-0 mt-0.5" /> |
| <p className="text-sm text-yellow-200"> |
| Scene changes restart FFmpeg briefly. Keep overlay text short. |
| </p> |
| </div> |
| </motion.div> |
| ) |
| } |
|
|