()
| 46 | } |
| 47 | |
| 48 | export default function App() { |
| 49 | const [apps, setApps] = useState<AppConfig[]>([]); |
| 50 | const [loading, setLoading] = useState(true); |
| 51 | const [showSettings, setShowSettings] = useState(false); |
| 52 | |
| 53 | // Load apps from persistent store |
| 54 | useEffect(() => { |
| 55 | async function load() { |
| 56 | if (window.electronAPI?.appConfig) { |
| 57 | const loaded = await window.electronAPI.appConfig.load(); |
| 58 | setApps(loaded); |
| 59 | } else { |
| 60 | // Fallback for dev without electron — use full config with production URLs |
| 61 | setApps(DEFAULT_APPS); |
| 62 | } |
| 63 | setLoading(false); |
| 64 | } |
| 65 | load(); |
| 66 | }, []); |
| 67 | |
| 68 | const enabledApps = apps.filter((a) => a.enabled); |
| 69 | const rawAppDefs = enabledApps.map(toAppDefinition); |
| 70 | // Keep this in sync with Sidebar's pinned-bottom order. |
| 71 | const PINNED_BOTTOM_ORDER = ["dispatch", "starter"]; |
| 72 | const pinnedBottomDefs = PINNED_BOTTOM_ORDER.map((id) => |
| 73 | rawAppDefs.find((a) => a.id === id), |
| 74 | ).filter((a): a is NonNullable<typeof a> => !!a); |
| 75 | const mainDefs = rawAppDefs.filter( |
| 76 | (a) => !PINNED_BOTTOM_ORDER.includes(a.id), |
| 77 | ); |
| 78 | const appDefs = [...mainDefs, ...pinnedBottomDefs]; |
| 79 | |
| 80 | const defaultApp = appDefs.find((a) => !a.placeholder) ?? appDefs[0]; |
| 81 | |
| 82 | const [activeSidebarAppId, setActiveSidebarAppId] = useState(""); |
| 83 | const [appTabs, setAppTabs] = useState<Record<string, AppTabState>>({}); |
| 84 | |
| 85 | // Initialize tabs when apps load |
| 86 | useEffect(() => { |
| 87 | if (enabledApps.length === 0) return; |
| 88 | setAppTabs((prev) => { |
| 89 | // Only init tabs for apps that don't have tabs yet |
| 90 | const next = { ...prev }; |
| 91 | for (const app of enabledApps) { |
| 92 | if (!next[app.id]) { |
| 93 | const tab = createTab(app); |
| 94 | next[app.id] = { tabs: [tab], activeTabId: tab.id }; |
| 95 | } |
| 96 | } |
| 97 | return next; |
| 98 | }); |
| 99 | setActiveSidebarAppId((prev) => { |
| 100 | if (prev && enabledApps.find((a) => a.id === prev)) return prev; |
| 101 | const def = |
| 102 | enabledApps.find((a) => !("placeholder" in a)) ?? enabledApps[0]; |
| 103 | return def?.id ?? ""; |
| 104 | }); |
| 105 | }, [enabledApps.map((a) => a.id).join(",")]); |
nothing calls this directly
no test coverage detected