From 8f97e69184b0f48a8e72834237342081bd8a975b Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Sat, 4 Apr 2026 03:33:10 +0000 Subject: [PATCH] feat(39-02): VoiceStep hardware-aware UI with conditional enable/skip - Add VoiceCapability interface to ui/src/api/hardware.ts - Export VoiceCapability type from useHardwareInfo.ts - VoiceStep accepts voiceCapability prop, renders conditionally - Insufficient hardware: shows capability note with skip-only button - Binaries present: shows green checkmarks next to STT/TTS labels - Missing binaries on sufficient hardware: shows install note, dimmed Enable - NexusOnboardingWizard passes voiceCapability from hardware probe to VoiceStep --- ui/src/api/hardware.ts | 7 ++ ui/src/components/NexusOnboardingWizard.tsx | 1 + ui/src/components/onboarding/VoiceStep.tsx | 102 +++++++++++++++++--- ui/src/hooks/useHardwareInfo.ts | 4 +- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/ui/src/api/hardware.ts b/ui/src/api/hardware.ts index 4b1fab09..9049b801 100644 --- a/ui/src/api/hardware.ts +++ b/ui/src/api/hardware.ts @@ -3,6 +3,12 @@ import { api } from "./client"; export type HardwareTier = "gpu" | "apple_silicon" | "cpu_only"; +export interface VoiceCapability { + whisperAvailable: boolean; + piperAvailable: boolean; + voiceTierSufficient: boolean; +} + export interface HardwareInfo { totalGb: number; freeGb: number; @@ -13,6 +19,7 @@ export interface HardwareInfo { unifiedMemory: boolean; hardwareTier: HardwareTier; cpuModel: string | null; + voiceCapability?: VoiceCapability; } export type NexusMode = "personal_ai" | "project_builder" | "both"; diff --git a/ui/src/components/NexusOnboardingWizard.tsx b/ui/src/components/NexusOnboardingWizard.tsx index 5e61199a..8738514f 100644 --- a/ui/src/components/NexusOnboardingWizard.tsx +++ b/ui/src/components/NexusOnboardingWizard.tsx @@ -430,6 +430,7 @@ export function OnboardingWizard() { setStep(5); }} onSkip={() => setStep(5)} + voiceCapability={hardwareInfo?.voiceCapability} /> + + + ); + } + + // Sufficient hardware or unknown — show full UI + const neitherBinaryFound = + voiceCapability && + !voiceCapability.whisperAvailable && + !voiceCapability.piperAvailable; + return (
+ {/* Install note when tier is sufficient but binaries are missing */} + {neitherBinaryFound && ( +
+ +

+ Install whisper-cpp and piper for local voice features. You can enable voice now and configure binaries later. +

+
+ )} +
-
+

Speech-to-Text (Whisper)

- {micAvailable === false - ? "No microphone detected — unavailable" - : micAvailable === true - ? "Microphone detected — speak to your assistant" - : "Checking microphone..."} + {whisperStatusLabel()}

+ {whisperStatusIcon()}
-
+

Text-to-Speech (Piper)

- Hear responses read aloud. Runs entirely on your device — no server needed. + {piperStatusLabel()}

+ {piperStatusIcon()}
-