/** * Root component. Smoke-tests the full hexagonal pipeline by calling `health` * through the {@link SystemGateway} (never `invoke()` directly) and listening * for relayed domain events. */ import { useEffect, useState } from "react"; import type { DomainEvent, HealthReport } from "@/domain"; import { ProjectsView } from "@/features/projects"; import { FirstRunWizard, ProfilesSettings } from "@/features/first-run"; import { AnnouncementsProvider } from "@/features/announcements"; import { Button, Panel, Spinner, Toolbar } from "@/shared"; import { useGateways, shouldUseMock } from "./di"; export function App() { const { system, profile } = useGateways(); const [health, setHealth] = useState(null); const [error, setError] = useState(null); const [events, setEvents] = useState([]); // First-run gating: null while loading, then true (wizard) / false (normal). const [firstRun, setFirstRun] = useState(null); const [showSettings, setShowSettings] = useState(false); useEffect(() => { let unsub: (() => void) | undefined; let cancelled = false; system .onDomainEvent((e) => setEvents((prev) => [...prev, e])) .then((u) => { if (cancelled) u(); else unsub = u; }) .catch(() => { /* event relay unavailable in this environment; ignore for smoke test */ }); system .health("ui-boot") .then((report) => setHealth(report)) .catch((e: unknown) => setError(describeError(e))); return () => { cancelled = true; unsub?.(); }; }, [system]); useEffect(() => { let cancelled = false; profile .firstRunState() .then((s) => { if (!cancelled) setFirstRun(s.isFirstRun); }) .catch(() => { // First-run state unavailable: behave as a normal (non-first) run. if (!cancelled) setFirstRun(false); }); return () => { cancelled = true; }; }, [profile]); return (
{/* ── Header ── */}

IdeA

{shouldUseMock() ? "mock" : "tauri"}
{health ? ( <> backend v{health.version} ·{" "} {events.length} events ) : error ? ( backend unavailable ) : ( checking… )} {!firstRun && ( )}
{/* ── Body ── */}
{error && (

Error: {error}

)} {firstRun ? ( // First launch: the wizard takes over the area, in a scrollable column // (it can be taller than the viewport).
setFirstRun(false)} />
) : showSettings ? ( // Settings is a full scrollable view (its "Configure profiles" reopens // the wizard, which can also exceed the viewport height).
) : ( // ProjectsView owns the full remaining area (its own IDE layout). )}
); } function describeError(e: unknown): string { if (e && typeof e === "object" && "message" in e) { return String((e as { message: unknown }).message); } return String(e); }