Wire existing voice command system into the new layout components...
Integrate the existing voice command system (40+ commands, fuzzy matching) into the new layout. The voice system is FULLY BUILT -- this skill only wires it into new UI touch points.
voiceNavigation.ts command definitionssrc/lib/voice/voiceNavigation.ts -- Command definitions and routing (DO NOT MODIFY)src/lib/voice/speechRecognition.ts -- Web Speech API wrapper (if exists)src/hooks/useVoiceInput.ts -- Existing voice hook (if exists)src/components/voice/ -- Any existing voice componentssrc/components/dashboard/VoiceInputArea.tsx -- From skill-guided-homesrc/components/shared/BottomNav.tsx -- Mic button integration pointUser speaks → Web Speech API → transcript text
→ voiceNavigation.matchCommand(transcript)
→ if matched: router.push(matchedRoute)
→ if not matched: send to AI chat
Create src/components/voice/VoiceModal.tsx:
Purpose: Full-screen voice input overlay (triggered from BottomNav or VoiceInputArea)
States:
1. IDLE: Large mic button, "Tap to speak" text
2. LISTENING: Pulsing mic animation, live transcript text, "Listening..." label
3. PROCESSING: Spinner, "Understanding..." label, final transcript shown
4. MATCHED: Green checkmark, matched command shown, auto-navigating in 1s
5. UNMATCHED: Shows transcript, "I'll help with that" → routes to AI chat
6. ERROR: Red mic, "Couldn't hear you. Tap to try again"
Display (full-screen overlay):
+------------------------------------------+
| [X close] |
| |
| [ Large Mic Button ] |
| (64px circle) |
| |
| "Log morning medications" | <- Live transcript
| |
| Suggestions: |
| "Log medication" | "Add note" | <- Voice hints
| "What's due" | "Show schedule" |
| |
+------------------------------------------+
Behavior:
- Opens with fade-in (150ms)
- Mic auto-starts listening on open (if permission granted)
- Shows 4-6 example phrases as hints
- Closes automatically after successful command match (1s delay)
- Close on X or swipe down
In src/components/dashboard/VoiceInputArea.tsx:
// Import existing voice hooks
import { useVoiceInput } from '@/hooks/useVoiceInput'; // or equivalent
export function VoiceInputArea() {
const [voiceModalOpen, setVoiceModalOpen] = useState(false);
const [textInput, setTextInput] = useState('');
// Mic icon tap = open voice modal
const handleMicTap = () => setVoiceModalOpen(true);
// Text input submit = process as command
const handleTextSubmit = (text: string) => {
const matched = voiceNavigation.matchCommand(text);
if (matched) {
router.push(matched.route);
} else {
// Route to AI chat with pre-filled text
router.push(`/dashboard/chat?q=${encodeURIComponent(text)}`);
}
};
return (
<>
<div className="flex items-center gap-2 px-4 py-3 bg-muted/50 rounded-xl">
<button onClick={handleMicTap}>
<Mic className="w-5 h-5 text-muted-foreground" />
</button>
<input
placeholder="What would you like to do?"
value={textInput}
onChange={(e) => setTextInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleTextSubmit(textInput)}
className="flex-1 bg-transparent text-sm"
/>
</div>
<VoiceModal open={voiceModalOpen} onClose={() => setVoiceModalOpen(false)} />
</>
);
}
In src/components/shared/BottomNav.tsx:
// Center button (Voice) opens the VoiceModal
<button
onClick={() => setVoiceModalOpen(true)}
className="flex flex-col items-center justify-center"
>
<div className="w-12 h-12 rounded-full bg-primary flex items-center justify-center -mt-4">
<Mic className="w-6 h-6 text-primary-foreground" />
</div>
<span className="text-xs mt-1">Voice</span>
</button>
When voice is used from the home screen context:
// Special voice commands when PriorityCard is visible:
const priorityVoiceCommands = {
"mark as given": () => handleComplete(currentTask.id),
"done": () => handleComplete(currentTask.id),
"skip": () => handleSkip(currentTask.id),
"skip this one": () => handleSkip(currentTask.id),
"next": () => handleSkip(currentTask.id),
"log it": () => handleComplete(currentTask.id),
"taken": () => handleComplete(currentTask.id),
};
// Register these as priority commands when PriorityCard is mounted
When user taps a suggestion chip with text like "Log breakfast":
// Pre-fill the voice/text input area with the chip text
// Then auto-execute the command
const handleChipTap = (chipText: string) => {
const matched = voiceNavigation.matchCommand(chipText);
if (matched) {
router.push(matched.route);
}
};
// First-time voice use:
1. Show permission prompt: "MyGuide needs microphone access for voice commands"
2. If granted: proceed to listen
3. If denied: show text-only input, hide mic button
4. Store permission state in localStorage
// Check on component mount:
const hasMicPermission = await navigator.permissions.query({ name: 'microphone' });
npm test -- --testPathPattern=voice