--- phase: 42-wallpapers-social-format-conversion-voice plan: 04 subsystem: ui tags: [voice, whisper, offline-badge, react-hook, lucide-react, typescript] # Dependency graph requires: - phase: 42-wallpapers-social-format-conversion-voice provides: Plan 01 foundation types and server hardware route at /api/system/providers affects: - 42-06 (UI wiring — voice offline badge now visible in ChatInput) provides: - useSystemProviders hook fetching /api/system/providers and exposing whisperAvailable/piperAvailable - Offline badge in ChatInput (WifiOff icon + "Offline" label) when local Whisper binary detected # Tech tracking tech-stack: added: [] patterns: - Simple one-shot fetch hook (no SWR/React Query) for UI capability flags - Graceful degradation: badge absent when providers null (fetch error or Whisper not installed) key-files: created: - ui/src/hooks/useSystemProviders.ts modified: - ui/src/components/ChatInput.tsx key-decisions: - "useSystemProviders uses plain useState+useEffect fetch (not React Query) — matches plan spec and keeps hook lightweight for a one-time capability probe" - "Hook maps voiceCapability from HardwareInfo response shape — /api/system/providers returns full HardwareInfo, whisperAvailable lives in voiceCapability sub-object" patterns-established: - "Offline badge pattern: enableVoiceInput && providers?.whisperAvailable — double guard ensures badge only shows in voice mode with confirmed local model" requirements-completed: [VOICE-01, VOICE-02, VOICE-03] # Metrics duration: 2min completed: 2026-04-04 --- # Phase 42 Plan 04: Voice Offline Badge and useSystemProviders Hook **useSystemProviders hook wires /api/system/providers to ChatInput offline badge, surfacing local Whisper availability via WifiOff icon when voice mode is active** ## Performance - **Duration:** 2 min - **Started:** 2026-04-04T22:12:11Z - **Completed:** 2026-04-04T22:14:00Z - **Tasks:** 2 - **Files modified:** 2 (1 created, 1 modified) ## Accomplishments - Created useSystemProviders hook that fetches /api/system/providers once on mount, extracts voiceCapability.whisperAvailable, and returns null providers on error for graceful degradation - Added WifiOff icon offline badge to ChatInput — renders only when enableVoiceInput=true AND providers.whisperAvailable=true - Verified VoiceMicButton and VoiceModeToggle already rendering correctly from prior phase work (no changes needed) ## Task Commits Each task was committed atomically: 1. **Task 1: Create useSystemProviders hook** - `768f62fa` (feat) 2. **Task 2: Add offline badge to ChatInput** - `69ae0a00` (feat) ## Files Created/Modified - `ui/src/hooks/useSystemProviders.ts` - One-shot fetch hook for system providers; exports SystemProviders interface and useSystemProviders function - `ui/src/components/ChatInput.tsx` - Added WifiOff import, useSystemProviders call, and offline badge JSX next to VoiceMicButton ## Decisions Made - Used plain useState+useEffect pattern (not React Query / useHardwareInfo) as the plan specified — the hook is intentionally lightweight for a one-time capability probe - Mapped voiceCapability sub-object from HardwareInfo since the /api/system/providers endpoint returns the full hardware info shape; whisperAvailable lives at `data.voiceCapability.whisperAvailable` ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Offline badge available in ChatInput; voice UI feature surface is now complete - Plan 06 (UI wiring) can wire enableVoiceInput prop from user settings/mode state - Voice mode toggle and mic button confirmed rendering correctly when enableVoiceInput=true --- *Phase: 42-wallpapers-social-format-conversion-voice* *Completed: 2026-04-04*