import { useEffect, useState } from "react"; import { Mic, Volume2, CheckCircle2, Info } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import type { VoiceCapability } from "../../hooks/useHardwareInfo"; interface VoiceStepProps { onEnable: () => void; onSkip: () => void; voiceCapability?: VoiceCapability; } export function VoiceStep({ onEnable, onSkip, voiceCapability }: VoiceStepProps) { const [micAvailable, setMicAvailable] = useState(null); useEffect(() => { navigator.mediaDevices?.enumerateDevices() .then(devices => setMicAvailable(devices.some(d => d.kind === "audioinput"))) .catch(() => setMicAvailable(false)); }, []); // Determine STT status label function whisperStatusLabel(): string { if (!voiceCapability) { if (micAvailable === false) return "No microphone detected -- unavailable"; if (micAvailable === true) return "Microphone detected -- speak to your assistant"; return "Checking microphone..."; } if (voiceCapability.whisperAvailable) return "Whisper detected -- speech recognition ready"; return "Whisper not found -- install whisper-cpp for voice input"; } function whisperBadge() { if (!voiceCapability) return null; if (voiceCapability.whisperAvailable) { return ( Available ); } return ( Install needed ); } // Determine TTS status label function piperStatusLabel(): string { if (!voiceCapability) { return "Hear responses read aloud. Runs entirely on your device -- no server needed."; } if (voiceCapability.piperAvailable) return "Piper detected -- text-to-speech ready"; return "Piper not found -- install piper for voice output"; } function piperBadge() { if (!voiceCapability) return null; if (voiceCapability.piperAvailable) { return ( Available ); } return ( Install needed ); } // Insufficient hardware: show note but still allow enabling if (voiceCapability && !voiceCapability.voiceTierSufficient) { return (

Limited hardware for voice

Voice features require at least 4GB free RAM. Transcription may be slower on your system.

); } // Sufficient hardware or unknown -- show full UI const neitherBinaryFound = voiceCapability && !voiceCapability.whisperAvailable && !voiceCapability.piperAvailable; // CPU-only note const isCpuOnly = 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.

)} {/* CPU-only note */} {voiceCapability?.voiceTierSufficient && !voiceCapability.whisperAvailable && (

Works on CPU -- transcription may take a few extra seconds.

)}

Speech-to-Text (Whisper)

{whisperStatusLabel()}

{whisperBadge()}

Text-to-Speech (Piper)

{piperStatusLabel()}

{piperBadge()}
); }