import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { create } from 'zustand'; import { getElectronAPI } from '../../platform/platform'; import { useVoiceStore } from '../../stores/voiceStore'; // --------------------------------------------------------------------------- // Zustand micro-store — bridges the event-driven API to React state // --------------------------------------------------------------------------- interface ScreenPickerState { isOpen: boolean; sources: ElectronScreenSource[]; } const useScreenPickerStore = create(() => ({ isOpen: false, sources: [], })); // --------------------------------------------------------------------------- // Close helper — sends selection back to main process // --------------------------------------------------------------------------- function closePicker(sourceId: string | null, shareAudio?: boolean) { const api = getElectronAPI(); if (api) api.selectScreenSource(sourceId, shareAudio); useScreenPickerStore.setState({ isOpen: false, sources: [] }); } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- type Tab = 'screens' | 'windows'; export function ScreenSharePicker() { const { isOpen, sources } = useScreenPickerStore(); const [activeTab, setActiveTab] = useState('screens'); const [selectedId, setSelectedId] = useState(null); const [search, setSearch] = useState(''); const shareAudio = useVoiceStore((s) => s.screenShareConfig.shareAudio); const setScreenShareConfig = useVoiceStore((s) => s.setScreenShareConfig); // Register listener for sources from main process (once on mount) useEffect(() => { const api = getElectronAPI(); console.log('[Picker] Mounted, registering onScreenShareSources listener, hasAPI:', !!api); if (!api) return; api.onScreenShareSources((incomingSources) => { console.log('[Picker] Received', incomingSources.length, 'sources from main process'); useScreenPickerStore.setState({ isOpen: true, sources: incomingSources, }); }); }, []); // Reset local state when picker opens useEffect(() => { if (isOpen) { setActiveTab('screens'); setSelectedId(null); setSearch(''); } }, [isOpen]); // Auto-select if there's exactly one screen useEffect(() => { if (isOpen && sources.length > 0 && !selectedId) { const screens = sources.filter((s) => s.isScreen); if (screens.length === 1 && activeTab === 'screens') { setSelectedId(screens[0]!.id); } } }, [isOpen, sources, selectedId, activeTab]); const handleKeyDown = useCallback((e: KeyboardEvent) => { if (e.key === 'Escape') { closePicker(null); } }, []); useEffect(() => { if (isOpen) { document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); } }, [isOpen, handleKeyDown]); const screens = useMemo(() => sources.filter((s) => s.isScreen), [sources]); const windows = useMemo(() => { const wins = sources.filter((s) => !s.isScreen); if (!search.trim()) return wins; const q = search.trim().toLowerCase(); return wins.filter((w) => w.name.toLowerCase().includes(q)); }, [sources, search]); if (!isOpen) return null; const activeSources = activeTab === 'screens' ? screens : windows; return (
{/* Backdrop */}
closePicker(null)} /> {/* Modal card */}
{/* Header */}

Share Your Screen

{/* Tabs */}
{ setActiveTab('screens'); setSelectedId(null); }} label="Screens" count={screens.length} /> { setActiveTab('windows'); setSelectedId(null); }} label="Windows" count={windows.length} />
{/* Search (windows tab only) */} {activeTab === 'windows' && (
setSearch(e.target.value)} placeholder="Search windows..." className="input-search w-full" autoFocus />
)} {/* Source grid */}
{activeSources.length === 0 ? (
{activeTab === 'windows' && search.trim() ? 'No windows match your search' : `No ${activeTab} available`}
) : (
{activeSources.map((source) => ( setSelectedId(source.id)} onDoubleClick={() => closePicker(source.id, shareAudio)} /> ))}
)}
{/* Footer */}
{shareAudio && (
May echo voices back to viewers — use Chrome browser for echo-free audio
)}
); } // --------------------------------------------------------------------------- // Sub-components // --------------------------------------------------------------------------- function TabButton({ active, onClick, label, count }: { active: boolean; onClick: () => void; label: string; count: number; }) { return ( ); } function SourceCard({ source, selected, onClick, onDoubleClick }: { source: ElectronScreenSource; selected: boolean; onClick: () => void; onDoubleClick: () => void; }) { return ( ); }