({
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"`.