Files
triqura-ecd/lib/cortex/use-cortex-voice.ts
colinislit 2170b23348 refactor: rename swift → cortex in code and documentation
Complete rename of "swift" terminology to "cortex" across the codebase:

Code Changes:
- Rename directories: lib/swift → lib/cortex, components/swift → components/cortex
- Rename API routes: /api/swift/* → /api/cortex/*
- Rename page routes: /epd/swift → /epd/cortex
- Rename store: swift-store.ts → cortex-store.ts

Type Renames:
- SwiftIntent → CortexIntent
- SwiftStore → CortexStore
- useSwiftStore → useCortexStore
- SwiftContext → CortexContext
- useSwiftVoice → useCortexVoice

Documentation Updates (docs/intent/):
- Safety Net → Nudge (Layer 3 rename)
- SafetySuggestion → NudgeSuggestion
- evaluateSafetyNet → evaluateNudge
- SWIFT_* feature flags → CORTEX_*
- All path references updated to match new structure

Files affected: 47 files, ~700 lines changed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 09:18:06 +01:00

116 lines
3.1 KiB
TypeScript

'use client';
/**
* Cortex Voice Hook
*
* Wraps useDeepgramStreaming for Cortex-specific voice input behavior.
* Streams transcript directly to the command input.
*/
import { useCallback, useEffect, useRef } from 'react';
import { useCortexStore } from '@/stores/cortex-store';
import {
useDeepgramStreaming,
type TranscriptResult,
} from '@/hooks/use-deepgram-streaming';
export interface UseCortexVoiceReturn {
isRecording: boolean;
isConnecting: boolean;
isConnected: boolean;
error: string | null;
startRecording: () => Promise<void>;
stopRecording: () => void;
analyserNode: AnalyserNode | null;
isBrowserSupported: boolean;
}
export function useCortexVoice(): UseCortexVoiceReturn {
const { setInputValue, setVoiceActive, inputValue } = useCortexStore();
// Track the base text (what was in input before recording started)
const baseTextRef = useRef('');
// Track interim transcript for replacement
const lastInterimRef = useRef('');
const handleTranscript = useCallback(
(result: TranscriptResult) => {
const { transcript, isFinal } = result;
if (isFinal) {
// Final transcript: append to base text and update base
const newText = baseTextRef.current
? `${baseTextRef.current} ${transcript}`
: transcript;
baseTextRef.current = newText;
lastInterimRef.current = '';
setInputValue(newText);
} else {
// Interim transcript: show as preview (replace previous interim)
const previewText = baseTextRef.current
? `${baseTextRef.current} ${transcript}`
: transcript;
setInputValue(previewText);
lastInterimRef.current = transcript;
}
},
[setInputValue]
);
const handleError = useCallback(
(error: Error) => {
console.error('[CortexVoice] Error:', error.message);
},
[]
);
const {
status,
isRecording,
startRecording: startDeepgram,
stopRecording: stopDeepgram,
analyserNode,
error,
isBrowserSupported,
} = useDeepgramStreaming({
onTranscript: handleTranscript,
onError: handleError,
language: 'nl',
model: 'nova-2',
endpointingMs: 2000, // Shorter for command-style input
});
const startRecording = useCallback(async () => {
// Store current input as base text
baseTextRef.current = inputValue;
lastInterimRef.current = '';
setVoiceActive(true);
await startDeepgram();
}, [inputValue, setVoiceActive, startDeepgram]);
const stopRecording = useCallback(() => {
stopDeepgram();
setVoiceActive(false);
// Keep whatever text is in the input
lastInterimRef.current = '';
}, [stopDeepgram, setVoiceActive]);
// Sync voice active state with recording state
useEffect(() => {
if (!isRecording) {
setVoiceActive(false);
}
}, [isRecording, setVoiceActive]);
return {
isRecording,
isConnecting: status === 'connecting' || status === 'reconnecting',
isConnected: status === 'connected',
error,
startRecording,
stopRecording,
analyserNode,
isBrowserSupported,
};
}