@pagelines/sdk 1.0.927 → 1.0.929

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bot.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bot.js","names":["_hoisted_1","_openBlock","_createBlock","BotWrap","_withCtx","_createElementBlock","_normalizeClass","_createElementVNode","_normalizeStyle","_createCommentVNode","BotChat","_sfc_main","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createElementVNode","_createVNode","ChatRichText","_sfc_main","_defineProperty","resolveApiBase","ChatRichText_default","ElBotChat_default","BotChatController","VoiceMode_default","BotWrap_default","Bot","BotChat_default","BOT_MODES","BotProvider_default","isVoiceAudible","createVoiceProviderClient","_hoisted_1$1","_hoisted_2","_hoisted_3","_hoisted_4","BotWidget_default","defineComponent","__expose","props","__props","isOpen","ref","isBotActive","newValue","nextTick","openBlock","createBlock","withCtx","resolvedSdk","bot","createElementBlock","normalizeClass","createElementVNode","normalizeStyle","_cache","createCommentVNode","_hoisted_1","ElBotChatPreview_default","Fragment","renderList","message","index","createVNode","event","state","turnId","turnIds","CHIME_PATHS","CHIME_VOLUME","LEVEL_INTERVAL_MS","CONNECT_TIMEOUT_MS","WORK_CUE_DELAY_MS","audio","elements","volume","players","kind","path","apiBase","WORK_CUE_VOLUME","active","SettingsObject","settings","generation","mint","error","shouldChime","args","provider","connectPromise","cause","thinking","timeoutId","reject","body","text","next","metric","durationMs","details","hadWork","level","abort","fetchFn","streamUrl","conversationId","response","readChatEventStream","previous","applied","wanted","res","result","code","viewerTimezone","detectBrowserTimezone","data","parsed","zVoiceSessionMint","fallbackCode"],"sources":["../bot/ui/BotWidget.vue","../bot/ui/BotWidget.vue","../bot/ui/ElBotChatPreview.vue","../bot/ui/ElBotChatPreview.vue","../bot/voice/voice-work-state.ts","../bot/voice/voice-session-client.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { PageLinesSDK } from '../../sdkClient'\nimport { nextTick, ref, watch } from 'vue'\nimport BotChat from './BotChat.vue'\nimport BotWrap from './BotWrap.vue'\n\ninterface Props {\n sdk?: PageLinesSDK\n handle: string\n context?: string\n firstMessage?: string\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'\n}\n\nconst props = defineProps<Props>()\nconst isOpen = ref(false)\nconst isBotActive = ref(false)\n\nfunction toggle() {\n isOpen.value = !isOpen.value\n}\n\n// Watch isOpen to delay bot activation for smooth transition\nwatch(isOpen, async (newValue) => {\n if (newValue) {\n // When opening, delay bot activation until transition starts\n await nextTick()\n setTimeout(() => {\n isBotActive.value = true\n }, 400) // Delay to let container expand first\n } else {\n // When closing, immediately deactivate bot\n isBotActive.value = false\n }\n})\n\nfunction open() {\n isOpen.value = true\n}\n\nfunction close() {\n isOpen.value = false\n}\n\nfunction handleClose() {\n close()\n}\n\ndefineExpose({\n toggle,\n open,\n close,\n})\n</script>\n\n<template>\n <BotWrap\n v-slot=\"{ bot, sdk: resolvedSdk }\"\n :sdk=\"sdk\"\n :handle=\"handle\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n >\n <!-- Fixed Widget Wrapper -->\n <div\n v-if=\"bot\"\n class=\"fixed z-[999999] pointer-events-auto\"\n :class=\"[\n props.position === 'bottom-left' ? 'bottom-6 left-6'\n : props.position === 'top-right' ? 'top-6 right-6'\n : props.position === 'top-left' ? 'top-6 left-6'\n : 'bottom-6 right-6',\n ]\"\n >\n <!-- Widget Content with Transition -->\n <div\n class=\"transition-all duration-500 ease-[cubic-bezier(0.25,1,0.33,1)] transform-gpu\"\n :class=\"[\n isOpen\n ? 'widget-expanded w-96 h-[600px] max-w-[calc(100vw-48px)] max-h-[calc(100vh-48px)] rounded-3xl opacity-100 scale-100'\n : 'widget-button w-[80px] h-[80px] opacity-100 scale-100 flex items-center justify-center cursor-pointer',\n ]\"\n :style=\"{\n 'transform-origin':\n props.position === 'bottom-left' ? 'bottom left'\n : props.position === 'top-right' ? 'top right'\n : props.position === 'top-left' ? 'top left'\n : 'bottom right',\n }\"\n @click=\"!isOpen ? toggle() : undefined\"\n >\n <!-- Button Content (visible when closed) -->\n <div v-if=\"!isOpen\" class=\"relative h-full w-full\">\n <div class=\"group relative h-full w-full rounded-3xl overflow-hidden shadow-lg transition-all duration-300 ease-[cubic-bezier(0.25,1,0.33,1)] hover:shadow-2xl hover:translate-y-[-1px] hover:brightness-110 active:scale-[1.06] active:-translate-y-0.5 active:duration-100\">\n <img\n :src=\"bot.avatarUrl.value\"\n :alt=\"`${bot.displayName.value} assistant`\"\n class=\"h-full w-full object-cover pointer-events-none\"\n width=\"80\"\n height=\"80\"\n />\n <!-- Gradient overlay for contrast -->\n <div class=\"absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60 transition-opacity duration-300 group-hover:opacity-40\" />\n </div>\n <!-- Online indicator -->\n <div v-if=\"bot.state.value.lifecycle === 'running'\" class=\"absolute top-0.5 right-0.5\">\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white absolute animate-ping\" style=\"animation-duration: 3s;\" />\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white\" />\n </div>\n </div>\n\n <!-- Chat Content (visible when open) -->\n <BotChat\n v-if=\"isOpen && bot\"\n class=\"rounded-3xl shadow-2xl border border-white/10\"\n :sdk=\"resolvedSdk\"\n :bot=\"bot\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n :has-close=\"true\"\n :is-active=\"isBotActive\"\n @close=\"handleClose\"\n />\n </div>\n </div>\n </BotWrap>\n</template>\n","<script setup lang=\"ts\">\nimport type { PageLinesSDK } from '../../sdkClient'\nimport { nextTick, ref, watch } from 'vue'\nimport BotChat from './BotChat.vue'\nimport BotWrap from './BotWrap.vue'\n\ninterface Props {\n sdk?: PageLinesSDK\n handle: string\n context?: string\n firstMessage?: string\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'\n}\n\nconst props = defineProps<Props>()\nconst isOpen = ref(false)\nconst isBotActive = ref(false)\n\nfunction toggle() {\n isOpen.value = !isOpen.value\n}\n\n// Watch isOpen to delay bot activation for smooth transition\nwatch(isOpen, async (newValue) => {\n if (newValue) {\n // When opening, delay bot activation until transition starts\n await nextTick()\n setTimeout(() => {\n isBotActive.value = true\n }, 400) // Delay to let container expand first\n } else {\n // When closing, immediately deactivate bot\n isBotActive.value = false\n }\n})\n\nfunction open() {\n isOpen.value = true\n}\n\nfunction close() {\n isOpen.value = false\n}\n\nfunction handleClose() {\n close()\n}\n\ndefineExpose({\n toggle,\n open,\n close,\n})\n</script>\n\n<template>\n <BotWrap\n v-slot=\"{ bot, sdk: resolvedSdk }\"\n :sdk=\"sdk\"\n :handle=\"handle\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n >\n <!-- Fixed Widget Wrapper -->\n <div\n v-if=\"bot\"\n class=\"fixed z-[999999] pointer-events-auto\"\n :class=\"[\n props.position === 'bottom-left' ? 'bottom-6 left-6'\n : props.position === 'top-right' ? 'top-6 right-6'\n : props.position === 'top-left' ? 'top-6 left-6'\n : 'bottom-6 right-6',\n ]\"\n >\n <!-- Widget Content with Transition -->\n <div\n class=\"transition-all duration-500 ease-[cubic-bezier(0.25,1,0.33,1)] transform-gpu\"\n :class=\"[\n isOpen\n ? 'widget-expanded w-96 h-[600px] max-w-[calc(100vw-48px)] max-h-[calc(100vh-48px)] rounded-3xl opacity-100 scale-100'\n : 'widget-button w-[80px] h-[80px] opacity-100 scale-100 flex items-center justify-center cursor-pointer',\n ]\"\n :style=\"{\n 'transform-origin':\n props.position === 'bottom-left' ? 'bottom left'\n : props.position === 'top-right' ? 'top right'\n : props.position === 'top-left' ? 'top left'\n : 'bottom right',\n }\"\n @click=\"!isOpen ? toggle() : undefined\"\n >\n <!-- Button Content (visible when closed) -->\n <div v-if=\"!isOpen\" class=\"relative h-full w-full\">\n <div class=\"group relative h-full w-full rounded-3xl overflow-hidden shadow-lg transition-all duration-300 ease-[cubic-bezier(0.25,1,0.33,1)] hover:shadow-2xl hover:translate-y-[-1px] hover:brightness-110 active:scale-[1.06] active:-translate-y-0.5 active:duration-100\">\n <img\n :src=\"bot.avatarUrl.value\"\n :alt=\"`${bot.displayName.value} assistant`\"\n class=\"h-full w-full object-cover pointer-events-none\"\n width=\"80\"\n height=\"80\"\n />\n <!-- Gradient overlay for contrast -->\n <div class=\"absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60 transition-opacity duration-300 group-hover:opacity-40\" />\n </div>\n <!-- Online indicator -->\n <div v-if=\"bot.state.value.lifecycle === 'running'\" class=\"absolute top-0.5 right-0.5\">\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white absolute animate-ping\" style=\"animation-duration: 3s;\" />\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white\" />\n </div>\n </div>\n\n <!-- Chat Content (visible when open) -->\n <BotChat\n v-if=\"isOpen && bot\"\n class=\"rounded-3xl shadow-2xl border border-white/10\"\n :sdk=\"resolvedSdk\"\n :bot=\"bot\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n :has-close=\"true\"\n :is-active=\"isBotActive\"\n @close=\"handleClose\"\n />\n </div>\n </div>\n </BotWrap>\n</template>\n","<script setup lang=\"ts\">\nimport ChatRichText from './ChatRichText.vue'\n\nexport type ChatPreviewMessage = {\n sender: 'user' | 'bot'\n text: string\n}\n\ndefineProps<{\n messages: ChatPreviewMessage[]\n variant?: 'light' | 'dark'\n}>()\n</script>\n\n<template>\n <div\n class=\"@container/chat-preview flex flex-col gap-3\"\n :class=\"variant === 'dark' ? 'text-white' : 'text-slate-950'\"\n data-test=\"bot-chat-preview\"\n >\n <div\n v-for=\"(message, index) in messages\"\n :key=\"`${message.sender}-${index}`\"\n class=\"flex\"\n :class=\"message.sender === 'user' ? 'justify-end' : 'justify-start'\"\n data-test=\"bot-chat-preview-message\"\n :data-sender=\"message.sender\"\n >\n <div\n class=\"max-w-[88%] rounded-2xl px-4 py-3 text-sm shadow-sm @sm/chat-preview:max-w-[78%]\"\n :class=\"message.sender === 'user'\n ? 'bg-slate-950 text-white'\n : variant === 'dark'\n ? 'border border-white/10 bg-white/10 text-white'\n : 'border border-slate-200 bg-white text-slate-950'\"\n >\n <ChatRichText\n :text=\"message.text\"\n :inverted=\"message.sender === 'user' || variant === 'dark'\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ChatRichText from './ChatRichText.vue'\n\nexport type ChatPreviewMessage = {\n sender: 'user' | 'bot'\n text: string\n}\n\ndefineProps<{\n messages: ChatPreviewMessage[]\n variant?: 'light' | 'dark'\n}>()\n</script>\n\n<template>\n <div\n class=\"@container/chat-preview flex flex-col gap-3\"\n :class=\"variant === 'dark' ? 'text-white' : 'text-slate-950'\"\n data-test=\"bot-chat-preview\"\n >\n <div\n v-for=\"(message, index) in messages\"\n :key=\"`${message.sender}-${index}`\"\n class=\"flex\"\n :class=\"message.sender === 'user' ? 'justify-end' : 'justify-start'\"\n data-test=\"bot-chat-preview-message\"\n :data-sender=\"message.sender\"\n >\n <div\n class=\"max-w-[88%] rounded-2xl px-4 py-3 text-sm shadow-sm @sm/chat-preview:max-w-[78%]\"\n :class=\"message.sender === 'user'\n ? 'bg-slate-950 text-white'\n : variant === 'dark'\n ? 'border border-white/10 bg-white/10 text-white'\n : 'border border-slate-200 bg-white text-slate-950'\"\n >\n <ChatRichText\n :text=\"message.text\"\n :inverted=\"message.sender === 'user' || variant === 'dark'\"\n />\n </div>\n </div>\n </div>\n</template>\n","import type { ChatEvent } from '@pagelines/core'\n\nexport interface VoiceWorkState {\n turnIds: ReadonlySet<string>\n activeTurnId?: string\n description?: string\n}\n\nexport function createVoiceWorkState(): VoiceWorkState {\n return { turnIds: new Set() }\n}\n\n/** Project canonical conversation events onto the one active voice work line. */\nexport function reduceVoiceWorkEvent(state: VoiceWorkState, event: ChatEvent): VoiceWorkState {\n if (event.event === 'message') {\n const message = event.data.message\n if (message.role === 'user' && message.metadata?.clientContext === 'voice') {\n return { ...state, turnIds: new Set(state.turnIds).add(message.messageId) }\n }\n const turnId = typeof message.metadata?.turnId === 'string' ? message.metadata.turnId : undefined\n if (turnId && state.turnIds.has(turnId)) {\n const turnIds = new Set(state.turnIds)\n turnIds.delete(turnId)\n return state.activeTurnId === turnId ? { turnIds } : { ...state, turnIds }\n }\n return state\n }\n\n const turnId = 'turnId' in event.data && typeof event.data.turnId === 'string'\n ? event.data.turnId\n : undefined\n if (!turnId || !state.turnIds.has(turnId))\n return state\n\n if (event.event === 'working_state') {\n return {\n ...state,\n activeTurnId: turnId,\n description: event.data.description.trim() || 'Working on it',\n }\n }\n if (event.event === 'tool_activity' && event.data.status === 'started') {\n return {\n ...state,\n activeTurnId: turnId,\n description: event.data.label,\n }\n }\n if (event.event === 'working_end' || event.event === 'stream_end')\n return { ...state, activeTurnId: undefined, description: undefined }\n return state\n}\n","/** Provider-neutral voice session state machine for web clients. */\nimport type { ApiResponse, VoiceClientMetric, VoiceSessionEndReason, VoiceSessionMint } from '@pagelines/core'\nimport type { Ref } from 'vue'\nimport type {\n CreateVoiceProviderClient,\n VoiceLevelEvent,\n VoiceProviderClient,\n VoiceSessionError,\n VoiceSpeakingState,\n} from './provider'\nimport { detectBrowserTimezone, readChatEventStream, SettingsObject, zVoiceSessionMint } from '@pagelines/core'\nimport { ref } from 'vue'\nimport { resolveApiBase } from '../../api'\nimport { createVoiceProviderClient, isVoiceAudible } from './provider'\nimport { createVoiceWorkState, reduceVoiceWorkEvent } from './voice-work-state'\n\nexport type { VoiceLevelEvent, VoiceSessionError, VoiceSpeakingState } from './provider'\nexport { isVoiceAudible, VOICE_SPECTRUM_BANDS, voiceSpectrum } from './provider'\n\nexport type VoiceSessionClientState = 'idle' | 'minting' | 'connecting' | 'live' | 'ended'\n/** Why a call ended without the caller hanging up; a caller's own hang-up has none. */\nexport type VoiceEndReason = VoiceSessionEndReason | 'bot' | 'network'\nexport type VoiceChimeKind = 'start' | 'end'\nexport interface VoiceWorkStatus { active: boolean, description?: string }\n\n/** Client-owned latency phases — speech-end-to-first-text is measured on the handler. */\nexport type VoiceMetric = Pick<VoiceClientMetric, 'name' | 'durationMs' | 'provider' | 'phase' | 'code'>\nexport type VoiceMetricName = VoiceMetric['name']\n\nexport interface VoiceClientHandlers {\n onStateChange?: (state: VoiceSessionClientState) => void\n onLevel?: (event: VoiceLevelEvent) => void\n onError?: (error: VoiceSessionError) => void\n onEnded?: (reason: VoiceEndReason) => void\n onThinkingChange?: (thinking: boolean) => void\n onSpeakingChange?: (state: VoiceSpeakingState) => void\n /** Fire-and-forget latency phases for product observability. */\n onMetric?: (metric: VoiceMetric) => void\n onWorkChange?: (work: VoiceWorkStatus) => void\n}\n\nexport interface VoiceSessionClientSettings extends VoiceClientHandlers, Record<string, unknown> {\n botId: string\n orgId: string\n conversationId?: string\n /** Last message already loaded by the host; the work stream starts after it. */\n latestSequence?: string\n apiBase?: string\n isDev?: boolean\n token?: string\n fetchFn?: typeof fetch\n /** Provider composition seam; the default dynamically loads the minted provider. */\n createProvider?: CreateVoiceProviderClient\n playChime?: (kind: VoiceChimeKind) => void\n /** Test seam; production uses the quiet looping work asset. */\n setWorkCue?: (active: boolean) => void\n /** Test seam; production waits WORK_CUE_DELAY_MS before the work loop starts. */\n workCueDelayMs?: number\n}\n\nconst CHIME_PATHS: Record<VoiceChimeKind, string> = {\n start: '/audio/start-call.mp3',\n end: '/audio/end-call.mp3',\n}\nconst CHIME_VOLUME = 0.7\nconst LEVEL_INTERVAL_MS = 50\nconst CONNECT_TIMEOUT_MS = 15_000\n/** Waiting must outlive this before the loop starts — normal turns stay silent. */\nconst WORK_CUE_DELAY_MS = 3_000\n\n/**\n * A chime player plus the one call that makes it audible later.\n *\n * `unlock` must run inside the user gesture that starts the call. The start\n * chime plays after minting and connecting — seconds of async — by which\n * time the gesture has expired and the autoplay policy rejects `play()`.\n * The rejection is swallowed (a missing cue must never take a call down),\n * so the failure is completely silent: exactly the reported bug, a call that\n * connects without ever announcing itself.\n */\nexport interface VoiceChimePlayer {\n play: (kind: VoiceChimeKind) => void\n unlock: () => void\n}\n\n/**\n * Mark elements user-activated so they can be played later without a gesture.\n *\n * Two details decide whether this works, and both are easy to get wrong:\n *\n * `muted`, not `volume` — iOS Safari treats `HTMLMediaElement.volume` as\n * read-only, so silencing by volume is a no-op there and the unlock plays the\n * cue out loud at full volume. iOS is the platform whose policy makes this\n * necessary at all, so getting it wrong there defeats the whole exercise.\n *\n * Stop synchronously, not from the `play()` promise. That promise settles only\n * once playback actually begins, which on a slow connection can be after the\n * real cue has been asked for — the pending stop then pauses the cue the\n * moment it starts. The activation is granted when `play()` is *called* inside\n * the gesture, so pausing on the next line keeps it and removes the race. The\n * interrupted `play()` rejects with `AbortError`; that is expected.\n */\nfunction unlockAudio(elements: HTMLAudioElement[], volume: number): void {\n for (const audio of elements) {\n audio.muted = true\n void audio.play().catch(() => {})\n audio.pause()\n audio.currentTime = 0\n audio.muted = false\n audio.volume = volume\n }\n}\n\n/**\n * The readiness cue is the only signal an eyes-off user gets, so it is served\n * from the API origin rather than the embedding page — a relative path would\n * silently 404 wherever the SDK is not hosted by the app itself.\n */\nexport function createBrowserVoiceChimePlayer(apiBase: string): VoiceChimePlayer {\n const players = Object.fromEntries(Object.entries(CHIME_PATHS).map(([kind, path]) => {\n const audio = new Audio(`${apiBase}${path}`)\n audio.preload = 'auto'\n audio.volume = CHIME_VOLUME\n return [kind, audio]\n })) as Record<VoiceChimeKind, HTMLAudioElement>\n return {\n play: (kind) => {\n players[kind].currentTime = 0\n // Still guarded: a browser that refuses anyway must not break the call.\n void players[kind].play().catch(() => {})\n },\n unlock: () => unlockAudio(Object.values(players), CHIME_VOLUME),\n }\n}\n\nconst WORK_CUE_VOLUME = 0.16\n\n/**\n * The work loop has the chime's problem and worse odds: it first plays from a\n * `WORK_CUE_DELAY_MS` timer, so it is *never* inside a gesture. It needs the\n * same unlock, which is why the session unlocks its audio as a set rather than\n * each caller solving it alone.\n */\nfunction createBrowserVoiceWorkCue(apiBase: string): { setActive: (active: boolean) => void, unlock: () => void } {\n const audio = new Audio(`${apiBase}/audio/thinking.mp3`)\n audio.preload = 'auto'\n audio.loop = true\n audio.volume = WORK_CUE_VOLUME\n return {\n setActive: (active) => {\n if (active) {\n void audio.play().catch(() => {})\n return\n }\n audio.pause()\n audio.currentTime = 0\n },\n unlock: () => unlockAudio([audio], WORK_CUE_VOLUME),\n }\n}\n\nexport class VoiceSessionClient extends SettingsObject<VoiceSessionClientSettings> {\n readonly state: Ref<VoiceSessionClientState> = ref('idle')\n readonly isMuted: Ref<boolean> = ref(false)\n\n private provider?: VoiceProviderClient\n private teardownPromise?: Promise<void>\n private providerName?: VoiceSessionMint['provider']\n private chime?: VoiceChimePlayer\n private workCue?: { setActive: (active: boolean) => void, unlock: () => void }\n private generation = 0\n private levelTimer: ReturnType<typeof setInterval> | undefined\n private startAtMs = 0\n private liveAtMs = 0\n private firstPlaybackEmitted = false\n private mutePending = false\n private conversationId?: string\n private workAbort?: AbortController\n private workState = createVoiceWorkState()\n private workCuePlaying = false\n private workCueWanted = false\n private workCuePending?: ReturnType<typeof setTimeout>\n private thinking = false\n private speaking: VoiceSpeakingState = { user: false, bot: false }\n\n constructor(settings: VoiceSessionClientSettings) {\n super('VoiceSessionClient', settings)\n // The injected seam is a bare function; tests do not need unlocking.\n this.chime = settings.playChime ? { play: settings.playChime, unlock: () => {} } : undefined\n this.workCue = settings.setWorkCue ? { setActive: settings.setWorkCue, unlock: () => {} } : undefined\n }\n\n async start(): Promise<void> {\n if (this.state.value !== 'idle')\n return\n // Before the first await, while the tap that started the call still\n // counts as consent. Everything after this point is async — the chimes\n // wait on minting and connecting, the work loop on a 3s timer — so this\n // is the only moment any of them can be authorised.\n this.ensureChime().unlock()\n this.ensureWorkCue().unlock()\n const generation = ++this.generation\n this.startAtMs = Date.now()\n this.liveAtMs = 0\n this.providerName = undefined\n this.firstPlaybackEmitted = false\n this.workState = createVoiceWorkState()\n this.thinking = false\n this.setState('minting')\n try {\n const mint = await this.mintSession(generation)\n if (!mint || generation !== this.generation)\n return\n if (this.conversationId)\n this.startWorkStream(this.conversationId, generation)\n this.setState('connecting')\n await this.connect({ mint, generation })\n } catch (error) {\n if (generation !== this.generation)\n return\n this.fail(this.toSessionError(error, 'VOICE_MINT_FAILED'))\n }\n }\n\n async end(): Promise<void> {\n if (this.state.value === 'idle')\n return\n if (this.state.value === 'ended')\n return this.teardown()\n const shouldChime = this.state.value === 'live'\n this.generation++\n if (shouldChime)\n this.playChime('end')\n this.setState('ended')\n await this.teardown()\n }\n\n private async connect(args: { mint: VoiceSessionMint, generation: number }): Promise<void> {\n const { mint, generation } = args\n this.providerName = mint.provider\n const provider = await (this.settings.createProvider ?? createVoiceProviderClient)(mint)\n if (generation !== this.generation) {\n await provider.disconnect().catch(() => {})\n return\n }\n this.provider = provider\n\n const connectPromise = provider.connect({\n onDisconnected: (cause) => {\n if (generation !== this.generation || this.provider !== provider)\n return\n const serverReason = cause === 'idle-timeout' || cause === 'session-cap' || cause === 'unavailable'\n if (serverReason || (this.state.value === 'live' && cause === 'bot')) {\n this.generation++\n this.settings.onEnded?.(cause)\n if (this.state.value === 'live')\n this.playChime('end')\n this.setState('ended')\n void this.teardown()\n return\n }\n this.fail({\n code: this.state.value === 'live' ? 'VOICE_CONNECTION_LOST' : 'VOICE_CONNECT_FAILED',\n message: this.state.value === 'live'\n ? 'The voice connection dropped.'\n : 'Could not open the voice connection.',\n })\n },\n onError: error => {\n if (generation === this.generation && this.provider === provider)\n this.fail(error)\n },\n onThinkingChange: thinking => {\n if (generation === this.generation && this.provider === provider) {\n this.thinking = thinking\n this.settings.onThinkingChange?.(thinking)\n this.updateWorkCue()\n }\n },\n onSpeakingChange: state => {\n if (generation === this.generation && this.provider === provider)\n this.setSpeaking(state)\n },\n })\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n connectPromise,\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(Object.assign(new Error('Voice connection timed out.'), { code: 'VOICE_CONNECT_TIMEOUT' })), CONNECT_TIMEOUT_MS)\n }),\n ])\n } catch (error) {\n void connectPromise.then(() => provider.disconnect()).catch(() => {})\n if (generation !== this.generation || this.provider !== provider)\n return\n this.fail(this.toSessionError(error, 'VOICE_CONNECT_FAILED'))\n return\n } finally {\n if (timeoutId)\n clearTimeout(timeoutId)\n }\n if (generation !== this.generation || this.provider !== provider)\n return\n\n this.startLevelTimer(provider, generation)\n this.liveAtMs = Date.now()\n this.setState('live')\n this.updateWorkCue()\n this.emitMetric('voice.ready', this.liveAtMs - this.startAtMs)\n this.playChime('start')\n }\n\n /** True while a live provider can accept typed text as a spoken turn. */\n get canSendUserMessage(): boolean {\n return this.state.value === 'live' && !!this.provider?.sendUserMessage\n }\n\n sendUserMessage(text: string): boolean {\n const body = text.trim()\n if (!body || !this.canSendUserMessage)\n return false\n this.provider!.sendUserMessage!(body)\n return true\n }\n\n /** Returns the applied state, or undefined when mute is unavailable or fails. */\n async toggleMuted(): Promise<boolean | undefined> {\n const provider = this.provider\n if (this.state.value !== 'live' || !provider?.setMuted || this.mutePending)\n return undefined\n const generation = this.generation\n const next = !this.isMuted.value\n this.mutePending = true\n try {\n await provider.setMuted(next)\n if (generation !== this.generation || this.provider !== provider || this.state.value !== 'live')\n return undefined\n this.isMuted.value = next\n // Mute clears the caller's activity and nothing else: working, thinking,\n // and playback carry on.\n if (next && this.speaking.user)\n this.setSpeaking({ ...this.speaking, user: false })\n return next\n }\n catch {\n return undefined\n }\n finally {\n this.mutePending = false\n }\n }\n\n private emitMetric(name: VoiceMetricName, durationMs: number, details: Pick<VoiceMetric, 'phase' | 'code'> = {}): void {\n const metric: VoiceMetric = {\n name,\n durationMs: Math.max(0, Math.round(durationMs)),\n ...(this.providerName ? { provider: this.providerName } : {}),\n ...details,\n }\n this.settings.onMetric?.(metric)\n this.postMetric(metric)\n }\n\n private setState(state: VoiceSessionClientState): void {\n if (this.state.value === state)\n return\n this.state.value = state\n this.settings.onStateChange?.(state)\n }\n\n private fail(error: VoiceSessionError): void {\n const shouldChime = this.state.value === 'live'\n if (this.startAtMs > 0) {\n this.emitMetric('voice.failed', Date.now() - this.startAtMs, {\n phase: this.state.value,\n code: error.code,\n })\n }\n this.generation++\n if (shouldChime)\n this.playChime('end')\n this.settings.onError?.(error)\n this.settings.onEnded?.(error.code === 'VOICE_CONNECTION_LOST' ? 'network' : 'unavailable')\n this.setState('ended')\n void this.teardown()\n }\n\n private teardown(): Promise<void> {\n if (this.teardownPromise)\n return this.teardownPromise\n this.workAbort?.abort()\n this.workAbort = undefined\n const hadWork = !!this.workState.activeTurnId\n this.workState = createVoiceWorkState()\n this.thinking = false\n if (hadWork)\n this.settings.onWorkChange?.({ active: false })\n this.setSpeaking({ user: false, bot: false })\n this.settings.onThinkingChange?.(false)\n this.workCueWanted = false\n if (this.workCuePending !== undefined) {\n clearTimeout(this.workCuePending)\n this.workCuePending = undefined\n }\n this.setWorkCue(false)\n if (this.levelTimer !== undefined) {\n clearInterval(this.levelTimer)\n this.levelTimer = undefined\n }\n const provider = this.provider\n this.provider = undefined\n this.isMuted.value = false\n this.settings.onLevel?.({ source: 'playback', level: 0 })\n this.teardownPromise = provider?.disconnect().catch(() => {}) ?? Promise.resolve()\n return this.teardownPromise\n }\n\n private startLevelTimer(provider: VoiceProviderClient, generation: number): void {\n if (this.levelTimer !== undefined)\n return\n this.levelTimer = setInterval(() => {\n if (generation !== this.generation || this.provider !== provider)\n return\n // The transport's speaker state picks the meter: the mic analyser hears\n // the speaker too, so \"loudest wins\" flickered green over the assistant.\n const level = provider.sampleLevel(this.speaking.bot ? 'playback' : this.speaking.user ? 'mic' : undefined)\n if (!level)\n return\n this.settings.onLevel?.(level)\n if (\n !this.firstPlaybackEmitted\n && level.source === 'playback'\n && isVoiceAudible(level.level)\n && this.liveAtMs > 0\n ) {\n this.firstPlaybackEmitted = true\n this.emitMetric('voice.first_playback', Date.now() - this.liveAtMs)\n }\n }, LEVEL_INTERVAL_MS)\n }\n\n private authHeaders(): Record<string, string> {\n return this.settings.token ? { Authorization: `Bearer ${this.settings.token}` } : {}\n }\n\n private postMetric(metric: VoiceMetric): void {\n void this.post<{ recorded: boolean }>({\n path: '/api/voice/metrics',\n keepalive: true,\n body: {\n ...metric,\n client: 'web',\n botId: this.settings.botId,\n ...(this.conversationId ? { conversationId: this.conversationId } : {}),\n },\n }).catch(() => {})\n }\n\n private startWorkStream(conversationId: string, generation: number): void {\n this.workAbort?.abort()\n const abort = new AbortController()\n this.workAbort = abort\n const apiBase = resolveApiBase(this.settings.apiBase, this.settings.isDev)\n const fetchFn = this.settings.fetchFn ?? fetch\n const streamUrl = this.settings.latestSequence\n ? `${apiBase}/api/conversations/${encodeURIComponent(conversationId)}/stream?since=${encodeURIComponent(this.settings.latestSequence)}`\n : `${apiBase}/api/conversations/${encodeURIComponent(conversationId)}/stream`\n void (async () => {\n const response = await fetchFn(streamUrl, {\n headers: { Accept: 'text/event-stream', ...this.authHeaders() },\n credentials: 'include',\n signal: abort.signal,\n })\n if (!response.ok || !response.body)\n return\n for await (const event of readChatEventStream(response.body.getReader())) {\n if (abort.signal.aborted || generation !== this.generation)\n return\n const previous = this.workState\n const next = reduceVoiceWorkEvent(previous, event)\n if (next === previous)\n continue\n this.workState = next\n if (next.activeTurnId !== previous.activeTurnId || next.description !== previous.description) {\n this.settings.onWorkChange?.({\n active: !!next.activeTurnId,\n ...(next.description ? { description: next.description } : {}),\n })\n this.updateWorkCue()\n }\n }\n })().catch(() => {})\n }\n\n private setSpeaking(next: VoiceSpeakingState): void {\n const applied = { user: next.user && !this.isMuted.value, bot: next.bot }\n if (applied.user === this.speaking.user && applied.bot === this.speaking.bot)\n return\n this.speaking = applied\n this.settings.onSpeakingChange?.(applied)\n this.updateWorkCue()\n }\n\n /**\n * Duck on speech, not on energy: room tone kept the loop from ever\n * starting. The loop starts only once waiting outlives the dwell — a blip\n * shorter than that is a normal turn and stays silent.\n */\n private updateWorkCue(): void {\n const wanted = this.state.value === 'live'\n && (this.thinking || !!this.workState.activeTurnId)\n && !this.speaking.user\n && !this.speaking.bot\n if (wanted === this.workCueWanted)\n return\n this.workCueWanted = wanted\n if (this.workCuePending !== undefined) {\n clearTimeout(this.workCuePending)\n this.workCuePending = undefined\n }\n if (wanted) {\n const generation = this.generation\n this.workCuePending = setTimeout(() => {\n this.workCuePending = undefined\n if (generation === this.generation && this.workCueWanted)\n this.setWorkCue(true)\n }, this.settings.workCueDelayMs ?? WORK_CUE_DELAY_MS)\n return\n }\n this.setWorkCue(false)\n }\n\n private setWorkCue(active: boolean): void {\n if (this.workCuePlaying === active)\n return\n this.workCuePlaying = active\n this.ensureWorkCue().setActive(active)\n }\n\n private ensureWorkCue(): { setActive: (active: boolean) => void, unlock: () => void } {\n this.workCue ??= createBrowserVoiceWorkCue(resolveApiBase(this.settings.apiBase, this.settings.isDev))\n return this.workCue\n }\n\n private ensureChime(): VoiceChimePlayer {\n this.chime ??= createBrowserVoiceChimePlayer(resolveApiBase(this.settings.apiBase, this.settings.isDev))\n return this.chime\n }\n\n private playChime(kind: VoiceChimeKind): void {\n this.ensureChime().play(kind)\n }\n\n private async post<T>(args: { path: string, body: unknown, keepalive?: boolean }): Promise<T> {\n const apiBase = resolveApiBase(this.settings.apiBase, this.settings.isDev)\n const fetchFn = this.settings.fetchFn ?? fetch\n const res = await fetchFn(`${apiBase}${args.path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.authHeaders() },\n credentials: 'include',\n keepalive: args.keepalive,\n body: JSON.stringify(args.body),\n })\n const result = await res.json() as ApiResponse<T>\n if (!res.ok || !result.ok) {\n const message = !result.ok ? result.error : `HTTP ${res.status}`\n const code = !result.ok && result.code ? result.code : 'VOICE_MINT_FAILED'\n throw Object.assign(new Error(message || 'Voice request failed.'), { code })\n }\n return result.data\n }\n\n private async mintSession(generation: number): Promise<VoiceSessionMint | null> {\n let conversationId = this.settings.conversationId\n if (!conversationId) {\n conversationId = (await this.post<{ conversationId: string }>({\n path: '/api/conversations',\n body: {\n orgId: this.settings.orgId,\n participants: [{ type: 'bot', id: this.settings.botId }],\n },\n })).conversationId\n if (generation !== this.generation)\n return null\n }\n\n const viewerTimezone = detectBrowserTimezone()\n const data = await this.post<unknown>({\n path: '/api/voice/sessions',\n body: { conversationId, ...(viewerTimezone ? { viewerTimezone } : {}) },\n })\n if (generation !== this.generation)\n return null\n const parsed = zVoiceSessionMint.safeParse(data)\n if (!parsed.success)\n throw Object.assign(new Error('Voice session mint returned an unexpected shape.'), { code: 'VOICE_MINT_INVALID' })\n this.conversationId = conversationId\n return parsed.data\n }\n\n private toSessionError(error: unknown, fallbackCode: string): VoiceSessionError {\n return error instanceof Error\n ? {\n code: 'code' in error ? String((error as Error & { code: unknown }).code) : fallbackCode,\n message: error.message,\n }\n : { code: fallbackCode, message: 'Voice session failed to start.' }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bot.js","names":["_hoisted_1","_openBlock","_createBlock","BotWrap","_withCtx","_createElementBlock","_normalizeClass","_createElementVNode","_normalizeStyle","_createCommentVNode","BotChat","_sfc_main","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createElementVNode","_createVNode","ChatRichText","_sfc_main","_defineProperty","resolveApiBase","BotChatController","ChatTurn_default","ChatComposer_default","ChatRichText_default","ElBotChat_default","ChatScroller_default","VoiceMode_default","ChatDivider_default","BotWrap_default","Bot","BotChat_default","BOT_MODES","BotProvider_default","isVoiceAudible","createVoiceProviderClient","_hoisted_1$1","_hoisted_2","_hoisted_3","_hoisted_4","BotWidget_default","defineComponent","__expose","props","__props","isOpen","ref","isBotActive","newValue","nextTick","openBlock","createBlock","withCtx","resolvedSdk","bot","createElementBlock","normalizeClass","createElementVNode","normalizeStyle","_cache","createCommentVNode","_hoisted_1","ElBotChatPreview_default","Fragment","renderList","message","index","createVNode","event","state","turnId","turnIds","CHIME_PATHS","CHIME_VOLUME","LEVEL_INTERVAL_MS","CONNECT_TIMEOUT_MS","WORK_CUE_DELAY_MS","audio","elements","volume","players","kind","path","apiBase","WORK_CUE_VOLUME","active","SettingsObject","settings","generation","mint","error","shouldChime","args","provider","connectPromise","cause","thinking","timeoutId","reject","body","text","next","metric","durationMs","details","hadWork","level","abort","fetchFn","streamUrl","conversationId","response","readChatEventStream","previous","applied","wanted","res","result","code","viewerTimezone","detectBrowserTimezone","data","parsed","zVoiceSessionMint","fallbackCode"],"sources":["../bot/ui/BotWidget.vue","../bot/ui/BotWidget.vue","../bot/ui/ElBotChatPreview.vue","../bot/ui/ElBotChatPreview.vue","../bot/voice/voice-work-state.ts","../bot/voice/voice-session-client.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { PageLinesSDK } from '../../sdkClient'\nimport { nextTick, ref, watch } from 'vue'\nimport BotChat from './BotChat.vue'\nimport BotWrap from './BotWrap.vue'\n\ninterface Props {\n sdk?: PageLinesSDK\n handle: string\n context?: string\n firstMessage?: string\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'\n}\n\nconst props = defineProps<Props>()\nconst isOpen = ref(false)\nconst isBotActive = ref(false)\n\nfunction toggle() {\n isOpen.value = !isOpen.value\n}\n\n// Watch isOpen to delay bot activation for smooth transition\nwatch(isOpen, async (newValue) => {\n if (newValue) {\n // When opening, delay bot activation until transition starts\n await nextTick()\n setTimeout(() => {\n isBotActive.value = true\n }, 400) // Delay to let container expand first\n } else {\n // When closing, immediately deactivate bot\n isBotActive.value = false\n }\n})\n\nfunction open() {\n isOpen.value = true\n}\n\nfunction close() {\n isOpen.value = false\n}\n\nfunction handleClose() {\n close()\n}\n\ndefineExpose({\n toggle,\n open,\n close,\n})\n</script>\n\n<template>\n <BotWrap\n v-slot=\"{ bot, sdk: resolvedSdk }\"\n :sdk=\"sdk\"\n :handle=\"handle\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n >\n <!-- Fixed Widget Wrapper -->\n <div\n v-if=\"bot\"\n class=\"fixed z-[999999] pointer-events-auto\"\n :class=\"[\n props.position === 'bottom-left' ? 'bottom-6 left-6'\n : props.position === 'top-right' ? 'top-6 right-6'\n : props.position === 'top-left' ? 'top-6 left-6'\n : 'bottom-6 right-6',\n ]\"\n >\n <!-- Widget Content with Transition -->\n <div\n class=\"transition-all duration-500 ease-[cubic-bezier(0.25,1,0.33,1)] transform-gpu\"\n :class=\"[\n isOpen\n ? 'widget-expanded w-96 h-[600px] max-w-[calc(100vw-48px)] max-h-[calc(100vh-48px)] rounded-3xl opacity-100 scale-100'\n : 'widget-button w-[80px] h-[80px] opacity-100 scale-100 flex items-center justify-center cursor-pointer',\n ]\"\n :style=\"{\n 'transform-origin':\n props.position === 'bottom-left' ? 'bottom left'\n : props.position === 'top-right' ? 'top right'\n : props.position === 'top-left' ? 'top left'\n : 'bottom right',\n }\"\n @click=\"!isOpen ? toggle() : undefined\"\n >\n <!-- Button Content (visible when closed) -->\n <div v-if=\"!isOpen\" class=\"relative h-full w-full\">\n <div class=\"group relative h-full w-full rounded-3xl overflow-hidden shadow-lg transition-all duration-300 ease-[cubic-bezier(0.25,1,0.33,1)] hover:shadow-2xl hover:translate-y-[-1px] hover:brightness-110 active:scale-[1.06] active:-translate-y-0.5 active:duration-100\">\n <img\n :src=\"bot.avatarUrl.value\"\n :alt=\"`${bot.displayName.value} assistant`\"\n class=\"h-full w-full object-cover pointer-events-none\"\n width=\"80\"\n height=\"80\"\n />\n <!-- Gradient overlay for contrast -->\n <div class=\"absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60 transition-opacity duration-300 group-hover:opacity-40\" />\n </div>\n <!-- Online indicator -->\n <div v-if=\"bot.state.value.lifecycle === 'running'\" class=\"absolute top-0.5 right-0.5\">\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white absolute animate-ping\" style=\"animation-duration: 3s;\" />\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white\" />\n </div>\n </div>\n\n <!-- Chat Content (visible when open) -->\n <BotChat\n v-if=\"isOpen && bot\"\n class=\"rounded-3xl shadow-2xl border border-white/10\"\n :sdk=\"resolvedSdk\"\n :bot=\"bot\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n :has-close=\"true\"\n :is-active=\"isBotActive\"\n @close=\"handleClose\"\n />\n </div>\n </div>\n </BotWrap>\n</template>\n","<script setup lang=\"ts\">\nimport type { PageLinesSDK } from '../../sdkClient'\nimport { nextTick, ref, watch } from 'vue'\nimport BotChat from './BotChat.vue'\nimport BotWrap from './BotWrap.vue'\n\ninterface Props {\n sdk?: PageLinesSDK\n handle: string\n context?: string\n firstMessage?: string\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'\n}\n\nconst props = defineProps<Props>()\nconst isOpen = ref(false)\nconst isBotActive = ref(false)\n\nfunction toggle() {\n isOpen.value = !isOpen.value\n}\n\n// Watch isOpen to delay bot activation for smooth transition\nwatch(isOpen, async (newValue) => {\n if (newValue) {\n // When opening, delay bot activation until transition starts\n await nextTick()\n setTimeout(() => {\n isBotActive.value = true\n }, 400) // Delay to let container expand first\n } else {\n // When closing, immediately deactivate bot\n isBotActive.value = false\n }\n})\n\nfunction open() {\n isOpen.value = true\n}\n\nfunction close() {\n isOpen.value = false\n}\n\nfunction handleClose() {\n close()\n}\n\ndefineExpose({\n toggle,\n open,\n close,\n})\n</script>\n\n<template>\n <BotWrap\n v-slot=\"{ bot, sdk: resolvedSdk }\"\n :sdk=\"sdk\"\n :handle=\"handle\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n >\n <!-- Fixed Widget Wrapper -->\n <div\n v-if=\"bot\"\n class=\"fixed z-[999999] pointer-events-auto\"\n :class=\"[\n props.position === 'bottom-left' ? 'bottom-6 left-6'\n : props.position === 'top-right' ? 'top-6 right-6'\n : props.position === 'top-left' ? 'top-6 left-6'\n : 'bottom-6 right-6',\n ]\"\n >\n <!-- Widget Content with Transition -->\n <div\n class=\"transition-all duration-500 ease-[cubic-bezier(0.25,1,0.33,1)] transform-gpu\"\n :class=\"[\n isOpen\n ? 'widget-expanded w-96 h-[600px] max-w-[calc(100vw-48px)] max-h-[calc(100vh-48px)] rounded-3xl opacity-100 scale-100'\n : 'widget-button w-[80px] h-[80px] opacity-100 scale-100 flex items-center justify-center cursor-pointer',\n ]\"\n :style=\"{\n 'transform-origin':\n props.position === 'bottom-left' ? 'bottom left'\n : props.position === 'top-right' ? 'top right'\n : props.position === 'top-left' ? 'top left'\n : 'bottom right',\n }\"\n @click=\"!isOpen ? toggle() : undefined\"\n >\n <!-- Button Content (visible when closed) -->\n <div v-if=\"!isOpen\" class=\"relative h-full w-full\">\n <div class=\"group relative h-full w-full rounded-3xl overflow-hidden shadow-lg transition-all duration-300 ease-[cubic-bezier(0.25,1,0.33,1)] hover:shadow-2xl hover:translate-y-[-1px] hover:brightness-110 active:scale-[1.06] active:-translate-y-0.5 active:duration-100\">\n <img\n :src=\"bot.avatarUrl.value\"\n :alt=\"`${bot.displayName.value} assistant`\"\n class=\"h-full w-full object-cover pointer-events-none\"\n width=\"80\"\n height=\"80\"\n />\n <!-- Gradient overlay for contrast -->\n <div class=\"absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60 transition-opacity duration-300 group-hover:opacity-40\" />\n </div>\n <!-- Online indicator -->\n <div v-if=\"bot.state.value.lifecycle === 'running'\" class=\"absolute top-0.5 right-0.5\">\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white absolute animate-ping\" style=\"animation-duration: 3s;\" />\n <div class=\"size-3 bg-green-500 rounded-full ring-2 ring-white\" />\n </div>\n </div>\n\n <!-- Chat Content (visible when open) -->\n <BotChat\n v-if=\"isOpen && bot\"\n class=\"rounded-3xl shadow-2xl border border-white/10\"\n :sdk=\"resolvedSdk\"\n :bot=\"bot\"\n :context=\"context\"\n :first-message=\"firstMessage\"\n :has-close=\"true\"\n :is-active=\"isBotActive\"\n @close=\"handleClose\"\n />\n </div>\n </div>\n </BotWrap>\n</template>\n","<script setup lang=\"ts\">\nimport ChatRichText from './ChatRichText.vue'\n\nexport type ChatPreviewMessage = {\n sender: 'user' | 'bot'\n text: string\n}\n\ndefineProps<{\n messages: ChatPreviewMessage[]\n variant?: 'light' | 'dark'\n}>()\n</script>\n\n<template>\n <div\n class=\"@container/chat-preview flex flex-col gap-3\"\n :class=\"variant === 'dark' ? 'text-white' : 'text-slate-950'\"\n data-test=\"bot-chat-preview\"\n >\n <div\n v-for=\"(message, index) in messages\"\n :key=\"`${message.sender}-${index}`\"\n class=\"flex\"\n :class=\"message.sender === 'user' ? 'justify-end' : 'justify-start'\"\n data-test=\"bot-chat-preview-message\"\n :data-sender=\"message.sender\"\n >\n <div\n class=\"max-w-[88%] rounded-2xl px-4 py-3 text-sm shadow-sm @sm/chat-preview:max-w-[78%]\"\n :class=\"message.sender === 'user'\n ? 'bg-slate-950 text-white'\n : variant === 'dark'\n ? 'border border-white/10 bg-white/10 text-white'\n : 'border border-slate-200 bg-white text-slate-950'\"\n >\n <ChatRichText\n :text=\"message.text\"\n :inverted=\"message.sender === 'user' || variant === 'dark'\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ChatRichText from './ChatRichText.vue'\n\nexport type ChatPreviewMessage = {\n sender: 'user' | 'bot'\n text: string\n}\n\ndefineProps<{\n messages: ChatPreviewMessage[]\n variant?: 'light' | 'dark'\n}>()\n</script>\n\n<template>\n <div\n class=\"@container/chat-preview flex flex-col gap-3\"\n :class=\"variant === 'dark' ? 'text-white' : 'text-slate-950'\"\n data-test=\"bot-chat-preview\"\n >\n <div\n v-for=\"(message, index) in messages\"\n :key=\"`${message.sender}-${index}`\"\n class=\"flex\"\n :class=\"message.sender === 'user' ? 'justify-end' : 'justify-start'\"\n data-test=\"bot-chat-preview-message\"\n :data-sender=\"message.sender\"\n >\n <div\n class=\"max-w-[88%] rounded-2xl px-4 py-3 text-sm shadow-sm @sm/chat-preview:max-w-[78%]\"\n :class=\"message.sender === 'user'\n ? 'bg-slate-950 text-white'\n : variant === 'dark'\n ? 'border border-white/10 bg-white/10 text-white'\n : 'border border-slate-200 bg-white text-slate-950'\"\n >\n <ChatRichText\n :text=\"message.text\"\n :inverted=\"message.sender === 'user' || variant === 'dark'\"\n />\n </div>\n </div>\n </div>\n</template>\n","import type { ChatEvent } from '@pagelines/core'\n\nexport interface VoiceWorkState {\n turnIds: ReadonlySet<string>\n activeTurnId?: string\n description?: string\n}\n\nexport function createVoiceWorkState(): VoiceWorkState {\n return { turnIds: new Set() }\n}\n\n/** Project canonical conversation events onto the one active voice work line. */\nexport function reduceVoiceWorkEvent(state: VoiceWorkState, event: ChatEvent): VoiceWorkState {\n if (event.event === 'message') {\n const message = event.data.message\n if (message.role === 'user' && message.metadata?.clientContext === 'voice') {\n return { ...state, turnIds: new Set(state.turnIds).add(message.messageId) }\n }\n const turnId = typeof message.metadata?.turnId === 'string' ? message.metadata.turnId : undefined\n if (turnId && state.turnIds.has(turnId)) {\n const turnIds = new Set(state.turnIds)\n turnIds.delete(turnId)\n return state.activeTurnId === turnId ? { turnIds } : { ...state, turnIds }\n }\n return state\n }\n\n const turnId = 'turnId' in event.data && typeof event.data.turnId === 'string'\n ? event.data.turnId\n : undefined\n if (!turnId || !state.turnIds.has(turnId))\n return state\n\n if (event.event === 'working_state') {\n return {\n ...state,\n activeTurnId: turnId,\n description: event.data.description.trim() || 'Working on it',\n }\n }\n if (event.event === 'tool_activity' && event.data.status === 'started') {\n return {\n ...state,\n activeTurnId: turnId,\n description: event.data.label,\n }\n }\n if (event.event === 'working_end' || event.event === 'stream_end')\n return { ...state, activeTurnId: undefined, description: undefined }\n return state\n}\n","/** Provider-neutral voice session state machine for web clients. */\nimport type { ApiResponse, VoiceClientMetric, VoiceSessionEndReason, VoiceSessionMint } from '@pagelines/core'\nimport type { Ref } from 'vue'\nimport type {\n CreateVoiceProviderClient,\n VoiceLevelEvent,\n VoiceProviderClient,\n VoiceSessionError,\n VoiceSpeakingState,\n} from './provider'\nimport { detectBrowserTimezone, readChatEventStream, SettingsObject, zVoiceSessionMint } from '@pagelines/core'\nimport { ref } from 'vue'\nimport { resolveApiBase } from '../../api'\nimport { createVoiceProviderClient, isVoiceAudible } from './provider'\nimport { createVoiceWorkState, reduceVoiceWorkEvent } from './voice-work-state'\n\nexport type { VoiceLevelEvent, VoiceSessionError, VoiceSpeakingState } from './provider'\nexport { isVoiceAudible, VOICE_SPECTRUM_BANDS, voiceSpectrum } from './provider'\n\nexport type VoiceSessionClientState = 'idle' | 'minting' | 'connecting' | 'live' | 'ended'\n/** Why a call ended without the caller hanging up; a caller's own hang-up has none. */\nexport type VoiceEndReason = VoiceSessionEndReason | 'bot' | 'network'\nexport type VoiceChimeKind = 'start' | 'end'\nexport interface VoiceWorkStatus { active: boolean, description?: string }\n\n/** Client-owned latency phases — speech-end-to-first-text is measured on the handler. */\nexport type VoiceMetric = Pick<VoiceClientMetric, 'name' | 'durationMs' | 'provider' | 'phase' | 'code'>\nexport type VoiceMetricName = VoiceMetric['name']\n\nexport interface VoiceClientHandlers {\n onStateChange?: (state: VoiceSessionClientState) => void\n onLevel?: (event: VoiceLevelEvent) => void\n onError?: (error: VoiceSessionError) => void\n onEnded?: (reason: VoiceEndReason) => void\n onThinkingChange?: (thinking: boolean) => void\n onSpeakingChange?: (state: VoiceSpeakingState) => void\n /** Fire-and-forget latency phases for product observability. */\n onMetric?: (metric: VoiceMetric) => void\n onWorkChange?: (work: VoiceWorkStatus) => void\n}\n\nexport interface VoiceSessionClientSettings extends VoiceClientHandlers, Record<string, unknown> {\n botId: string\n orgId: string\n conversationId?: string\n /** Last message already loaded by the host; the work stream starts after it. */\n latestSequence?: string\n apiBase?: string\n isDev?: boolean\n token?: string\n fetchFn?: typeof fetch\n /** Provider composition seam; the default dynamically loads the minted provider. */\n createProvider?: CreateVoiceProviderClient\n playChime?: (kind: VoiceChimeKind) => void\n /** Test seam; production uses the quiet looping work asset. */\n setWorkCue?: (active: boolean) => void\n /** Test seam; production waits WORK_CUE_DELAY_MS before the work loop starts. */\n workCueDelayMs?: number\n}\n\nconst CHIME_PATHS: Record<VoiceChimeKind, string> = {\n start: '/audio/start-call.mp3',\n end: '/audio/end-call.mp3',\n}\nconst CHIME_VOLUME = 0.7\nconst LEVEL_INTERVAL_MS = 50\nconst CONNECT_TIMEOUT_MS = 15_000\n/** Waiting must outlive this before the loop starts — normal turns stay silent. */\nconst WORK_CUE_DELAY_MS = 3_000\n\n/**\n * A chime player plus the one call that makes it audible later.\n *\n * `unlock` must run inside the user gesture that starts the call. The start\n * chime plays after minting and connecting — seconds of async — by which\n * time the gesture has expired and the autoplay policy rejects `play()`.\n * The rejection is swallowed (a missing cue must never take a call down),\n * so the failure is completely silent: exactly the reported bug, a call that\n * connects without ever announcing itself.\n */\nexport interface VoiceChimePlayer {\n play: (kind: VoiceChimeKind) => void\n unlock: () => void\n}\n\n/**\n * Mark elements user-activated so they can be played later without a gesture.\n *\n * Two details decide whether this works, and both are easy to get wrong:\n *\n * `muted`, not `volume` — iOS Safari treats `HTMLMediaElement.volume` as\n * read-only, so silencing by volume is a no-op there and the unlock plays the\n * cue out loud at full volume. iOS is the platform whose policy makes this\n * necessary at all, so getting it wrong there defeats the whole exercise.\n *\n * Stop synchronously, not from the `play()` promise. That promise settles only\n * once playback actually begins, which on a slow connection can be after the\n * real cue has been asked for — the pending stop then pauses the cue the\n * moment it starts. The activation is granted when `play()` is *called* inside\n * the gesture, so pausing on the next line keeps it and removes the race. The\n * interrupted `play()` rejects with `AbortError`; that is expected.\n */\nfunction unlockAudio(elements: HTMLAudioElement[], volume: number): void {\n for (const audio of elements) {\n audio.muted = true\n void audio.play().catch(() => {})\n audio.pause()\n audio.currentTime = 0\n audio.muted = false\n audio.volume = volume\n }\n}\n\n/**\n * The readiness cue is the only signal an eyes-off user gets, so it is served\n * from the API origin rather than the embedding page — a relative path would\n * silently 404 wherever the SDK is not hosted by the app itself.\n */\nexport function createBrowserVoiceChimePlayer(apiBase: string): VoiceChimePlayer {\n const players = Object.fromEntries(Object.entries(CHIME_PATHS).map(([kind, path]) => {\n const audio = new Audio(`${apiBase}${path}`)\n audio.preload = 'auto'\n audio.volume = CHIME_VOLUME\n return [kind, audio]\n })) as Record<VoiceChimeKind, HTMLAudioElement>\n return {\n play: (kind) => {\n players[kind].currentTime = 0\n // Still guarded: a browser that refuses anyway must not break the call.\n void players[kind].play().catch(() => {})\n },\n unlock: () => unlockAudio(Object.values(players), CHIME_VOLUME),\n }\n}\n\nconst WORK_CUE_VOLUME = 0.16\n\n/**\n * The work loop has the chime's problem and worse odds: it first plays from a\n * `WORK_CUE_DELAY_MS` timer, so it is *never* inside a gesture. It needs the\n * same unlock, which is why the session unlocks its audio as a set rather than\n * each caller solving it alone.\n */\nfunction createBrowserVoiceWorkCue(apiBase: string): { setActive: (active: boolean) => void, unlock: () => void } {\n const audio = new Audio(`${apiBase}/audio/thinking.mp3`)\n audio.preload = 'auto'\n audio.loop = true\n audio.volume = WORK_CUE_VOLUME\n return {\n setActive: (active) => {\n if (active) {\n void audio.play().catch(() => {})\n return\n }\n audio.pause()\n audio.currentTime = 0\n },\n unlock: () => unlockAudio([audio], WORK_CUE_VOLUME),\n }\n}\n\nexport class VoiceSessionClient extends SettingsObject<VoiceSessionClientSettings> {\n readonly state: Ref<VoiceSessionClientState> = ref('idle')\n readonly isMuted: Ref<boolean> = ref(false)\n\n private provider?: VoiceProviderClient\n private teardownPromise?: Promise<void>\n private providerName?: VoiceSessionMint['provider']\n private chime?: VoiceChimePlayer\n private workCue?: { setActive: (active: boolean) => void, unlock: () => void }\n private generation = 0\n private levelTimer: ReturnType<typeof setInterval> | undefined\n private startAtMs = 0\n private liveAtMs = 0\n private firstPlaybackEmitted = false\n private mutePending = false\n private conversationId?: string\n private workAbort?: AbortController\n private workState = createVoiceWorkState()\n private workCuePlaying = false\n private workCueWanted = false\n private workCuePending?: ReturnType<typeof setTimeout>\n private thinking = false\n private speaking: VoiceSpeakingState = { user: false, bot: false }\n\n constructor(settings: VoiceSessionClientSettings) {\n super('VoiceSessionClient', settings)\n // The injected seam is a bare function; tests do not need unlocking.\n this.chime = settings.playChime ? { play: settings.playChime, unlock: () => {} } : undefined\n this.workCue = settings.setWorkCue ? { setActive: settings.setWorkCue, unlock: () => {} } : undefined\n }\n\n async start(): Promise<void> {\n if (this.state.value !== 'idle')\n return\n // Before the first await, while the tap that started the call still\n // counts as consent. Everything after this point is async — the chimes\n // wait on minting and connecting, the work loop on a 3s timer — so this\n // is the only moment any of them can be authorised.\n this.ensureChime().unlock()\n this.ensureWorkCue().unlock()\n const generation = ++this.generation\n this.startAtMs = Date.now()\n this.liveAtMs = 0\n this.providerName = undefined\n this.firstPlaybackEmitted = false\n this.workState = createVoiceWorkState()\n this.thinking = false\n this.setState('minting')\n try {\n const mint = await this.mintSession(generation)\n if (!mint || generation !== this.generation)\n return\n if (this.conversationId)\n this.startWorkStream(this.conversationId, generation)\n this.setState('connecting')\n await this.connect({ mint, generation })\n } catch (error) {\n if (generation !== this.generation)\n return\n this.fail(this.toSessionError(error, 'VOICE_MINT_FAILED'))\n }\n }\n\n async end(): Promise<void> {\n if (this.state.value === 'idle')\n return\n if (this.state.value === 'ended')\n return this.teardown()\n const shouldChime = this.state.value === 'live'\n this.generation++\n if (shouldChime)\n this.playChime('end')\n this.setState('ended')\n await this.teardown()\n }\n\n private async connect(args: { mint: VoiceSessionMint, generation: number }): Promise<void> {\n const { mint, generation } = args\n this.providerName = mint.provider\n const provider = await (this.settings.createProvider ?? createVoiceProviderClient)(mint)\n if (generation !== this.generation) {\n await provider.disconnect().catch(() => {})\n return\n }\n this.provider = provider\n\n const connectPromise = provider.connect({\n onDisconnected: (cause) => {\n if (generation !== this.generation || this.provider !== provider)\n return\n const serverReason = cause === 'idle-timeout' || cause === 'session-cap' || cause === 'unavailable'\n if (serverReason || (this.state.value === 'live' && cause === 'bot')) {\n this.generation++\n this.settings.onEnded?.(cause)\n if (this.state.value === 'live')\n this.playChime('end')\n this.setState('ended')\n void this.teardown()\n return\n }\n this.fail({\n code: this.state.value === 'live' ? 'VOICE_CONNECTION_LOST' : 'VOICE_CONNECT_FAILED',\n message: this.state.value === 'live'\n ? 'The voice connection dropped.'\n : 'Could not open the voice connection.',\n })\n },\n onError: error => {\n if (generation === this.generation && this.provider === provider)\n this.fail(error)\n },\n onThinkingChange: thinking => {\n if (generation === this.generation && this.provider === provider) {\n this.thinking = thinking\n this.settings.onThinkingChange?.(thinking)\n this.updateWorkCue()\n }\n },\n onSpeakingChange: state => {\n if (generation === this.generation && this.provider === provider)\n this.setSpeaking(state)\n },\n })\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n connectPromise,\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(Object.assign(new Error('Voice connection timed out.'), { code: 'VOICE_CONNECT_TIMEOUT' })), CONNECT_TIMEOUT_MS)\n }),\n ])\n } catch (error) {\n void connectPromise.then(() => provider.disconnect()).catch(() => {})\n if (generation !== this.generation || this.provider !== provider)\n return\n this.fail(this.toSessionError(error, 'VOICE_CONNECT_FAILED'))\n return\n } finally {\n if (timeoutId)\n clearTimeout(timeoutId)\n }\n if (generation !== this.generation || this.provider !== provider)\n return\n\n this.startLevelTimer(provider, generation)\n this.liveAtMs = Date.now()\n this.setState('live')\n this.updateWorkCue()\n this.emitMetric('voice.ready', this.liveAtMs - this.startAtMs)\n this.playChime('start')\n }\n\n /** True while a live provider can accept typed text as a spoken turn. */\n get canSendUserMessage(): boolean {\n return this.state.value === 'live' && !!this.provider?.sendUserMessage\n }\n\n sendUserMessage(text: string): boolean {\n const body = text.trim()\n if (!body || !this.canSendUserMessage)\n return false\n this.provider!.sendUserMessage!(body)\n return true\n }\n\n /** Returns the applied state, or undefined when mute is unavailable or fails. */\n async toggleMuted(): Promise<boolean | undefined> {\n const provider = this.provider\n if (this.state.value !== 'live' || !provider?.setMuted || this.mutePending)\n return undefined\n const generation = this.generation\n const next = !this.isMuted.value\n this.mutePending = true\n try {\n await provider.setMuted(next)\n if (generation !== this.generation || this.provider !== provider || this.state.value !== 'live')\n return undefined\n this.isMuted.value = next\n // Mute clears the caller's activity and nothing else: working, thinking,\n // and playback carry on.\n if (next && this.speaking.user)\n this.setSpeaking({ ...this.speaking, user: false })\n return next\n }\n catch {\n return undefined\n }\n finally {\n this.mutePending = false\n }\n }\n\n private emitMetric(name: VoiceMetricName, durationMs: number, details: Pick<VoiceMetric, 'phase' | 'code'> = {}): void {\n const metric: VoiceMetric = {\n name,\n durationMs: Math.max(0, Math.round(durationMs)),\n ...(this.providerName ? { provider: this.providerName } : {}),\n ...details,\n }\n this.settings.onMetric?.(metric)\n this.postMetric(metric)\n }\n\n private setState(state: VoiceSessionClientState): void {\n if (this.state.value === state)\n return\n this.state.value = state\n this.settings.onStateChange?.(state)\n }\n\n private fail(error: VoiceSessionError): void {\n const shouldChime = this.state.value === 'live'\n if (this.startAtMs > 0) {\n this.emitMetric('voice.failed', Date.now() - this.startAtMs, {\n phase: this.state.value,\n code: error.code,\n })\n }\n this.generation++\n if (shouldChime)\n this.playChime('end')\n this.settings.onError?.(error)\n this.settings.onEnded?.(error.code === 'VOICE_CONNECTION_LOST' ? 'network' : 'unavailable')\n this.setState('ended')\n void this.teardown()\n }\n\n private teardown(): Promise<void> {\n if (this.teardownPromise)\n return this.teardownPromise\n this.workAbort?.abort()\n this.workAbort = undefined\n const hadWork = !!this.workState.activeTurnId\n this.workState = createVoiceWorkState()\n this.thinking = false\n if (hadWork)\n this.settings.onWorkChange?.({ active: false })\n this.setSpeaking({ user: false, bot: false })\n this.settings.onThinkingChange?.(false)\n this.workCueWanted = false\n if (this.workCuePending !== undefined) {\n clearTimeout(this.workCuePending)\n this.workCuePending = undefined\n }\n this.setWorkCue(false)\n if (this.levelTimer !== undefined) {\n clearInterval(this.levelTimer)\n this.levelTimer = undefined\n }\n const provider = this.provider\n this.provider = undefined\n this.isMuted.value = false\n this.settings.onLevel?.({ source: 'playback', level: 0 })\n this.teardownPromise = provider?.disconnect().catch(() => {}) ?? Promise.resolve()\n return this.teardownPromise\n }\n\n private startLevelTimer(provider: VoiceProviderClient, generation: number): void {\n if (this.levelTimer !== undefined)\n return\n this.levelTimer = setInterval(() => {\n if (generation !== this.generation || this.provider !== provider)\n return\n // The transport's speaker state picks the meter: the mic analyser hears\n // the speaker too, so \"loudest wins\" flickered green over the assistant.\n const level = provider.sampleLevel(this.speaking.bot ? 'playback' : this.speaking.user ? 'mic' : undefined)\n if (!level)\n return\n this.settings.onLevel?.(level)\n if (\n !this.firstPlaybackEmitted\n && level.source === 'playback'\n && isVoiceAudible(level.level)\n && this.liveAtMs > 0\n ) {\n this.firstPlaybackEmitted = true\n this.emitMetric('voice.first_playback', Date.now() - this.liveAtMs)\n }\n }, LEVEL_INTERVAL_MS)\n }\n\n private authHeaders(): Record<string, string> {\n return this.settings.token ? { Authorization: `Bearer ${this.settings.token}` } : {}\n }\n\n private postMetric(metric: VoiceMetric): void {\n void this.post<{ recorded: boolean }>({\n path: '/api/voice/metrics',\n keepalive: true,\n body: {\n ...metric,\n client: 'web',\n botId: this.settings.botId,\n ...(this.conversationId ? { conversationId: this.conversationId } : {}),\n },\n }).catch(() => {})\n }\n\n private startWorkStream(conversationId: string, generation: number): void {\n this.workAbort?.abort()\n const abort = new AbortController()\n this.workAbort = abort\n const apiBase = resolveApiBase(this.settings.apiBase, this.settings.isDev)\n const fetchFn = this.settings.fetchFn ?? fetch\n const streamUrl = this.settings.latestSequence\n ? `${apiBase}/api/conversations/${encodeURIComponent(conversationId)}/stream?since=${encodeURIComponent(this.settings.latestSequence)}`\n : `${apiBase}/api/conversations/${encodeURIComponent(conversationId)}/stream`\n void (async () => {\n const response = await fetchFn(streamUrl, {\n headers: { Accept: 'text/event-stream', ...this.authHeaders() },\n credentials: 'include',\n signal: abort.signal,\n })\n if (!response.ok || !response.body)\n return\n for await (const event of readChatEventStream(response.body.getReader())) {\n if (abort.signal.aborted || generation !== this.generation)\n return\n const previous = this.workState\n const next = reduceVoiceWorkEvent(previous, event)\n if (next === previous)\n continue\n this.workState = next\n if (next.activeTurnId !== previous.activeTurnId || next.description !== previous.description) {\n this.settings.onWorkChange?.({\n active: !!next.activeTurnId,\n ...(next.description ? { description: next.description } : {}),\n })\n this.updateWorkCue()\n }\n }\n })().catch(() => {})\n }\n\n private setSpeaking(next: VoiceSpeakingState): void {\n const applied = { user: next.user && !this.isMuted.value, bot: next.bot }\n if (applied.user === this.speaking.user && applied.bot === this.speaking.bot)\n return\n this.speaking = applied\n this.settings.onSpeakingChange?.(applied)\n this.updateWorkCue()\n }\n\n /**\n * Duck on speech, not on energy: room tone kept the loop from ever\n * starting. The loop starts only once waiting outlives the dwell — a blip\n * shorter than that is a normal turn and stays silent.\n */\n private updateWorkCue(): void {\n const wanted = this.state.value === 'live'\n && (this.thinking || !!this.workState.activeTurnId)\n && !this.speaking.user\n && !this.speaking.bot\n if (wanted === this.workCueWanted)\n return\n this.workCueWanted = wanted\n if (this.workCuePending !== undefined) {\n clearTimeout(this.workCuePending)\n this.workCuePending = undefined\n }\n if (wanted) {\n const generation = this.generation\n this.workCuePending = setTimeout(() => {\n this.workCuePending = undefined\n if (generation === this.generation && this.workCueWanted)\n this.setWorkCue(true)\n }, this.settings.workCueDelayMs ?? WORK_CUE_DELAY_MS)\n return\n }\n this.setWorkCue(false)\n }\n\n private setWorkCue(active: boolean): void {\n if (this.workCuePlaying === active)\n return\n this.workCuePlaying = active\n this.ensureWorkCue().setActive(active)\n }\n\n private ensureWorkCue(): { setActive: (active: boolean) => void, unlock: () => void } {\n this.workCue ??= createBrowserVoiceWorkCue(resolveApiBase(this.settings.apiBase, this.settings.isDev))\n return this.workCue\n }\n\n private ensureChime(): VoiceChimePlayer {\n this.chime ??= createBrowserVoiceChimePlayer(resolveApiBase(this.settings.apiBase, this.settings.isDev))\n return this.chime\n }\n\n private playChime(kind: VoiceChimeKind): void {\n this.ensureChime().play(kind)\n }\n\n private async post<T>(args: { path: string, body: unknown, keepalive?: boolean }): Promise<T> {\n const apiBase = resolveApiBase(this.settings.apiBase, this.settings.isDev)\n const fetchFn = this.settings.fetchFn ?? fetch\n const res = await fetchFn(`${apiBase}${args.path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.authHeaders() },\n credentials: 'include',\n keepalive: args.keepalive,\n body: JSON.stringify(args.body),\n })\n const result = await res.json() as ApiResponse<T>\n if (!res.ok || !result.ok) {\n const message = !result.ok ? result.error : `HTTP ${res.status}`\n const code = !result.ok && result.code ? result.code : 'VOICE_MINT_FAILED'\n throw Object.assign(new Error(message || 'Voice request failed.'), { code })\n }\n return result.data\n }\n\n private async mintSession(generation: number): Promise<VoiceSessionMint | null> {\n let conversationId = this.settings.conversationId\n if (!conversationId) {\n conversationId = (await this.post<{ conversationId: string }>({\n path: '/api/conversations',\n body: {\n orgId: this.settings.orgId,\n participants: [{ type: 'bot', id: this.settings.botId }],\n },\n })).conversationId\n if (generation !== this.generation)\n return null\n }\n\n const viewerTimezone = detectBrowserTimezone()\n const data = await this.post<unknown>({\n path: '/api/voice/sessions',\n body: { conversationId, ...(viewerTimezone ? { viewerTimezone } : {}) },\n })\n if (generation !== this.generation)\n return null\n const parsed = zVoiceSessionMint.safeParse(data)\n if (!parsed.success)\n throw Object.assign(new Error('Voice session mint returned an unexpected shape.'), { code: 'VOICE_MINT_INVALID' })\n this.conversationId = conversationId\n return parsed.data\n }\n\n private toSessionError(error: unknown, fallbackCode: string): VoiceSessionError {\n return error instanceof Error\n ? {\n code: 'code' in error ? String((error as Error & { code: unknown }).code) : fallbackCode,\n message: error.message,\n }\n : { code: fallbackCode, message: 'Voice session failed to start.' }\n }\n}\n"],"mappings":"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"}
package/dist/sdk.css CHANGED
@@ -1,2 +1,2 @@
1
- :where(.spinner){color:var(--color-theme-300)}.ring-circular{animation:1s linear infinite rotate}.ring-path{will-change:stroke-dasharray;will-change:stroke-dashoffset;stroke-dasharray:1 200;stroke-dashoffset:0;stroke-linecap:round;animation:2s ease-in-out infinite dash}@keyframes rotate{to{transform:rotate(360deg)}}@keyframes dash{0%{stroke-dasharray:1 200;stroke-dashoffset:0}50%{stroke-dasharray:89 200;stroke-dashoffset:-35px}to{stroke-dasharray:89 200;stroke-dashoffset:-124px}}.voice-call-enter-active[data-v-4c8d65fc]{transition:opacity .3s cubic-bezier(.25,1,.33,1)}.voice-call-leave-active[data-v-4c8d65fc]{transition:opacity .2s cubic-bezier(.5,0,.75,0)}.voice-call-enter-active .voice-call-card[data-v-4c8d65fc]{transition:transform .42s cubic-bezier(.25,1,.33,1)}.voice-call-leave-active .voice-call-card[data-v-4c8d65fc]{transition:transform .2s cubic-bezier(.5,0,.75,0)}.voice-call-enter-from[data-v-4c8d65fc],.voice-call-leave-to[data-v-4c8d65fc]{opacity:0}.voice-call-enter-from .voice-call-card[data-v-4c8d65fc]{transform:translateY(32px)}.voice-call-leave-to .voice-call-card[data-v-4c8d65fc]{transform:translateY(12px)}@media (prefers-reduced-motion:reduce){.voice-call-enter-active[data-v-4c8d65fc],.voice-call-leave-active[data-v-4c8d65fc],.voice-call-enter-active .voice-call-card[data-v-4c8d65fc],.voice-call-leave-active .voice-call-card[data-v-4c8d65fc]{transition-duration:1ms}.voice-call-enter-from .voice-call-card[data-v-4c8d65fc],.voice-call-leave-to .voice-call-card[data-v-4c8d65fc]{transform:none}}.nlist-enter-active,.nlist-leave-active,.nlist-move{transition:all .6s cubic-bezier(.68,-.55,.27,1.55)}.nlist-enter-from{opacity:0;transform:translate(-30px)scale(.9)}.nlist-enter-to{opacity:1;transform:translate(0)scale(1)}.nlist-leave-active{position:absolute}.nlist-leave-to{opacity:0;transform:scale(.9)}.widget-container[data-v-5674cae2]{transition:width .4s cubic-bezier(.32,.72,0,1),height .4s cubic-bezier(.32,.72,0,1),border-radius .4s cubic-bezier(.32,.72,0,1),box-shadow .4s cubic-bezier(.32,.72,0,1)}
1
+ :where(.spinner){color:var(--color-theme-300)}.ring-circular{animation:1s linear infinite rotate}.ring-path{will-change:stroke-dasharray;will-change:stroke-dashoffset;stroke-dasharray:1 200;stroke-dashoffset:0;stroke-linecap:round;animation:2s ease-in-out infinite dash}@keyframes rotate{to{transform:rotate(360deg)}}@keyframes dash{0%{stroke-dasharray:1 200;stroke-dashoffset:0}50%{stroke-dasharray:89 200;stroke-dashoffset:-35px}to{stroke-dasharray:89 200;stroke-dashoffset:-124px}}.voice-call-enter-active[data-v-f60c6cac]{transition:opacity .3s cubic-bezier(.25,1,.33,1)}.voice-call-leave-active[data-v-f60c6cac]{transition:opacity .2s cubic-bezier(.5,0,.75,0)}.voice-call-enter-active .voice-call-card[data-v-f60c6cac]{transition:transform .42s cubic-bezier(.25,1,.33,1)}.voice-call-leave-active .voice-call-card[data-v-f60c6cac]{transition:transform .2s cubic-bezier(.5,0,.75,0)}.voice-call-enter-from[data-v-f60c6cac],.voice-call-leave-to[data-v-f60c6cac]{opacity:0}.voice-call-enter-from .voice-call-card[data-v-f60c6cac]{transform:translateY(32px)}.voice-call-leave-to .voice-call-card[data-v-f60c6cac]{transform:translateY(12px)}@media (prefers-reduced-motion:reduce){.voice-call-enter-active[data-v-f60c6cac],.voice-call-leave-active[data-v-f60c6cac],.voice-call-enter-active .voice-call-card[data-v-f60c6cac],.voice-call-leave-active .voice-call-card[data-v-f60c6cac]{transition-duration:1ms}.voice-call-enter-from .voice-call-card[data-v-f60c6cac],.voice-call-leave-to .voice-call-card[data-v-f60c6cac]{transform:none}}.nlist-enter-active,.nlist-leave-active,.nlist-move{transition:all .6s cubic-bezier(.68,-.55,.27,1.55)}.nlist-enter-from{opacity:0;transform:translate(-30px)scale(.9)}.nlist-enter-to{opacity:1;transform:translate(0)scale(1)}.nlist-leave-active{position:absolute}.nlist-leave-to{opacity:0;transform:scale(.9)}.widget-container[data-v-5674cae2]{transition:width .4s cubic-bezier(.32,.72,0,1),height .4s cubic-bezier(.32,.72,0,1),border-radius .4s cubic-bezier(.32,.72,0,1),box-shadow .4s cubic-bezier(.32,.72,0,1)}
2
2
  /*$vite$:1*/
package/dist/widget.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "./rolldown-runtime.js";
2
2
  import { c as e, l as t, t as n } from "./sdkClient.js";
3
- import { c as r, h as i, l as a, n as o, p as s, s as c, t as l, u } from "./BotWrap.js";
3
+ import { _ as r, d as i, f as a, m as o, n as s, p as c, t as l, y as u } from "./BotWrap.js";
4
4
  import { t as d } from "./useWidgetState.js";
5
5
  import { PAGELINES_APP_CONFIG as f, createLogger as p, zChatAttachmentInput as m } from "@pagelines/core";
6
6
  import { Fragment as h, computed as g, createApp as _, createBlock as v, createCommentVNode as y, createElementBlock as b, createElementVNode as x, createStaticVNode as S, createTextVNode as C, createVNode as w, defineComponent as T, normalizeClass as E, normalizeStyle as ee, onMounted as te, openBlock as D, ref as O, shallowRef as k, toDisplayString as A, unref as j, vModelText as ne, watch as M, withCtx as N, withDirectives as re, withKeys as P, withModifiers as ie } from "vue";
@@ -21,7 +21,7 @@ var ae = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@la
21
21
  __name: "ElAuthPanel",
22
22
  props: { sdk: {} },
23
23
  setup(e) {
24
- let t = O(!1), n = O("email"), i = O({
24
+ let t = O(!1), n = O("email"), r = O({
25
25
  email: "",
26
26
  code: ""
27
27
  }), o = O(!1);
@@ -34,7 +34,7 @@ var ae = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@la
34
34
  if (!o.value) {
35
35
  o.value = !0, e.sdk.error.value = null;
36
36
  try {
37
- await e.sdk.auth.requestAuthCode({ email: i.value.email }) && (n.value = "code");
37
+ await e.sdk.auth.requestAuthCode({ email: r.value.email }) && (n.value = "code");
38
38
  } finally {
39
39
  o.value = !1;
40
40
  }
@@ -45,8 +45,8 @@ var ae = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@la
45
45
  o.value = !0, e.sdk.error.value = null;
46
46
  try {
47
47
  await e.sdk.auth.loginWithCode({
48
- email: i.value.email,
49
- code: i.value.code
48
+ email: r.value.email,
49
+ code: r.value.code
50
50
  });
51
51
  } finally {
52
52
  o.value = !1;
@@ -73,40 +73,40 @@ var ae = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@la
73
73
  class: "w-full px-5 py-3 rounded-xl border border-theme-100 text-sm text-theme-500 transition-all duration-150 hover:border-theme-200 hover:text-theme-700 cursor-pointer",
74
74
  onClick: l[0] || (l[0] = (e) => t.value = !0)
75
75
  }, " Use email instead ")])),
76
- t.value && n.value === "email" ? (D(), b("div", B, [w(r, {
77
- modelValue: i.value.email,
78
- "onUpdate:modelValue": l[1] || (l[1] = (e) => i.value.email = e),
76
+ t.value && n.value === "email" ? (D(), b("div", B, [w(a, {
77
+ modelValue: r.value.email,
78
+ "onUpdate:modelValue": l[1] || (l[1] = (e) => r.value.email = e),
79
79
  "data-test": "auth-email-input",
80
80
  onKeyup: P(sendCode, ["enter"])
81
- }, null, 8, ["modelValue"]), w(a, {
81
+ }, null, 8, ["modelValue"]), w(c, {
82
82
  "data-test": "auth-send-code",
83
83
  theme: "dark",
84
84
  size: "md",
85
85
  class: "w-full",
86
86
  loading: o.value,
87
- disabled: !i.value.email || o.value,
87
+ disabled: !r.value.email || o.value,
88
88
  onClick: sendCode
89
89
  }, {
90
90
  default: N(() => [...l[6] || (l[6] = [C(" Send verification code ", -1)])]),
91
91
  _: 1
92
92
  }, 8, ["loading", "disabled"])])) : y("", !0),
93
93
  t.value && n.value === "code" ? (D(), b("div", V, [
94
- x("p", H, " Code sent to " + A(i.value.email), 1),
95
- w(c, {
96
- modelValue: i.value.code,
97
- "onUpdate:modelValue": l[2] || (l[2] = (e) => i.value.code = e),
94
+ x("p", H, " Code sent to " + A(r.value.email), 1),
95
+ w(i, {
96
+ modelValue: r.value.code,
97
+ "onUpdate:modelValue": l[2] || (l[2] = (e) => r.value.code = e),
98
98
  length: 6,
99
99
  "focus-first": !0,
100
100
  "data-test": "auth-code-input",
101
101
  onAutoSubmit: verifyCode
102
102
  }, null, 8, ["modelValue"]),
103
- w(a, {
103
+ w(c, {
104
104
  "data-test": "auth-verify-code",
105
105
  theme: "dark",
106
106
  size: "md",
107
107
  class: "w-full",
108
108
  loading: o.value,
109
- disabled: i.value.code.length !== 6 || o.value,
109
+ disabled: r.value.code.length !== 6 || o.value,
110
110
  onClick: verifyCode
111
111
  }, {
112
112
  default: N(() => [...l[7] || (l[7] = [C(" Verify ", -1)])]),
@@ -224,18 +224,18 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
224
224
  onClose: { type: Function }
225
225
  },
226
226
  setup(e) {
227
- let r = e, i = r.sdk || n.getInstance({ isDev: typeof window < "u" ? window.location.hostname === "localhost" || window.location.hostname.includes("127.0.0.1") : !1 }), a = k(), c = O(!1), l = O(), u = O(!1), d = g(() => a.value ?? i.currentBot.value), f = k(), p = k();
227
+ let i = e, a = i.sdk || n.getInstance({ isDev: typeof window < "u" ? window.location.hostname === "localhost" || window.location.hostname.includes("127.0.0.1") : !1 }), o = k(), c = O(!1), l = O(), u = O(!1), d = g(() => o.value ?? a.currentBot.value), f = k(), p = k();
228
228
  M(() => d.value?.botId.value, (e) => {
229
229
  if (!e || !d.value) {
230
230
  f.value = void 0, p.value = void 0;
231
231
  return;
232
232
  }
233
233
  let t = d.value;
234
- f.value = t, p.value = new s({
235
- sdk: i,
234
+ f.value = t, p.value = new r({
235
+ sdk: a,
236
236
  bot: t,
237
237
  chatStreamFn: async (n) => {
238
- await i.chat.chatStreamAuthenticated({
238
+ await a.chat.chatStreamAuthenticated({
239
239
  botId: e,
240
240
  orgId: t.computer.orgId.value,
241
241
  message: n.message,
@@ -253,14 +253,14 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
253
253
  });
254
254
  }, { immediate: !0 });
255
255
  let h = g(() => deriveOnboardMode({
256
- hasToken: !!i.token.value,
256
+ hasToken: !!a.token.value,
257
257
  hasBot: !!d.value,
258
258
  isNewBot: u.value,
259
259
  botReady: c.value,
260
260
  provisionError: l.value
261
- })), _ = g(() => `${t(i.apiBase, i.isDev)}/`);
261
+ })), _ = g(() => `${t(a.apiBase, a.isDev)}/`);
262
262
  function onBotCreated(e) {
263
- a.value = e, l.value = void 0, c.value = !1, u.value = !0;
263
+ o.value = e, l.value = void 0, c.value = !1, u.value = !0;
264
264
  }
265
265
  function onProvisionReady() {
266
266
  c.value = !0;
@@ -276,10 +276,10 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
276
276
  "data-test": "onboard-widget",
277
277
  "data-test-state": h.value
278
278
  }, [
279
- x("div", pe, [x("span", me, A(d.value?.name.value || "PageLines"), 1), r.onClose ? (D(), b("button", {
279
+ x("div", pe, [x("span", me, A(d.value?.name.value || "PageLines"), 1), i.onClose ? (D(), b("button", {
280
280
  key: 0,
281
281
  class: "text-theme-400 hover:text-theme-600 transition-colors cursor-pointer",
282
- onClick: t[0] || (t[0] = (e) => r.onClose?.())
282
+ onClick: t[0] || (t[0] = (e) => i.onClose?.())
283
283
  }, [...t[1] || (t[1] = [x("svg", {
284
284
  xmlns: "http://www.w3.org/2000/svg",
285
285
  class: "size-5",
@@ -293,21 +293,21 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
293
293
  x("div", he, [
294
294
  h.value === "auth" ? (D(), v(U, {
295
295
  key: 0,
296
- sdk: j(i)
296
+ sdk: j(a)
297
297
  }, null, 8, ["sdk"])) : y("", !0),
298
298
  h.value === "create" ? (D(), v(oe, {
299
299
  key: 1,
300
- sdk: j(i),
300
+ sdk: j(a),
301
301
  onCreated: onBotCreated
302
302
  }, null, 8, ["sdk"])) : y("", !0),
303
303
  h.value === "provisioning" && d.value ? (D(), v(de, {
304
304
  key: 2,
305
- sdk: j(i),
305
+ sdk: j(a),
306
306
  "computer-id": d.value.computerId.value,
307
307
  onReady: onProvisionReady,
308
308
  onError: onProvisionError
309
309
  }, null, 8, ["sdk", "computer-id"])) : y("", !0),
310
- h.value === "chat" && f.value && p.value ? (D(), v(o, {
310
+ h.value === "chat" && f.value && p.value ? (D(), v(s, {
311
311
  key: 3,
312
312
  "chat-controller": p.value,
313
313
  bot: f.value,
@@ -326,7 +326,7 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
326
326
  }, " Go to Dashboard → ", 8, be)])) : y("", !0)
327
327
  ], 8, fe));
328
328
  }
329
- }), xe = { class: "relative size-16 rounded-full overflow-hidden" }, Se = ["src", "alt"], Ce = { class: "flex items-center shrink-0 border-b border-theme-100 px-4 py-3" }, we = { class: "flex items-center gap-3 flex-1 min-w-0" }, Te = ["src", "alt"], Ee = { class: "min-w-0" }, X = { class: "text-sm font-medium text-theme-900 truncate" }, De = { class: "flex-1 overflow-hidden" }, Oe = { class: "shrink-0 flex items-center justify-center border-t border-theme-100 py-1.5" }, ke = ["href"], Z = /*#__PURE__*/ i(/* @__PURE__ */ T({
329
+ }), xe = { class: "relative size-16 rounded-full overflow-hidden" }, Se = ["src", "alt"], Ce = { class: "flex items-center shrink-0 border-b border-theme-100 px-4 py-3" }, we = { class: "flex items-center gap-3 flex-1 min-w-0" }, Te = ["src", "alt"], Ee = { class: "min-w-0" }, X = { class: "text-sm font-medium text-theme-900 truncate" }, De = { class: "flex-1 overflow-hidden" }, Oe = { class: "shrink-0 flex items-center justify-center border-t border-theme-100 py-1.5" }, ke = ["href"], Z = /*#__PURE__*/ u(/* @__PURE__ */ T({
330
330
  __name: "BotWidgetPopup",
331
331
  props: {
332
332
  sdk: {},
@@ -338,7 +338,7 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
338
338
  chatController: {}
339
339
  },
340
340
  setup(e, { expose: t }) {
341
- let n = e, r = O(!1), i = O(!1), a = k(), { currentBot: c, currentContext: p, currentFirstMessage: m, update: h } = d(n), _ = g(() => ({
341
+ let n = e, i = O(!1), a = O(!1), c = k(), { currentBot: u, currentContext: p, currentFirstMessage: m, update: h } = d(n), _ = g(() => ({
342
342
  "bottom-left": "bottom-4 left-4 sm:bottom-6 sm:left-6",
343
343
  "top-right": "top-4 right-4 sm:top-6 sm:right-6",
344
344
  "top-left": "top-4 left-4 sm:top-6 sm:left-6",
@@ -355,17 +355,17 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
355
355
  "bottom-right": "bottom-0 right-0"
356
356
  })[n.position || "bottom-right"]);
357
357
  function initController(e, t) {
358
- n.chatController || a.value || (a.value = new s({
358
+ n.chatController || c.value || (c.value = new r({
359
359
  sdk: t,
360
360
  bot: e,
361
361
  context: p.value,
362
362
  firstMessage: m.value
363
363
  }));
364
364
  }
365
- M(r, async (e) => {
366
- if (!e || i.value) return;
367
- i.value = !0;
368
- let t = n.chatController || a.value;
365
+ M(i, async (e) => {
366
+ if (!e || a.value) return;
367
+ a.value = !0;
368
+ let t = n.chatController || c.value;
369
369
  if (t) try {
370
370
  await t.startTextConversation();
371
371
  } catch (e) {
@@ -373,13 +373,13 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
373
373
  }
374
374
  });
375
375
  function toggle() {
376
- r.value = !r.value;
376
+ i.value = !i.value;
377
377
  }
378
378
  function open() {
379
- r.value = !0;
379
+ i.value = !0;
380
380
  }
381
381
  function close() {
382
- r.value = !1;
382
+ i.value = !1;
383
383
  }
384
384
  return t({
385
385
  toggle,
@@ -389,28 +389,28 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
389
389
  }), (t, n) => (D(), v(l, {
390
390
  sdk: e.sdk,
391
391
  handle: e.handle,
392
- bot: j(c),
392
+ bot: j(u),
393
393
  context: j(p),
394
394
  "first-message": j(m)
395
395
  }, {
396
- default: N(({ bot: t, sdk: s }) => [t ? (D(), b("div", {
396
+ default: N(({ bot: t, sdk: r }) => [t ? (D(), b("div", {
397
397
  key: 0,
398
- ref: () => initController(t, s),
398
+ ref: () => initController(t, r),
399
399
  class: E(["fixed z-[999999] pointer-events-auto", _.value])
400
400
  }, [x("div", {
401
- class: E(["widget-container transform-gpu overflow-hidden", r.value ? "w-[min(400px,calc(100vw-2rem))] h-[min(600px,calc(100vh-4rem))] shadow-[0_24px_80px_-12px_rgba(0,0,0,0.2),0_8px_24px_-8px_rgba(0,0,0,0.08)]" : "w-16 h-16 shadow-[0_8px_32px_-4px_rgba(0,0,0,0.15)] cursor-pointer"]),
401
+ class: E(["widget-container transform-gpu overflow-hidden", i.value ? "w-[min(400px,calc(100vw-2rem))] h-[min(600px,calc(100vh-4rem))] shadow-[0_24px_80px_-12px_rgba(0,0,0,0.2),0_8px_24px_-8px_rgba(0,0,0,0.08)]" : "w-16 h-16 shadow-[0_8px_32px_-4px_rgba(0,0,0,0.15)] cursor-pointer"]),
402
402
  style: ee({
403
- "border-radius": r.value ? "16px" : "32px",
403
+ "border-radius": i.value ? "16px" : "32px",
404
404
  "transform-origin": S.value
405
405
  }),
406
- onClick: n[0] || (n[0] = (e) => r.value ? void 0 : toggle())
407
- }, [x("div", { class: E(["absolute size-16 transition-opacity duration-200", [C.value, r.value ? "opacity-0 pointer-events-none" : "opacity-100"]]) }, [x("div", xe, [x("img", {
408
- src: j(u)(t.toData()),
406
+ onClick: n[0] || (n[0] = (e) => i.value ? void 0 : toggle())
407
+ }, [x("div", { class: E(["absolute size-16 transition-opacity duration-200", [C.value, i.value ? "opacity-0 pointer-events-none" : "opacity-100"]]) }, [x("div", xe, [x("img", {
408
+ src: j(o)(t.toData()),
409
409
  alt: `${t.name.value} bot`,
410
410
  class: "h-full w-full object-cover pointer-events-none",
411
411
  width: "64",
412
412
  height: "64"
413
- }, null, 8, Se), n[1] || (n[1] = x("div", { class: "absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60" }, null, -1))])], 2), x("div", { class: E(["absolute inset-0 flex flex-col bg-white transition-opacity duration-250", r.value ? "opacity-100" : "opacity-0 pointer-events-none"]) }, [
413
+ }, null, 8, Se), n[1] || (n[1] = x("div", { class: "absolute inset-0 pointer-events-none bg-gradient-to-br from-transparent via-black/10 to-black/40 opacity-60" }, null, -1))])], 2), x("div", { class: E(["absolute inset-0 flex flex-col bg-white transition-opacity duration-250", i.value ? "opacity-100" : "opacity-0 pointer-events-none"]) }, [
414
414
  x("div", Ce, [x("div", we, [x("img", {
415
415
  src: t.avatarUrl.value,
416
416
  alt: t.name.value,
@@ -419,9 +419,9 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
419
419
  class: "cursor-pointer flex items-center justify-center p-2 -mr-1 rounded-xl text-theme-400 hover:text-theme-600 hover:bg-theme-50 transition-colors",
420
420
  onClick: ie(close, ["stop"])
421
421
  }, [...n[2] || (n[2] = [x("i", { class: "size-5 i-tabler-x" }, null, -1)])])]),
422
- x("div", De, [i.value ? (D(), v(o, {
422
+ x("div", De, [a.value ? (D(), v(s, {
423
423
  key: 0,
424
- "chat-controller": e.chatController || a.value,
424
+ "chat-controller": e.chatController || c.value,
425
425
  bot: t,
426
426
  variant: "light",
427
427
  "setup-hint": "",
@@ -435,7 +435,7 @@ var fe = ["data-test-state"], pe = { class: "flex items-center justify-between p
435
435
  }, " Learning Bot™ by PageLines ", 8, ke)])
436
436
  ], 2)], 6), t.desiredStatus.value === "active" ? (D(), b("div", {
437
437
  key: 0,
438
- class: E(["absolute top-[5%] right-[5%] transition-opacity duration-200 pointer-events-none", r.value ? "opacity-0" : "opacity-100"])
438
+ class: E(["absolute top-[5%] right-[5%] transition-opacity duration-200 pointer-events-none", i.value ? "opacity-0" : "opacity-100"])
439
439
  }, [...n[3] || (n[3] = [x("div", {
440
440
  class: "size-3 bg-green-500 rounded-full ring-2 ring-white absolute animate-ping",
441
441
  style: { "animation-duration": "5s" }