diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index 872559b..7569ea5 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -6045,30 +6045,10 @@ export default function ChatPage() { setIsDragOver(false); if (event.dataTransfer.files.length) addFiles(event.dataTransfer.files); }} - agentPanel={voiceMode === "agent" && !activeThread ? ( + agentPanel={voiceMode === "agent" && !activeThread && agentOverlayMode !== "immersive" ? (
- {agentOverlayMode === "immersive" && ( -
- )} sendMessage(text, { forceVoiceResponse: true })} onRealtimeTranscript={(event) => persistRealtimeTranscript(event, { @@ -6081,8 +6061,7 @@ export default function ChatPage() { isLoading={isLoading} accentColor={resolvedVisualAccentColor} autoActivate - compact={agentOverlayMode !== "immersive"} - immersive={agentOverlayMode === "immersive"} + compact isMicMuted={agentMicMuted} isAgentMuted={agentAudioMuted} onMicMutedChange={setAgentMicMuted} @@ -6097,36 +6076,25 @@ export default function ChatPage() { voiceSettings={resolvedVoiceSettings} visualSettings={resolvedVisualSettings} /> -
+
@@ -6135,6 +6103,75 @@ export default function ChatPage() { />
+ {voiceMode === "agent" && !activeThread && agentOverlayMode === "immersive" ? ( +
+
+ sendMessage(text, { forceVoiceResponse: true })} + onRealtimeTranscript={(event) => persistRealtimeTranscript(event, { + sessionKey: activeVoiceSessionKey, + storeKey: activeVoiceStoreKey, + setVisibleMessages: setMessages, + })} + isPlayingAudio={isPlayingAudio} + onInterrupt={interruptAudio} + isLoading={isLoading} + accentColor={resolvedVisualAccentColor} + autoActivate + immersive + isMicMuted={agentMicMuted} + isAgentMuted={agentAudioMuted} + onMicMutedChange={setAgentMicMuted} + onAgentMutedChange={handleAgentAudioMutedChange} + agent={selectedAgent?.callsign} + gatewayAgent={delegatedViaAgent?.callsign ?? selectedAgent?.callsign} + companyId={company?.id} + sessionKey={selectedSessionBelongsToAgent(selectedSessionKey, selectedAgent?.callsign) + ? selectedSessionKey ?? gatewaySessionKeyForAgent(selectedAgent) + : gatewaySessionKeyForAgent(selectedAgent)} + realtimeRuntimeId={selectedAgent?.runtimeId ?? undefined} + voiceSettings={resolvedVoiceSettings} + visualSettings={resolvedVisualSettings} + /> +
+ + +
+
+ ) : null}
diff --git a/src/app/globals.css b/src/app/globals.css index 1116e63..376268a 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -342,6 +342,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l .voice-agent-visual-layer { position: absolute; inset: 0; + z-index: 1; border-radius: 999px; overflow: hidden; } @@ -354,7 +355,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l .voice-visual-hologram, .voice-visual-command { position: absolute; - inset: 4%; + inset: 2%; border-radius: 999px; pointer-events: none; } @@ -364,9 +365,9 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l inset: 8%; border-radius: 999px; background: - radial-gradient(circle, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.08 + var(--voice-volume, 0) * 0.12)) 0%, transparent 58%), + radial-gradient(circle, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.14 + var(--voice-volume, 0) * 0.18)) 0%, transparent 58%), repeating-radial-gradient(circle, var(--voice-shell-grid-soft) 0 1px, transparent 1px 28px); - opacity: calc(0.28 + var(--voice-motion, 0) * 0.3); + opacity: calc(0.48 + var(--voice-motion, 0) * 0.34); filter: blur(0.2px); animation: voice-agent-breathe calc(5.8s / var(--voice-intensity, 1)) ease-in-out infinite; } @@ -380,7 +381,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l linear-gradient(24deg, transparent 18%, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.08 + var(--voice-motion, 0) * 0.18)) 18.5%, transparent 20% 46%, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.1 + var(--voice-volume, 0) * 0.16)) 47%, transparent 48.5%), linear-gradient(144deg, transparent 24%, var(--voice-shell-grid) 25%, transparent 26% 62%, rgba(var(--voice-accent-rgb, 99, 183, 170), 0.18) 63%, transparent 64%), linear-gradient(82deg, transparent 32%, var(--voice-shell-grid-soft) 33%, transparent 34% 72%, rgba(var(--voice-accent-rgb, 99, 183, 170), 0.12) 73%, transparent 74%); - opacity: calc(0.38 + var(--voice-motion, 0) * 0.34); + opacity: calc(0.58 + var(--voice-motion, 0) * 0.36); animation: voice-agent-drift calc(13s / var(--voice-intensity, 1)) ease-in-out infinite; } @@ -400,7 +401,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l margin-left: calc(var(--node-size) / -2); margin-top: calc(var(--node-size) / -2); border-radius: 999px; - background: rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.62 + var(--voice-motion, 0) * 0.32)); + background: rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.78 + var(--voice-motion, 0) * 0.2)); box-shadow: 0 0 calc(8px + var(--voice-volume, 0) * 18px * var(--voice-intensity, 1)) rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.34 + var(--voice-motion, 0) * 0.34)), 0 0 1px var(--voice-shell-highlight); @@ -408,7 +409,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l rotate(var(--node-angle)) translateY(calc(-1 * var(--node-radius))) scale(calc(0.84 + var(--voice-volume, 0) * 0.32 + var(--voice-motion, 0) * 0.38)); - opacity: calc(0.38 + var(--voice-volume, 0) * 0.22 + var(--voice-motion, 0) * 0.28); + opacity: calc(0.68 + var(--voice-volume, 0) * 0.16 + var(--voice-motion, 0) * 0.16); animation: voice-neural-node calc(7s / var(--voice-intensity, 1)) ease-in-out infinite; animation-delay: var(--node-delay); } @@ -416,7 +417,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l .voice-visual-hologram { inset: 14%; overflow: hidden; - border: 1px solid color-mix(in srgb, rgba(var(--voice-accent-rgb, 99, 183, 170), 0.24) 80%, transparent); + border: 1px solid rgba(var(--voice-accent-rgb, 99, 183, 170), 0.42); background: radial-gradient(ellipse at center, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.08 + var(--voice-volume, 0) * 0.1)), transparent 68%), linear-gradient(180deg, transparent, color-mix(in srgb, var(--voice-shell-highlight) 16%, transparent), transparent); @@ -435,7 +436,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l radial-gradient(ellipse at 25% 50%, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.18 + var(--voice-volume, 0) * 0.22)), transparent 44%), linear-gradient(90deg, transparent, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.12 + var(--voice-volume, 0) * 0.2)), transparent); filter: blur(calc(var(--ribbon-index) * 0.35px)); - opacity: calc(0.24 + var(--voice-motion, 0) * 0.38); + opacity: calc(0.54 + var(--voice-motion, 0) * 0.38); transform: translateY(-50%) skewX(-8deg) scaleX(calc(0.78 + var(--voice-motion, 0) * 0.18 + var(--ribbon-index) * 0.035)); animation: voice-hologram-ribbon calc(2.8s / var(--voice-intensity, 1)) ease-in-out infinite; animation-delay: var(--ribbon-delay); @@ -464,8 +465,8 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l inset: 6%; overflow: hidden; background: - radial-gradient(circle, transparent 0 41%, rgba(var(--voice-accent-rgb, 99, 183, 170), 0.08) 42% 43%, transparent 44%), - conic-gradient(from 0deg, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.08 + var(--voice-motion, 0) * 0.16)), transparent 18%, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.1 + var(--voice-volume, 0) * 0.16)), transparent 52%, rgba(var(--voice-accent-rgb, 99, 183, 170), 0.08)); + radial-gradient(circle, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.06 + var(--voice-volume, 0) * 0.1)) 0 40%, rgba(var(--voice-accent-rgb, 99, 183, 170), 0.18) 42% 43%, transparent 44%), + conic-gradient(from 0deg, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.18 + var(--voice-motion, 0) * 0.22)), transparent 18%, rgba(var(--voice-accent-rgb, 99, 183, 170), calc(0.2 + var(--voice-volume, 0) * 0.22)), transparent 52%, rgba(var(--voice-accent-rgb, 99, 183, 170), 0.18)); } .voice-visual-command-grid { @@ -477,7 +478,7 @@ body { margin: 0; min-height: 100vh; background: radial-gradient(circle at top l linear-gradient(90deg, var(--voice-shell-grid-soft) 1px, transparent 1px); background-size: 18px 18px; mask-image: radial-gradient(circle, black 50%, transparent 74%); - opacity: calc(0.18 + var(--voice-motion, 0) * 0.24); + opacity: calc(0.3 + var(--voice-motion, 0) * 0.34); } .voice-visual-command-ring { diff --git a/src/components/chat/voice-agent.tsx b/src/components/chat/voice-agent.tsx index adcf200..dbaa9df 100644 --- a/src/components/chat/voice-agent.tsx +++ b/src/components/chat/voice-agent.tsx @@ -1190,16 +1190,6 @@ export function VoiceAgent({ const listeningRgb = "var(--voice-listening-rgb)"; const speakingRgb = "var(--voice-speaking-rgb)"; const processingRgb = "var(--voice-processing-rgb)"; - const activeRgb = - isMicMuted && !isPlayingAudio && !isLoading - ? "var(--text-muted-rgb, 128, 128, 128)" - : state === "listening" - ? listeningRgb - : state === "speaking" - ? speakingRgb - : state === "processing" - ? processingRgb - : accentRgb; const stateColor = isMicMuted && !isPlayingAudio && !isLoading ? "var(--text-tertiary)" @@ -1212,7 +1202,14 @@ export function VoiceAgent({ : "var(--text-tertiary)"; const glowStrength = state === "idle" ? 0.16 : 0.28 + volumeLevel * 0.32; const realtimeRelayActive = Boolean(realtimeRelayRef.current); - const visualVolume = Math.min(1, volumeLevel * (nativeSessionActive || realtimeRelayActive ? 1.25 : 0.8)); + const baseReactiveLevel = state === "speaking" + ? 0.36 + : state === "listening" + ? 0.22 + : state === "processing" + ? 0.28 + : 0; + const visualVolume = Math.min(1, Math.max(baseReactiveLevel, volumeLevel * (nativeSessionActive || realtimeRelayActive ? 1.25 : 0.8))); const motionLevel = state === "speaking" ? Math.min(0.82, 0.24 + visualVolume * 0.78) @@ -1257,7 +1254,7 @@ export function VoiceAgent({ haloSize={haloSize} orbScale={orbScale} glowStrength={glowStrength} - activeRgb={activeRgb} + activeRgb={accentRgb} listeningRgb={listeningRgb} speakingRgb={speakingRgb} processingRgb={processingRgb}