import { useState, useRef, useCallback, useEffect, useLayoutEffect, type JSX } from 'react' import Strip from './components/Strip' import Toolbar from './components/Toolbar' import SettingsPanel from './components/SettingsPanel' import BottomBar from './components/BottomBar' import ScopePopoutBridge from './components/ScopePopoutBridge' import { useSettingsStore } from './stores/settingsStore' import { useAudioStore } from './stores/audioStore' import { SCOPE_KINDS } from '../types/scope' const DEFAULT_SETTINGS_HEIGHT = 400 export default function App(): JSX.Element { const [toolbarVisible, setToolbarVisible] = useState(false) const [settingsOpen, setSettingsOpen] = useState(false) const [settingsPanelHeight, setSettingsPanelHeight] = useState(0) const [bottomBarHeight, setBottomBarHeight] = useState(0) const hideTimeoutRef = useRef | null>(null) const startupBoundsAppliedRef = useRef(false) const toggleScope = useSettingsStore((s) => s.toggleScope) const profiles = useSettingsStore((s) => s.profiles) const activeProfileId = useSettingsStore((s) => s.activeProfileId) useLayoutEffect(() => { if (startupBoundsAppliedRef.current) return const profile = activeProfileId ? profiles[activeProfileId] : null startupBoundsAppliedRef.current = true if (profile?.windowBounds) { window.electronAPI.setWindowBounds(profile.windowBounds) } }, [activeProfileId, profiles]) // Auto-capture on launch useEffect(() => { const { isCapturing, captureStatus, startCapture } = useAudioStore.getState() if (!isCapturing && captureStatus !== 'connecting') { void startCapture() } }, []) const measuredSettingsHeight = settingsPanelHeight > 0 && bottomBarHeight > 0 ? settingsPanelHeight + bottomBarHeight : DEFAULT_SETTINGS_HEIGHT const settingsHeight = settingsOpen ? measuredSettingsHeight : 0 useLayoutEffect(() => { window.electronAPI.setSettingsHeight(settingsHeight) }, [settingsHeight]) const showToolbar = useCallback(() => { if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current) hideTimeoutRef.current = null } setToolbarVisible(true) }, []) const scheduleHide = useCallback(() => { if (settingsOpen) return hideTimeoutRef.current = setTimeout(() => { setToolbarVisible(false) }, 400) }, [settingsOpen]) const handleToggleSettings = useCallback(() => { setSettingsOpen((prev) => !prev) }, []) const handleCloseSettings = useCallback(() => { setSettingsOpen(false) }, []) const handleAltDragStart = useCallback((event: React.MouseEvent) => { if (event.altKey && event.button === 0) { event.preventDefault() window.electronAPI.startWindowMove() } }, []) const handleAltDragEnd = useCallback(() => { window.electronAPI.stopWindowMove() }, []) useEffect(() => { return () => { if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current) } window.electronAPI.stopWindowMove() } }, []) // Keyboard shortcuts from main process useEffect(() => { const unsubs = [ window.electronAPI.onToggleScope((index) => { if (index >= 0 && index < SCOPE_KINDS.length) { toggleScope(SCOPE_KINDS[index]) } }), window.electronAPI.onToggleCapture(() => { const { isCapturing, startCapture, stopCapture } = useAudioStore.getState() if (isCapturing) { stopCapture() } else { startCapture() } }), window.electronAPI.onToggleSettings(() => { setSettingsOpen((prev) => !prev) }), ] return () => unsubs.forEach((unsub) => unsub()) }, [toggleScope]) return (
{settingsOpen && (
)}
) }