Skip to content

Commit af6a178

Browse files
committed
fix: preserve realtime voice across immersive toggle
1 parent 5195d54 commit af6a178

1 file changed

Lines changed: 46 additions & 92 deletions

File tree

src/app/chat/page.tsx

Lines changed: 46 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -5867,86 +5867,6 @@ export default function ChatPage() {
58675867
)}
58685868
</div>
58695869

5870-
{/* Voice surface: inline by default, immersive only when expanded. */}
5871-
{voiceMode === "agent" && !activeThread && agentOverlayMode === "immersive" && (
5872-
<div
5873-
className="fixed inset-0 z-[90] overflow-hidden"
5874-
style={{
5875-
color: "var(--text-primary)",
5876-
background: "linear-gradient(180deg, color-mix(in srgb, var(--bg-primary) 97%, transparent), color-mix(in srgb, var(--bg-primary) 93%, var(--bg-secondary) 7%))",
5877-
}}
5878-
>
5879-
<div
5880-
className="absolute inset-0 opacity-60"
5881-
style={{
5882-
backgroundImage: [
5883-
"linear-gradient(var(--voice-overlay-grid) 1px, transparent 1px)",
5884-
"linear-gradient(90deg, var(--voice-overlay-grid-soft) 1px, transparent 1px)",
5885-
].join(", "),
5886-
backgroundSize: "44px 44px",
5887-
}}
5888-
/>
5889-
<div className="relative flex h-full flex-col justify-center">
5890-
<div className="absolute right-4 top-[max(var(--mobile-safe-top),1rem)] z-10 flex gap-2 sm:right-6">
5891-
<button
5892-
onClick={() => setVoicePickerOpen(true)}
5893-
title="Choose voice"
5894-
aria-label="Choose voice"
5895-
className={`flex h-10 w-10 items-center justify-center rounded-full border shadow-[var(--theme-shadow)] transition ${sessionVoiceOverride ? "border-[#00f0ff]/45 bg-[#00f0ff]/15 text-[#00f0ff]" : "border-[var(--border-medium)] bg-[var(--bg-surface)]/85 text-[var(--text-secondary)] hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"}`}
5896-
>
5897-
<VoicePersonIcon />
5898-
</button>
5899-
<button
5900-
onClick={() => setAgentOverlayMode("transcript")}
5901-
title="Return to chat"
5902-
aria-label="Return to chat"
5903-
className="flex h-10 w-10 items-center justify-center rounded-full border border-[var(--border-medium)] bg-[var(--bg-surface)]/85 text-[var(--text-secondary)] shadow-[var(--theme-shadow)] transition hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"
5904-
>
5905-
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.75}>
5906-
<path strokeLinecap="round" strokeLinejoin="round" d="M9 4.5H5.25A.75.75 0 0 0 4.5 5.25V9m10.5-4.5h3.75a.75.75 0 0 1 .75.75V9M9 19.5H5.25a.75.75 0 0 1-.75-.75V15m10.5 4.5h3.75a.75.75 0 0 0 .75-.75V15M8.25 8.25l-3.75-3.75m15 0-3.75 3.75m-7.5 7.5-3.75 3.75m15 0-3.75-3.75" />
5907-
</svg>
5908-
</button>
5909-
</div>
5910-
5911-
<div
5912-
className="flex flex-1 items-center px-4 pb-3 sm:px-6"
5913-
>
5914-
<div className="mx-auto w-full max-w-none px-0 py-0">
5915-
<div
5916-
className="flex flex-col items-center gap-2"
5917-
>
5918-
<VoiceAgent
5919-
onTranscript={(text) => sendMessage(text, { forceVoiceResponse: true })}
5920-
onRealtimeTranscript={(event) => persistRealtimeTranscript(event, {
5921-
sessionKey: activeVoiceSessionKey,
5922-
storeKey: activeVoiceStoreKey,
5923-
setVisibleMessages: setMessages,
5924-
})}
5925-
isPlayingAudio={isPlayingAudio}
5926-
onInterrupt={interruptAudio}
5927-
isLoading={isLoading}
5928-
accentColor={agentColor}
5929-
autoActivate
5930-
immersive
5931-
isMicMuted={agentMicMuted}
5932-
isAgentMuted={agentAudioMuted}
5933-
onMicMutedChange={setAgentMicMuted}
5934-
onAgentMutedChange={handleAgentAudioMutedChange}
5935-
agent={selectedAgent?.callsign}
5936-
gatewayAgent={delegatedViaAgent?.callsign ?? selectedAgent?.callsign}
5937-
companyId={company?.id}
5938-
sessionKey={selectedSessionBelongsToAgent(selectedSessionKey, selectedAgent?.callsign)
5939-
? selectedSessionKey ?? gatewaySessionKeyForAgent(selectedAgent)
5940-
: gatewaySessionKeyForAgent(selectedAgent)}
5941-
realtimeRuntimeId={selectedAgent?.runtimeId ?? undefined}
5942-
/>
5943-
</div>
5944-
</div>
5945-
</div>
5946-
</div>
5947-
</div>
5948-
)}
5949-
59505870
<VoiceSelectModal
59515871
open={voicePickerOpen}
59525872
title="Voice"
@@ -5993,8 +5913,30 @@ export default function ChatPage() {
59935913
setIsDragOver(false);
59945914
if (event.dataTransfer.files.length) addFiles(event.dataTransfer.files);
59955915
}}
5996-
agentPanel={voiceMode === "agent" && !activeThread && agentOverlayMode === "transcript" ? (
5997-
<div className="relative max-h-[min(10.75rem,28dvh)] overflow-hidden rounded-[24px] border border-[var(--voice-shell-border)] bg-[var(--bg-surface)]/88 px-3 py-2 shadow-[var(--theme-shadow)] backdrop-blur-xl sm:max-h-none">
5916+
agentPanel={voiceMode === "agent" && !activeThread ? (
5917+
<div
5918+
className={agentOverlayMode === "immersive"
5919+
? "fixed inset-0 z-[90] flex items-center justify-center overflow-hidden px-4 pb-3 sm:px-6"
5920+
: "relative max-h-[min(10.75rem,28dvh)] overflow-hidden rounded-[24px] border border-[var(--voice-shell-border)] bg-[var(--bg-surface)]/88 px-3 py-2 shadow-[var(--theme-shadow)] backdrop-blur-xl sm:max-h-none"}
5921+
style={agentOverlayMode === "immersive"
5922+
? {
5923+
color: "var(--text-primary)",
5924+
background: "linear-gradient(180deg, color-mix(in srgb, var(--bg-primary) 97%, transparent), color-mix(in srgb, var(--bg-primary) 93%, var(--bg-secondary) 7%))",
5925+
}
5926+
: undefined}
5927+
>
5928+
{agentOverlayMode === "immersive" && (
5929+
<div
5930+
className="absolute inset-0 opacity-60"
5931+
style={{
5932+
backgroundImage: [
5933+
"linear-gradient(var(--voice-overlay-grid) 1px, transparent 1px)",
5934+
"linear-gradient(90deg, var(--voice-overlay-grid-soft) 1px, transparent 1px)",
5935+
].join(", "),
5936+
backgroundSize: "44px 44px",
5937+
}}
5938+
/>
5939+
)}
59985940
<VoiceAgent
59995941
onTranscript={(text) => sendMessage(text, { forceVoiceResponse: true })}
60005942
onRealtimeTranscript={(event) => persistRealtimeTranscript(event, {
@@ -6007,7 +5949,8 @@ export default function ChatPage() {
60075949
isLoading={isLoading}
60085950
accentColor={agentColor}
60095951
autoActivate
6010-
compact
5952+
compact={agentOverlayMode !== "immersive"}
5953+
immersive={agentOverlayMode === "immersive"}
60115954
isMicMuted={agentMicMuted}
60125955
isAgentMuted={agentAudioMuted}
60135956
onMicMutedChange={setAgentMicMuted}
@@ -6020,23 +5963,34 @@ export default function ChatPage() {
60205963
: gatewaySessionKeyForAgent(selectedAgent)}
60215964
realtimeRuntimeId={selectedAgent?.runtimeId ?? undefined}
60225965
/>
6023-
<div className="absolute right-2 top-2 flex items-center gap-1">
5966+
<div className={agentOverlayMode === "immersive"
5967+
? "absolute right-4 top-[max(var(--mobile-safe-top),1rem)] z-10 flex gap-2 sm:right-6"
5968+
: "absolute right-2 top-2 flex items-center gap-1"}
5969+
>
60245970
<button
60255971
onClick={() => setVoicePickerOpen(true)}
60265972
title="Choose voice"
60275973
aria-label="Choose voice"
6028-
className={`flex h-8 w-8 items-center justify-center rounded-full border transition ${sessionVoiceOverride ? "border-[#00f0ff]/45 bg-[#00f0ff]/15 text-[#00f0ff]" : "border-[var(--border-medium)] bg-[var(--bg-primary)]/70 text-[var(--text-secondary)] hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"}`}
5974+
className={agentOverlayMode === "immersive"
5975+
? `flex h-10 w-10 items-center justify-center rounded-full border shadow-[var(--theme-shadow)] transition ${sessionVoiceOverride ? "border-[#00f0ff]/45 bg-[#00f0ff]/15 text-[#00f0ff]" : "border-[var(--border-medium)] bg-[var(--bg-surface)]/85 text-[var(--text-secondary)] hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"}`
5976+
: `flex h-8 w-8 items-center justify-center rounded-full border transition ${sessionVoiceOverride ? "border-[#00f0ff]/45 bg-[#00f0ff]/15 text-[#00f0ff]" : "border-[var(--border-medium)] bg-[var(--bg-primary)]/70 text-[var(--text-secondary)] hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"}`}
60295977
>
6030-
<VoicePersonIcon className="h-3.5 w-3.5" />
5978+
<VoicePersonIcon className={agentOverlayMode === "immersive" ? undefined : "h-3.5 w-3.5"} />
60315979
</button>
60325980
<button
6033-
onClick={() => setAgentOverlayMode("immersive")}
6034-
title="Enter fullscreen visual mode"
6035-
aria-label="Enter fullscreen visual mode"
6036-
className="flex h-8 w-8 items-center justify-center rounded-full border border-[var(--border-medium)] bg-[var(--bg-primary)]/70 text-[var(--text-secondary)] transition hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"
5981+
onClick={() => setAgentOverlayMode(agentOverlayMode === "immersive" ? "transcript" : "immersive")}
5982+
title={agentOverlayMode === "immersive" ? "Return to chat" : "Enter fullscreen visual mode"}
5983+
aria-label={agentOverlayMode === "immersive" ? "Return to chat" : "Enter fullscreen visual mode"}
5984+
className={agentOverlayMode === "immersive"
5985+
? "flex h-10 w-10 items-center justify-center rounded-full border border-[var(--border-medium)] bg-[var(--bg-surface)]/85 text-[var(--text-secondary)] shadow-[var(--theme-shadow)] transition hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"
5986+
: "flex h-8 w-8 items-center justify-center rounded-full border border-[var(--border-medium)] bg-[var(--bg-primary)]/70 text-[var(--text-secondary)] transition hover:bg-[var(--bg-surface-hover)] hover:text-[var(--text-primary)]"}
60375987
>
6038-
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.75}>
6039-
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 9V5.25A1.5 1.5 0 0 1 5.25 3.75H9m6 0h3.75A1.5 1.5 0 0 1 20.25 5.25V9m0 6v3.75a1.5 1.5 0 0 1-1.5 1.5H15m-6 0H5.25a1.5 1.5 0 0 1-1.5-1.5V15" />
5988+
<svg className={agentOverlayMode === "immersive" ? "h-4 w-4" : "h-3.5 w-3.5"} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.75}>
5989+
{agentOverlayMode === "immersive" ? (
5990+
<path strokeLinecap="round" strokeLinejoin="round" d="M9 4.5H5.25A.75.75 0 0 0 4.5 5.25V9m10.5-4.5h3.75a.75.75 0 0 1 .75.75V9M9 19.5H5.25a.75.75 0 0 1-.75-.75V15m10.5 4.5h3.75a.75.75 0 0 0 .75-.75V15M8.25 8.25l-3.75-3.75m15 0-3.75 3.75m-7.5 7.5-3.75 3.75m15 0-3.75-3.75" />
5991+
) : (
5992+
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 9V5.25A1.5 1.5 0 0 1 5.25 3.75H9m6 0h3.75A1.5 1.5 0 0 1 20.25 5.25V9m0 6v3.75a1.5 1.5 0 0 1-1.5 1.5H15m-6 0H5.25a1.5 1.5 0 0 1-1.5-1.5V15" />
5993+
)}
60405994
</svg>
60415995
</button>
60425996
</div>

0 commit comments

Comments
 (0)