diff --git a/src/components/playground/PhoneSimulator.tsx b/src/components/playground/PhoneSimulator.tsx
index a8ed4f0..cf81601 100644
--- a/src/components/playground/PhoneSimulator.tsx
+++ b/src/components/playground/PhoneSimulator.tsx
@@ -715,7 +715,7 @@ export function PhoneSimulator({
>
- Call Agent
+ 呼叫智能体
@@ -725,7 +725,7 @@ export function PhoneSimulator({
>
- {currentVoiceId === "BV001_streaming" ? "Female Voice" : "Male Voice"}
+ {currentVoiceId === "BV001_streaming" ? "女性声音" : "男性声音"}
{showVoiceMenu && (
@@ -745,7 +745,7 @@ export function PhoneSimulator({
: "text-white"
}`}
>
- Female Voice
+ 女性声音
{currentVoiceId === "BV001_streaming" && }
- Male Voice
+ 男性声音
{currentVoiceId === "BV002_streaming" && (
)}
@@ -1198,6 +1198,20 @@ export function PhoneSimulator({
)}
+
+ {/* Fallback: Show End Call Button when in push-to-talk mode but no agent/audio */}
+ {phoneMode === "normal" &&
+ isPushToTalkMode &&
+ !voiceAssistant.agent && (
+
+ )}
)
diff --git a/src/components/playground/PlaygroundTile.tsx b/src/components/playground/PlaygroundTile.tsx
index e21edcc..9b3b0d9 100644
--- a/src/components/playground/PlaygroundTile.tsx
+++ b/src/components/playground/PlaygroundTile.tsx
@@ -100,7 +100,18 @@ export const PlaygroundTabbedTile: React.FC = ({
padding: `${contentPadding * 4}px`,
}}
>
- {tabs[activeTab].content}
+ {tabs.map((tab, index) => (
+
+ {tab.content}
+
+ ))}
);
diff --git a/src/transcriptions/TranscriptionTile.tsx b/src/transcriptions/TranscriptionTile.tsx
index f2129d3..98f0288 100644
--- a/src/transcriptions/TranscriptionTile.tsx
+++ b/src/transcriptions/TranscriptionTile.tsx
@@ -11,7 +11,7 @@ import {
Track,
TranscriptionSegment,
} from "livekit-client";
-import { useEffect, useState } from "react";
+import { useEffect, useState, useRef } from "react";
export function TranscriptionTile({
agentAudioTrack,
@@ -30,39 +30,51 @@ export function TranscriptionTile({
participant: localParticipant.localParticipant,
});
- const [transcripts, setTranscripts] = useState>(
- new Map(),
- );
const [messages, setMessages] = useState([]);
const { chatMessages, send: sendChat } = useChat();
+ const transcriptMapRef = useRef>(new Map());
- // store transcripts
+ // Build messages from segments and chat - always rebuild from current state
useEffect(() => {
+ const transcriptMap = transcriptMapRef.current;
+
+ // Process agent segments - update existing or add new
if (agentAudioTrack) {
- agentMessages.segments.forEach((s) =>
- transcripts.set(
+ agentMessages.segments.forEach((s) => {
+ const existing = transcriptMap.get(s.id);
+ transcriptMap.set(
s.id,
segmentToChatMessage(
s,
- transcripts.get(s.id),
+ existing,
agentAudioTrack.participant,
),
- ),
- );
+ );
+ });
}
- localMessages.segments.forEach((s) =>
- transcripts.set(
+ // Process local segments - update existing or add new
+ localMessages.segments.forEach((s) => {
+ const existing = transcriptMap.get(s.id);
+ transcriptMap.set(
s.id,
segmentToChatMessage(
s,
- transcripts.get(s.id),
+ existing,
localParticipant.localParticipant,
),
- ),
- );
+ );
+ });
- const allMessages = Array.from(transcripts.values());
+ // Build all messages
+ const allMessages: ChatMessageType[] = [];
+
+ // Add all transcript messages
+ transcriptMap.forEach((msg) => {
+ allMessages.push(msg);
+ });
+
+ // Add chat messages
for (const msg of chatMessages) {
const isAgent = agentAudioTrack
? msg.from?.identity === agentAudioTrack.participant?.identity
@@ -79,6 +91,7 @@ export function TranscriptionTile({
name = "Unknown";
}
}
+
allMessages.push({
name,
message: msg.message,
@@ -86,10 +99,11 @@ export function TranscriptionTile({
isSelf: isSelf,
});
}
+
+ // Sort by timestamp
allMessages.sort((a, b) => a.timestamp - b.timestamp);
setMessages(allMessages);
}, [
- transcripts,
chatMessages,
localParticipant.localParticipant,
agentAudioTrack?.participant,