diff --git a/src/App.tsx b/src/App.tsx index 8c590dca..b2ca2b0a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { CountdownOverlay } from "./components/countdown/CountdownOverlay"; import { LaunchWindow } from "./components/launch/LaunchWindow"; -import { SourceSelector } from "./components/launch/SourceSelector"; +import { StandaloneSourceSelector } from "./components/launch/StandaloneSourceSelector"; import { UpdateToastWindow } from "./components/launch/UpdateToastWindow"; import { Toaster } from "./components/ui/sonner"; import { ShortcutsConfigDialog } from "./components/video-editor/ShortcutsConfigDialog"; @@ -63,7 +63,7 @@ export default function App() { ); case "source-selector": - return ; + return ; case "countdown": return ; case "update-toast": diff --git a/src/components/launch/StandaloneSourceSelector.tsx b/src/components/launch/StandaloneSourceSelector.tsx new file mode 100644 index 00000000..5cf00278 --- /dev/null +++ b/src/components/launch/StandaloneSourceSelector.tsx @@ -0,0 +1,91 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { SourceSelector } from "./SourceSelector"; +import type { DesktopSource } from "./popovers/launchPopoverTypes"; + +export function StandaloneSourceSelector() { + const [sources, setSources] = useState([]); + const [loading, setLoading] = useState(false); + const [selectedSourceName, setSelectedSourceName] = useState("Screen"); + const [open, setOpen] = useState(true); + + const fetchSources = useCallback(async () => { + if (!window.electronAPI) return; + setLoading(true); + try { + const rawSources = await window.electronAPI.getSources({ + types: ["screen", "window"], + thumbnailSize: { width: 160, height: 90 }, + fetchWindowIcons: true, + }); + setSources( + rawSources.map((s: any) => { + const isWindow = s.id.startsWith("window:"); + const type = s.sourceType ?? (isWindow ? "window" : "screen"); + let displayName = s.name; + let appName = s.appName; + if (isWindow && !appName && s.name.includes(" — ")) { + const parts = s.name.split(" — "); + appName = parts[0]?.trim(); + displayName = parts.slice(1).join(" — ").trim() || s.name; + } else if (isWindow && s.windowTitle) { + displayName = s.windowTitle; + } + return { + id: s.id, + name: displayName, + thumbnail: s.thumbnail, + display_id: s.display_id, + appIcon: s.appIcon, + sourceType: type, + appName, + windowTitle: s.windowTitle ?? displayName, + }; + }), + ); + } catch (error) { + console.error("Failed to fetch sources:", error); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + void fetchSources(); + }, [fetchSources]); + + const handleSourceSelect = useCallback(async (source: DesktopSource) => { + if (!window.electronAPI) return; + try { + const result = await window.electronAPI.selectSource(source); + if (result) { + setSelectedSourceName(source.name); + } + } catch (error) { + console.error("Failed to select source:", error); + } + }, []); + + const screenSources = useMemo( + () => sources.filter((s) => s.sourceType === "screen" || s.id.startsWith("screen:")), + [sources], + ); + const windowSources = useMemo( + () => sources.filter((s) => s.sourceType === "window" || s.id.startsWith("window:")), + [sources], + ); + + return ( +
+ +
+ ); +}