feat(ui): display livePartial / transcript_delta in ChatBubble as 'Hearing:' indicator

- REST STT now sends delta (full text) so UI lights up immediately with what was heard.
- Works as 'live' for the final transcript (true partials would stream words if Realtime was available).
- Per PLAN item 3.
This commit is contained in:
2026-06-04 15:34:37 -04:00
parent 77cbd91b93
commit 771c00830a
3 changed files with 20 additions and 3 deletions
+2 -1
View File
@@ -28,6 +28,7 @@ export default function App() {
sendText,
startRecording,
stopRecording,
livePartial,
} = useConversation();
const [currentSceneId, setCurrentSceneId] = useState('cozy-room');
@@ -143,7 +144,7 @@ export default function App() {
{/* Column 3: Chat + Text Input */}
<div className="space-y-4">
<ChatBubble messages={messages} isKiraSpeaking={isKiraSpeaking} userName={userName} />
<ChatBubble messages={messages} isKiraSpeaking={isKiraSpeaking} userName={userName} livePartial={livePartial} />
{/* Text input fallback */}
<div className="glass-card p-3">
+11 -1
View File
@@ -11,9 +11,10 @@ interface Props {
messages: Message[];
isKiraSpeaking: boolean;
userName?: string;
livePartial?: string;
}
export default function ChatBubble({ messages, isKiraSpeaking }: Props) {
export default function ChatBubble({ messages, isKiraSpeaking, livePartial }: Props) {
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
@@ -27,6 +28,15 @@ export default function ChatBubble({ messages, isKiraSpeaking }: Props) {
<span className={`w-2 h-2 rounded-full ${isKiraSpeaking ? 'bg-kira-pink animate-pulse' : 'bg-kira-mint'}`} />
</h3>
{livePartial && (
<div className="mb-2 px-3 py-1.5 bg-kira-lav/20 text-kira-plum/70 text-xs rounded-xl flex items-center gap-2">
<span>👂</span>
<span className="font-medium">Hearing:</span>
<span className="truncate">{livePartial}</span>
<span className="animate-pulse">...</span>
</div>
)}
<div className="flex-1 overflow-y-auto space-y-2 scrollbar-thin pr-1">
{messages.length === 0 && (
<div className="text-xs text-kira-plum/30 text-center py-6">
+7 -1
View File
@@ -39,6 +39,7 @@ export function useConversation() {
});
const [loadingPrefs, setLoadingPrefs] = useState(true);
const [micError, setMicError] = useState<string | null>(null);
const [livePartial, setLivePartial] = useState<string>('');
const wsRef = useRef<WebSocket | null>(null);
const audioRef = useRef<HTMLAudioElement | null>(null);
@@ -109,7 +110,11 @@ export function useConversation() {
break;
case 'transcript_delta':
// Streaming partial transcript — could show as typing indicator
if (msg.text) {
setLivePartial(msg.text);
// Clear after short delay so it doesn't stick (for REST full-text case)
setTimeout(() => setLivePartial(''), 1500);
}
break;
case 'speaking_start':
@@ -282,6 +287,7 @@ export function useConversation() {
preferences,
loadingPrefs,
micError,
livePartial,
identify,
setPreference,
sendText,