import { useState, useCallback, useEffect, useRef } from 'react';
import { useVoice } from '../../hooks/useVoice';
import { useArthur } from '../../hooks/useArthur';
import { OrbCore } from './OrbCore';
import { StatusPanel } from './StatusPanel';
import { Conversation } from './Conversation';
import './arthur.css';
interface ArthurPageProps {
onNavigateToOracle: () => void;
}
export function ArthurPage({ onNavigateToOracle }: ArthurPageProps) {
const [textInput, setTextInput] = useState('');
const inputRef = useRef<HTMLInputElement>(null);
const voice = useVoice();
const arthur = useArthur();
// Get status text based on state
const getStatusText = () => {
if (arthur.isThinking) return 'Thinking...';
if (voice.state === 'listening') return 'Listening...';
if (voice.state === 'speaking') return 'Speaking...';
if (voice.state === 'error') return 'Voice Error';
return 'Awaiting Input';
};
// Handle voice recognition result
const handleVoiceResult = useCallback(async (transcript: string) => {
const response = await arthur.sendMessage(transcript);
if (response !== undefined && voice.isSupported) {
voice.speak(response);
}
}, [arthur, voice]);
// Set up voice recognition callback
useEffect(() => {
// This is a simplified approach - in production, use context
const originalOnResult = (window as unknown as Record<string, unknown>).__arthurVoiceCallback;
(window as unknown as Record<string, unknown>).__arthurVoiceCallback = handleVoiceResult;
return () => {
(window as unknown as Record<string, unknown>).__arthurVoiceCallback = originalOnResult;
};
}, [handleVoiceResult]);
// Handle text input submit
const handleSendText = useCallback(async () => {
if (!textInput.trim()) return;
const text = textInput;
setTextInput('');
const response = await arthur.sendMessage(text);
if (response !== undefined && voice.isSupported) {
voice.speak(response);
}
}, [textInput, arthur, voice]);
// Handle Enter key
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
handleSendText();
}
};
// Handle Space key for voice toggle (when not in input)
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.code === 'Space' && document.activeElement !== inputRef.current) {
e.preventDefault();
if (voice.state === 'listening') {
voice.stopListening();
} else if (voice.state === 'idle') {
voice.startListening();
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [voice]);
// Toggle voice listening
const toggleVoice = () => {
if (voice.state === 'listening') {
voice.stopListening();
} else if (voice.state === 'speaking') {
voice.stopSpeaking();
} else {
voice.startListening();
}
};
return (
<div className="arthur-page">
<div className="grid-bg" />
<div className="arthur-title">
ARTHUR <span>AI ASSISTANT</span>
</div>
<button className="arthur-nav-link" onClick={onNavigateToOracle}>
ORACLE KNOWLEDGE
</button>
<StatusPanel
voiceState={voice.state}
connected={arthur.status.connected}
ragActive={arthur.status.ragActive}
docCount={arthur.status.docCount}
/>
<div className="arthur-container">
<OrbCore state={voice.state} />
<div className="arthur-status">{getStatusText()}</div>
<Conversation messages={arthur.messages} isThinking={arthur.isThinking} />
<div className="arthur-input-row">
<input
ref={inputRef}
type="text"
className="arthur-text-input"
placeholder="Type your message..."
value={textInput}
onChange={(e) => setTextInput(e.target.value)}
onKeyPress={handleKeyPress}
/>
<button className="arthur-send-btn" onClick={handleSendText}>
Send
</button>
{voice.state === 'speaking' && (
<button className="arthur-stop-btn" onClick={voice.stopSpeaking}>
Stop
</button>
)}
</div>
<button
className={`voice-btn ${voice.state === 'listening' ? 'listening' : ''}`}
onClick={toggleVoice}
>
🎤
</button>
<div className="arthur-hint">
{voice.isSupported
? 'Click mic to speak, press Space, or type below'
: 'Voice not supported - type your message below'}
</div>
</div>
</div>
);
}