import { ChevronDown } from "lucide-react"; import { useEffect, useState } from "react"; import { BsRecordCircle } from "react-icons/bs"; import { FaRegStopCircle } from "react-icons/fa"; import { FaFolderOpen } from "react-icons/fa6"; import { FiMinus, FiX } from "react-icons/fi"; import { MdMic, MdMicOff, MdMonitor, MdVideocam, MdVideocamOff, MdVideoFile, MdVolumeOff, MdVolumeUp, } from "react-icons/md"; import { RxDragHandleDots2 } from "react-icons/rx"; import { useAudioLevelMeter } from "../../hooks/useAudioLevelMeter"; import { useMicrophoneDevices } from "../../hooks/useMicrophoneDevices"; import { useScreenRecorder } from "../../hooks/useScreenRecorder"; import { formatTimePadded } from "../../utils/timeUtils"; import { AudioLevelMeter } from "../ui/audio-level-meter"; import { Tooltip } from "../ui/tooltip"; import styles from "./LaunchWindow.module.css"; const ICON_SIZE = 20; const ICON_CONFIG = { drag: { icon: RxDragHandleDots2, size: ICON_SIZE }, monitor: { icon: MdMonitor, size: ICON_SIZE }, volumeOn: { icon: MdVolumeUp, size: ICON_SIZE }, volumeOff: { icon: MdVolumeOff, size: ICON_SIZE }, micOn: { icon: MdMic, size: ICON_SIZE }, micOff: { icon: MdMicOff, size: ICON_SIZE }, webcamOn: { icon: MdVideocam, size: ICON_SIZE }, webcamOff: { icon: MdVideocamOff, size: ICON_SIZE }, stop: { icon: FaRegStopCircle, size: ICON_SIZE }, record: { icon: BsRecordCircle, size: ICON_SIZE }, videoFile: { icon: MdVideoFile, size: ICON_SIZE }, folder: { icon: FaFolderOpen, size: ICON_SIZE }, minimize: { icon: FiMinus, size: ICON_SIZE }, close: { icon: FiX, size: ICON_SIZE }, } as const; type IconName = keyof typeof ICON_CONFIG; function getIcon(name: IconName, className?: string) { const { icon: Icon, size } = ICON_CONFIG[name]; return ; } const hudGroupClasses = "flex items-center gap-0.5 bg-white/5 rounded-full transition-colors duration-150 hover:bg-white/[0.08]"; const hudIconBtnClasses = "flex items-center justify-center p-2 rounded-full transition-all duration-150 cursor-pointer text-white hover:bg-white/10 hover:scale-[1.08] active:scale-95"; const windowBtnClasses = "flex items-center justify-center p-2 rounded-full transition-all duration-150 cursor-pointer opacity-50 hover:opacity-90 hover:bg-white/[0.08]"; export function LaunchWindow() { const { recording, toggleRecording, microphoneEnabled, setMicrophoneEnabled, microphoneDeviceId, setMicrophoneDeviceId, systemAudioEnabled, setSystemAudioEnabled, webcamEnabled, setWebcamEnabled, } = useScreenRecorder(); const [recordingStart, setRecordingStart] = useState(null); const [elapsed, setElapsed] = useState(0); const showMicControls = microphoneEnabled && !recording; const { devices, selectedDeviceId, setSelectedDeviceId } = useMicrophoneDevices(microphoneEnabled); const { level } = useAudioLevelMeter({ enabled: showMicControls, deviceId: microphoneDeviceId, }); useEffect(() => { if (selectedDeviceId && selectedDeviceId !== "default") { setMicrophoneDeviceId(selectedDeviceId); } }, [selectedDeviceId, setMicrophoneDeviceId]); useEffect(() => { let timer: NodeJS.Timeout | null = null; if (recording) { if (!recordingStart) setRecordingStart(Date.now()); timer = setInterval(() => { if (recordingStart) { setElapsed(Math.floor((Date.now() - recordingStart) / 1000)); } }, 1000); } else { setRecordingStart(null); setElapsed(0); if (timer) clearInterval(timer); } return () => { if (timer) clearInterval(timer); }; }, [recording, recordingStart]); const [selectedSource, setSelectedSource] = useState("Screen"); const [hasSelectedSource, setHasSelectedSource] = useState(false); useEffect(() => { const checkSelectedSource = async () => { if (window.electronAPI) { const source = await window.electronAPI.getSelectedSource(); if (source) { setSelectedSource(source.name); setHasSelectedSource(true); } else { setSelectedSource("Screen"); setHasSelectedSource(false); } } }; checkSelectedSource(); const interval = setInterval(checkSelectedSource, 500); return () => clearInterval(interval); }, []); const openSourceSelector = () => { if (window.electronAPI) { window.electronAPI.openSourceSelector(); } }; const openVideoFile = async () => { const result = await window.electronAPI.openVideoFilePicker(); if (result.canceled) { return; } if (result.success && result.path) { await window.electronAPI.setCurrentVideoPath(result.path); await window.electronAPI.switchToEditor(); } }; const openProjectFile = async () => { const result = await window.electronAPI.loadProjectFile(); if (result.canceled || !result.success) return; await window.electronAPI.switchToEditor(); }; const sendHudOverlayHide = () => { if (window.electronAPI && window.electronAPI.hudOverlayHide) { window.electronAPI.hudOverlayHide(); } }; const sendHudOverlayClose = () => { if (window.electronAPI && window.electronAPI.hudOverlayClose) { window.electronAPI.hudOverlayClose(); } }; const toggleMicrophone = () => { if (!recording) { setMicrophoneEnabled(!microphoneEnabled); } }; return (
{/* Mic controls panel */} {showMicControls && (
)} {/* Main pill bar */}
{/* Drag handle */}
{getIcon("drag", "text-white/30")}
{/* Source selector */} {/* Audio controls group */}
{/* Record/Stop group */} {/* Open video file */} {/* Open project */} {/* Window controls */}
); }