diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index 570ec280..328db39d 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -1,4 +1,4 @@ -import { Check, ChevronDown, Languages } from "lucide-react"; +import { Check, ChevronDown, Columns3, Languages, Rows3 } from "lucide-react"; import { useCallback, useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { BsPauseCircle, BsPlayCircle, BsRecordCircle } from "react-icons/bs"; @@ -27,6 +27,7 @@ import { useCameraDevices } from "../../hooks/useCameraDevices"; import { useMicrophoneDevices } from "../../hooks/useMicrophoneDevices"; import { useScreenRecorder } from "../../hooks/useScreenRecorder"; import { requestCameraAccess } from "../../lib/requestCameraAccess"; +import { loadUserPreferences, saveUserPreferences } from "../../lib/userPreferences"; import { formatTimePadded } from "../../utils/timeUtils"; import { AudioLevelMeter } from "../ui/audio-level-meter"; import { Button } from "../ui/button"; @@ -59,6 +60,7 @@ const ICON_CONFIG = { type IconName = keyof typeof ICON_CONFIG; +/** Renders the configured icon for a HUD control. */ function getIcon(name: IconName, className?: string) { const { icon: Icon, size } = ICON_CONFIG[name]; return ; @@ -77,7 +79,10 @@ const windowBtnClasses = "flex h-8 w-8 items-center justify-center rounded-lg transition-all duration-150 cursor-pointer opacity-50 hover:opacity-90 hover:bg-white/[0.08]"; const hudSidebarClasses = "ml-0.5 pl-1.5 border-l border-white/10 flex items-center gap-0.5"; +const hudSidebarVerticalClasses = + "mt-0.5 pt-1.5 border-t border-white/10 flex flex-col items-center gap-0.5"; +/** Launches the floating recording HUD and its recorder controls. */ export function LaunchWindow() { const t = useScopedT("launch"); const availableLocales = getAvailableLocales(); @@ -128,6 +133,9 @@ export function LaunchWindow() { const [isWebcamFocused, setIsWebcamFocused] = useState(false); const webcamExpanded = isWebcamHovered || isWebcamFocused; const [isLanguageMenuOpen, setIsLanguageMenuOpen] = useState(false); + const [trayLayout, setTrayLayout] = useState<"horizontal" | "vertical">( + () => loadUserPreferences().trayLayout, + ); const [supportsCursorModeToggle, setSupportsCursorModeToggle] = useState(false); const languageTriggerRef = useRef(null); const languageMenuPanelRef = useRef(null); @@ -365,6 +373,12 @@ export function LaunchWindow() { window.electronAPI.hudOverlayClose(); } }; + /** Switches the HUD between horizontal and vertical tray layouts. */ + const toggleTrayLayout = () => { + const nextLayout = trayLayout === "horizontal" ? "vertical" : "horizontal"; + setTrayLayout(nextLayout); + saveUserPreferences({ trayLayout: nextLayout }); + }; const toggleMicrophone = () => { if (!recording) { @@ -589,7 +603,12 @@ export function LaunchWindow() { {/* HUD bar — fixed at bottom center, viewport-relative, never moves */}
setHudMouseEventsEnabled(true)} onPointerDown={() => setHudMouseEventsEnabled(true)} onMouseEnter={() => setHudMouseEventsEnabled(true)} @@ -610,6 +629,33 @@ export function LaunchWindow() { {getIcon("drag", "text-white/30")}
+ + + + {/* Source selector */}