({
+ queryKey: queryKeys.hardware.info,
+ queryFn: fetchHardwareInfo,
+ enabled,
+ staleTime: 5 * 60 * 1000, // 5 minutes — matches server cache TTL
+ retry: 1,
+ });
+ }
+ ```
+
+ **4. Create `ui/src/components/onboarding/ModeSelector.tsx`:**
+
+ Exact implementation from RESEARCH.md Pattern 5 and UI-SPEC.md:
+ - Three buttons in a vertical grid (`grid gap-3`).
+ - Each button: `flex flex-col gap-1 rounded-lg border p-4 text-left transition-colors`.
+ - Selected state: `border-primary bg-primary/5`.
+ - Unselected state: `border-border hover:border-muted-foreground/50`.
+ - Label: `font-medium text-sm`. Description: `text-xs text-muted-foreground`.
+
+ Mode definitions (verbatim from PRD/RESEARCH):
+ ```
+ personal_ai: "Personal AI Assistant" / "Always available, persistent memory, private."
+ project_builder: "Project Builder" / "Brainstorm -> PM -> Engineer -> shipped product."
+ both: "Both (recommended)" / "A conversation becomes a project with one click."
+ ```
+
+ Props: `{ value: NexusMode; onChange: (mode: NexusMode) => void }`.
+ Import `cn` from `"@/lib/utils"` and `NexusMode` type from `"@/api/hardware"`.
+
+ **5. Create `ui/src/components/onboarding/HardwareSummaryStep.tsx`:**
+
+ Props: `{ hardwareInfo: HardwareInfo | undefined; isLoading: boolean; isError: boolean }`.
+
+ **Loading state** (isLoading = true): Render three `Skeleton` rows (`h-4 w-full rounded`). Import Skeleton from `"@/components/ui/skeleton"`.
+
+ **Error state** (isError = true): Render `Could not detect hardware. You can still continue.
`. Note: NOT `text-destructive` — this is non-blocking per UI-SPEC.
+
+ **Success state** (hardwareInfo exists):
+
+ Render a vertical stack (`flex flex-col gap-4`):
+
+ a) Hardware stats rows (`flex flex-col gap-2`):
+ - If `hardwareInfo.hardwareTier === "apple_silicon"`:
+ - Row: label "Unified memory" (never "VRAM"), value `{hardwareInfo.totalGb} GB`
+ - Row: label "Available", value `{hardwareInfo.usableGb} GB`
+ - Row: label "CPU", value `{hardwareInfo.cpuModel}`
+ - If `hardwareInfo.hardwareTier === "gpu"`:
+ - Row: label "GPU", value `{hardwareInfo.gpuName}`
+ - Row: label "GPU VRAM", value `{hardwareInfo.gpuVramGb} GB`
+ - Row: label "System RAM", value `{hardwareInfo.totalGb} GB`
+ - If `hardwareInfo.hardwareTier === "cpu_only"`:
+ - Row: label "System RAM", value `{hardwareInfo.totalGb} GB`
+ - Row: label "CPU", value `{hardwareInfo.cpuModel}`
+ - Warning: `Slower than GPU-accelerated models -- cloud AI recommended
`
+
+ Each stat row: `{label}{value}
`
+
+ b) Privacy frame (shown when `hardwareTier !== "cpu_only"`):
+ ```tsx
+
+ Local AI (recommended for privacy)
+
+ Runs entirely on your machine.{"\n"}
+ No accounts. No tracking. Works offline.
+
+
+ ```
+
+ Import `cn` from `"@/lib/utils"`, `Skeleton` from `"@/components/ui/skeleton"`, `HardwareInfo` from `"@/api/hardware"`.
+