@convai/web-sdk 1.8.0-beta.4 → 1.8.0-beta.6
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/README.md +64 -3
- package/dist/core/CharacterRoster.d.ts +84 -0
- package/dist/core/CharacterRoster.d.ts.map +1 -0
- package/dist/core/CharacterRoster.js +348 -0
- package/dist/core/CharacterRoster.js.map +1 -0
- package/dist/core/CharacterVersionManager.d.ts +100 -0
- package/dist/core/CharacterVersionManager.d.ts.map +1 -0
- package/dist/core/CharacterVersionManager.js +227 -0
- package/dist/core/CharacterVersionManager.js.map +1 -0
- package/dist/core/ConvaiClient.d.ts +109 -1
- package/dist/core/ConvaiClient.d.ts.map +1 -1
- package/dist/core/ConvaiClient.js +642 -49
- package/dist/core/ConvaiClient.js.map +1 -1
- package/dist/core/ConvaiRoomError.d.ts +48 -0
- package/dist/core/ConvaiRoomError.d.ts.map +1 -0
- package/dist/core/ConvaiRoomError.js +111 -0
- package/dist/core/ConvaiRoomError.js.map +1 -0
- package/dist/core/MessageHandler.d.ts +14 -0
- package/dist/core/MessageHandler.d.ts.map +1 -1
- package/dist/core/MessageHandler.js +111 -5
- package/dist/core/MessageHandler.js.map +1 -1
- package/dist/core/SSESession.d.ts +15 -0
- package/dist/core/SSESession.d.ts.map +1 -1
- package/dist/core/SSESession.js +92 -3
- package/dist/core/SSESession.js.map +1 -1
- package/dist/core/characterReference.d.ts +40 -0
- package/dist/core/characterReference.d.ts.map +1 -0
- package/dist/core/characterReference.js +72 -0
- package/dist/core/characterReference.js.map +1 -0
- package/dist/core/connectRequest.d.ts +1 -0
- package/dist/core/connectRequest.d.ts.map +1 -1
- package/dist/core/connectRequest.js +11 -0
- package/dist/core/connectRequest.js.map +1 -1
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/rosterRequest.d.ts +57 -0
- package/dist/core/rosterRequest.d.ts.map +1 -0
- package/dist/core/rosterRequest.js +210 -0
- package/dist/core/rosterRequest.js.map +1 -0
- package/dist/core/types.d.ts +717 -7
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/react/components/rtc-widget/components/MarkdownRenderer.d.ts.map +1 -1
- package/dist/react/components/rtc-widget/components/MarkdownRenderer.js +39 -44
- package/dist/react/components/rtc-widget/components/MarkdownRenderer.js.map +1 -1
- package/dist/react/hooks/useConvaiClient.d.ts.map +1 -1
- package/dist/react/hooks/useConvaiClient.js +15 -2
- package/dist/react/hooks/useConvaiClient.js.map +1 -1
- package/dist/utils/inlineMarkdown.d.ts +38 -0
- package/dist/utils/inlineMarkdown.d.ts.map +1 -0
- package/dist/utils/inlineMarkdown.js +106 -0
- package/dist/utils/inlineMarkdown.js.map +1 -0
- package/dist/vanilla/AudioRenderer.d.ts.map +1 -1
- package/dist/vanilla/AudioRenderer.js +6 -0
- package/dist/vanilla/AudioRenderer.js.map +1 -1
- package/dist/vanilla/ConvaiWidget.d.ts.map +1 -1
- package/dist/vanilla/ConvaiWidget.js +71 -57
- package/dist/vanilla/ConvaiWidget.js.map +1 -1
- package/dist/vanilla/index.d.ts +2 -0
- package/dist/vanilla/index.d.ts.map +1 -1
- package/dist/vanilla/index.js +2 -0
- package/dist/vanilla/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -27,8 +27,10 @@ import { ConvaiClient as CoreConvaiClient } from "../../core/index.js";
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
export const useConvaiClient = (config) => {
|
|
30
|
-
// Create client instance once
|
|
31
|
-
|
|
30
|
+
// Create client instance once. The config is handed to the client as well
|
|
31
|
+
// as kept in the hook, so config-derived surfaces such as
|
|
32
|
+
// `characterVersions` are usable before the first connect().
|
|
33
|
+
const [client] = useState(() => new CoreConvaiClient(config));
|
|
32
34
|
// React state for UI updates
|
|
33
35
|
const [state, setState] = useState(client.state);
|
|
34
36
|
const [activity, setActivity] = useState("Idle");
|
|
@@ -166,8 +168,18 @@ export const useConvaiClient = (config) => {
|
|
|
166
168
|
...(client.apiKey && { apiKey: client.apiKey }),
|
|
167
169
|
...(client.authToken && { authToken: client.authToken }),
|
|
168
170
|
characterId: client.characterId,
|
|
171
|
+
characterVersion: client.characterVersion,
|
|
172
|
+
characterReference: client.characterReference,
|
|
169
173
|
connect,
|
|
170
174
|
connectWithConnectionData: client.connectWithConnectionData.bind(client),
|
|
175
|
+
joinRoom: client.joinRoom.bind(client),
|
|
176
|
+
appendMessage: client.appendMessage.bind(client),
|
|
177
|
+
toggleUsageUpdates: client.toggleUsageUpdates.bind(client),
|
|
178
|
+
setInteractionTarget: client.setInteractionTarget.bind(client),
|
|
179
|
+
updateCharacterRoster: client.updateCharacterRoster.bind(client),
|
|
180
|
+
roomSession: client.roomSession,
|
|
181
|
+
characters: client.characters,
|
|
182
|
+
activeMembershipId: client.activeMembershipId,
|
|
171
183
|
disconnect: client.disconnect.bind(client),
|
|
172
184
|
reconnect,
|
|
173
185
|
resetSession: client.resetSession.bind(client),
|
|
@@ -195,6 +207,7 @@ export const useConvaiClient = (config) => {
|
|
|
195
207
|
blendshapeQueue: client.blendshapeQueue,
|
|
196
208
|
conversationSessionId: client.conversationSessionId,
|
|
197
209
|
memoryManager: client.memoryManager,
|
|
210
|
+
characterVersions: client.characterVersions,
|
|
198
211
|
toggleTts: client.toggleTts.bind(client),
|
|
199
212
|
toggleStt: client.toggleStt.bind(client),
|
|
200
213
|
uploadFile: client.uploadFile.bind(client),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConvaiClient.js","sourceRoot":"","sources":["../../../src/react/hooks/useConvaiClient.ts"],"names":[],"mappings":"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","sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { ConvaiClient as CoreConvaiClient } from \"../../core\";\nimport type {\n ConvaiConfig,\n IConvaiClient,\n ChatMessage,\n} from \"../../core/types\";\n\n/**\n * React hook wrapper for ConvaiClient\n * Main hook for managing Convai client connection and functionality.\n *\n * Provides a complete interface for connecting to Convai's AI-powered voice assistants,\n * managing real-time audio/video conversations, and handling various message types.\n *\n * @param config - Optional configuration to store for later connection\n * @returns {ConvaiClient & { activity: string; chatMessages: ChatMessage[] }} Complete client interface with connection state, methods, and message history\n *\n * @example\n * ```tsx\n * function App() {\n * const convaiClient = useConvaiClient({\n * apiKey: 'your-api-key',\n * characterId: 'your-character-id',\n * endUserId: 'user-uuid', // Optional: enables memory & analytics\n * enableVideo: true,\n * });\n *\n * return (\n * <ConvaiWidget convaiClient={convaiClient} />\n * );\n * }\n * ```\n */\nexport const useConvaiClient = (\n config?: ConvaiConfig,\n): IConvaiClient & {\n activity: string;\n chatMessages: ChatMessage[];\n isAudioMuted: boolean;\n isVideoEnabled: boolean;\n isScreenShareActive: boolean;\n} => {\n // Create client instance once\n const [client] = useState(() => new CoreConvaiClient());\n\n // React state for UI updates\n const [state, setState] = useState(client.state);\n const [activity, setActivity] = useState<string>(\"Idle\");\n const [chatMessages, setChatMessages] = useState<ChatMessage[]>(\n client.chatMessages,\n );\n const [userTranscription, setUserTranscription] = useState<string>(\n client.userTranscription,\n );\n const [isSpeaking, setIsSpeaking] = useState<boolean>(false);\n const [isBotReady, setIsBotReady] = useState<boolean>(client.isBotReady);\n \n // Reactive states for control managers\n const [isAudioMuted, setIsAudioMuted] = useState<boolean>(\n client.audioControls.isAudioMuted\n );\n const [isVideoEnabled, setIsVideoEnabled] = useState<boolean>(\n client.videoControls.isVideoEnabled\n );\n const [isScreenShareActive, setIsScreenShareActive] = useState<boolean>(\n client.screenShareControls.isScreenShareActive\n );\n\n // Store config if provided\n const [storedConfig] = useState<ConvaiConfig | undefined>(config);\n\n // Setup event listeners\n useEffect(() => {\n // State change listener\n const unsubStateChange = client.on(\"stateChange\", (newState) => {\n setState(newState);\n\n // Update activity based on state (priority: speaking > listening > thinking > connected)\n if (!newState.isConnected) {\n setActivity(\"Disconnected\");\n } else if (newState.isConnecting) {\n setActivity(\"Connecting...\");\n } else if (newState.isSpeaking) {\n setActivity(\"Speaking\");\n } else if (newState.isListening) {\n setActivity(\"Listening\");\n } else if (newState.isThinking) {\n setActivity(\"Thinking\");\n } else {\n setActivity(\"Connected\");\n }\n });\n\n // Messages change listener\n const unsubMessagesChange = client.on(\"messagesChange\", (messages) => {\n setChatMessages([...messages]);\n });\n\n // User transcription change listener\n const unsubUserTranscription = client.on(\n \"userTranscriptionChange\",\n (transcription) => {\n setUserTranscription(transcription);\n },\n );\n\n // Speaking change listener\n const unsubSpeaking = client.on(\"speakingChange\", (speaking) => {\n setIsSpeaking(speaking);\n });\n\n // Bot ready listener\n const unsubBotReady = client.on(\"botReady\", () => {\n setIsBotReady(true);\n setActivity(\"Connected\");\n });\n\n // Connect listener\n const unsubConnect = client.on(\"connect\", () => {\n setState(client.state);\n });\n\n // Disconnect listener\n const unsubDisconnect = client.on(\"disconnect\", () => {\n setIsBotReady(false);\n setState(client.state);\n setActivity(\"Disconnected\");\n setChatMessages([]);\n // Reset control states on disconnect\n setIsAudioMuted(true);\n setIsVideoEnabled(false);\n setIsScreenShareActive(false);\n });\n\n // WebSocket transport: play bot audio track via a hidden <audio> element\n const wsAudioEl = document.createElement(\"audio\");\n wsAudioEl.autoplay = true;\n wsAudioEl.dataset.convaiWs = \"true\";\n document.body.appendChild(wsAudioEl);\n\n const unsubBotAudioTrack = client.on(\n \"botAudioTrack\",\n (track: MediaStreamTrack) => {\n wsAudioEl.srcObject = new MediaStream([track]);\n },\n );\n\n // Disconnect listener also clears WS audio\n const unsubDisconnectWsAudio = client.on(\"disconnect\", () => {\n wsAudioEl.srcObject = null;\n });\n\n // Audio controls state change listener\n const unsubAudioState = client.audioControls.on(\"audioStateChange\", (audioState: any) => {\n if (audioState.isAudioMuted !== undefined) {\n setIsAudioMuted(audioState.isAudioMuted);\n }\n });\n\n // Video controls state change listener\n const unsubVideoState = client.videoControls.on(\"videoStateChange\", (videoState: any) => {\n if (videoState.isVideoEnabled !== undefined) {\n setIsVideoEnabled(videoState.isVideoEnabled);\n }\n });\n\n // Screen share controls state change listener\n const unsubScreenShareState = client.screenShareControls.on(\"screenShareStateChange\", (screenShareState: any) => {\n if (screenShareState.isScreenShareActive !== undefined) {\n setIsScreenShareActive(screenShareState.isScreenShareActive);\n }\n });\n\n // Cleanup listeners on unmount\n return () => {\n unsubStateChange();\n unsubMessagesChange();\n unsubUserTranscription();\n unsubSpeaking();\n unsubBotReady();\n unsubConnect();\n unsubDisconnect();\n unsubBotAudioTrack();\n unsubDisconnectWsAudio();\n unsubAudioState();\n unsubVideoState();\n unsubScreenShareState();\n wsAudioEl.srcObject = null;\n wsAudioEl.remove();\n };\n }, [client]);\n\n // Create enhanced connect function that can use stored config\n const connect = useCallback(\n async (configOverride?: ConvaiConfig) => {\n const finalConfig = configOverride || storedConfig;\n\n if (!finalConfig) {\n throw new Error(\n \"No configuration provided. Pass config to useConvaiClient() or to connect()\",\n );\n }\n\n await client.connect(finalConfig);\n },\n [client, storedConfig],\n );\n\n // Create reconnect function wrapper\n const reconnect = useCallback(async () => {\n await client.reconnect();\n }, [client]);\n\n // Return client interface with React-friendly additions\n return {\n state,\n connectionType: client.connectionType,\n ...(client.apiKey && { apiKey: client.apiKey }),\n ...(client.authToken && { authToken: client.authToken }),\n characterId: client.characterId,\n connect,\n connectWithConnectionData: client.connectWithConnectionData.bind(client),\n disconnect: client.disconnect.bind(client),\n reconnect,\n resetSession: client.resetSession.bind(client),\n room: client.room,\n sendUserTextMessage: client.sendUserTextMessage.bind(client),\n sendTriggerMessage: client.sendTriggerMessage.bind(client),\n sendInterruptMessage: client.sendInterruptMessage.bind(client),\n resetIdleTimer: client.resetIdleTimer.bind(client),\n updateTemplateKeys: client.updateTemplateKeys.bind(client),\n updateDynamicInfo: client.updateDynamicInfo.bind(client),\n updateContext: client.updateContext.bind(client),\n updateEmotion: client.updateEmotion.bind(client),\n visionStatus: client.visionStatus.bind(client),\n visionTrigger: client.visionTrigger.bind(client),\n respondModeUpdate: client.respondModeUpdate.bind(client),\n updateSceneMetadata: client.updateSceneMetadata.bind(client),\n activity,\n chatMessages,\n userTranscription,\n characterSessionId: client.characterSessionId,\n isBotReady,\n audioControls: client.audioControls,\n videoControls: client.videoControls,\n screenShareControls: client.screenShareControls,\n blendshapeQueue: client.blendshapeQueue,\n conversationSessionId: client.conversationSessionId,\n memoryManager: client.memoryManager,\n toggleTts: client.toggleTts.bind(client),\n toggleStt: client.toggleStt.bind(client),\n uploadFile: client.uploadFile.bind(client),\n on: client.on.bind(client),\n off: client.off.bind(client),\n // Reactive control states\n isAudioMuted,\n isVideoEnabled,\n isScreenShareActive,\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"useConvaiClient.js","sourceRoot":"","sources":["../../../src/react/hooks/useConvaiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAO9D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,MAAqB,EAOrB,EAAE;IACF,0EAA0E;IAC1E,0DAA0D;IAC1D,6DAA6D;IAC7D,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;IAE9D,6BAA6B;IAC7B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAS,MAAM,CAAC,CAAC;IACzD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAC9C,MAAM,CAAC,YAAY,CACpB,CAAC;IACF,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CACxD,MAAM,CAAC,iBAAiB,CACzB,CAAC;IACF,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC7D,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAU,MAAM,CAAC,UAAU,CAAC,CAAC;IAEzE,uCAAuC;IACvC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAC9C,MAAM,CAAC,aAAa,CAAC,YAAY,CAClC,CAAC;IACF,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAClD,MAAM,CAAC,aAAa,CAAC,cAAc,CACpC,CAAC;IACF,MAAM,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAC5D,MAAM,CAAC,mBAAmB,CAAC,mBAAmB,CAC/C,CAAC;IAEF,2BAA2B;IAC3B,MAAM,CAAC,YAAY,CAAC,GAAG,QAAQ,CAA2B,MAAM,CAAC,CAAC;IAElE,wBAAwB;IACxB,SAAS,CAAC,GAAG,EAAE;QACb,wBAAwB;QACxB,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,QAAQ,EAAE,EAAE;YAC7D,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAEnB,yFAAyF;YACzF,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC1B,WAAW,CAAC,cAAc,CAAC,CAAC;YAC9B,CAAC;iBAAM,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;gBACjC,WAAW,CAAC,eAAe,CAAC,CAAC;YAC/B,CAAC;iBAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBAC/B,WAAW,CAAC,UAAU,CAAC,CAAC;YAC1B,CAAC;iBAAM,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAChC,WAAW,CAAC,WAAW,CAAC,CAAC;YAC3B,CAAC;iBAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBAC/B,WAAW,CAAC,UAAU,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,WAAW,CAAC,WAAW,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,2BAA2B;QAC3B,MAAM,mBAAmB,GAAG,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,EAAE;YACnE,eAAe,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,qCAAqC;QACrC,MAAM,sBAAsB,GAAG,MAAM,CAAC,EAAE,CACtC,yBAAyB,EACzB,CAAC,aAAa,EAAE,EAAE;YAChB,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACtC,CAAC,CACF,CAAC;QAEF,2BAA2B;QAC3B,MAAM,aAAa,GAAG,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,EAAE;YAC7D,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,qBAAqB;QACrB,MAAM,aAAa,GAAG,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE;YAC/C,aAAa,CAAC,IAAI,CAAC,CAAC;YACpB,WAAW,CAAC,WAAW,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,mBAAmB;QACnB,MAAM,YAAY,GAAG,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YAC7C,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,sBAAsB;QACtB,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;YACnD,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvB,WAAW,CAAC,cAAc,CAAC,CAAC;YAC5B,eAAe,CAAC,EAAE,CAAC,CAAC;YACpB,qCAAqC;YACrC,eAAe,CAAC,IAAI,CAAC,CAAC;YACtB,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACzB,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,yEAAyE;QACzE,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAClD,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;QAC1B,SAAS,CAAC,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAErC,MAAM,kBAAkB,GAAG,MAAM,CAAC,EAAE,CAClC,eAAe,EACf,CAAC,KAAuB,EAAE,EAAE;YAC1B,SAAS,CAAC,SAAS,GAAG,IAAI,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACjD,CAAC,CACF,CAAC;QAEF,2CAA2C;QAC3C,MAAM,sBAAsB,GAAG,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;YAC1D,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,uCAAuC;QACvC,MAAM,eAAe,GAAG,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,kBAAkB,EAAE,CAAC,UAAe,EAAE,EAAE;YACtF,IAAI,UAAU,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC1C,eAAe,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,uCAAuC;QACvC,MAAM,eAAe,GAAG,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,kBAAkB,EAAE,CAAC,UAAe,EAAE,EAAE;YACtF,IAAI,UAAU,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;gBAC5C,iBAAiB,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,8CAA8C;QAC9C,MAAM,qBAAqB,GAAG,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC,wBAAwB,EAAE,CAAC,gBAAqB,EAAE,EAAE;YAC9G,IAAI,gBAAgB,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;gBACvD,sBAAsB,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,+BAA+B;QAC/B,OAAO,GAAG,EAAE;YACV,gBAAgB,EAAE,CAAC;YACnB,mBAAmB,EAAE,CAAC;YACtB,sBAAsB,EAAE,CAAC;YACzB,aAAa,EAAE,CAAC;YAChB,aAAa,EAAE,CAAC;YAChB,YAAY,EAAE,CAAC;YACf,eAAe,EAAE,CAAC;YAClB,kBAAkB,EAAE,CAAC;YACrB,sBAAsB,EAAE,CAAC;YACzB,eAAe,EAAE,CAAC;YAClB,eAAe,EAAE,CAAC;YAClB,qBAAqB,EAAE,CAAC;YACxB,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC;YAC3B,SAAS,CAAC,MAAM,EAAE,CAAC;QACrB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,8DAA8D;IAC9D,MAAM,OAAO,GAAG,WAAW,CACzB,KAAK,EAAE,cAA6B,EAAE,EAAE;QACtC,MAAM,WAAW,GAAG,cAAc,IAAI,YAAY,CAAC;QAEnD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,6EAA6E,CAC9E,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACpC,CAAC,EACD,CAAC,MAAM,EAAE,YAAY,CAAC,CACvB,CAAC;IAEF,oCAAoC;IACpC,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACvC,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;IAC3B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,wDAAwD;IACxD,OAAO;QACL,KAAK;QACL,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/C,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;QACxD,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,OAAO;QACP,yBAAyB,EAAE,MAAM,CAAC,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC;QACxE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QACtC,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC;QAChD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1D,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC9D,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC;QAChE,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1C,SAAS;QACT,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QAC9C,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC5D,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1D,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC9D,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QAClD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1D,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;QACxD,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC;QAChD,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC;QAChD,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QAC9C,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC;QAChD,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;QACxD,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC5D,QAAQ;QACR,YAAY;QACZ,iBAAiB;QACjB,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,UAAU;QACV,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,qBAAqB,EAAE,MAAM,CAAC,qBAAqB;QACnD,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;QACxC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;QACxC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1C,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1B,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;QAC5B,0BAA0B;QAC1B,YAAY;QACZ,cAAc;QACd,mBAAmB;KACpB,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { ConvaiClient as CoreConvaiClient } from \"../../core\";\nimport type {\n ConvaiConfig,\n IConvaiClient,\n ChatMessage,\n} from \"../../core/types\";\n\n/**\n * React hook wrapper for ConvaiClient\n * Main hook for managing Convai client connection and functionality.\n *\n * Provides a complete interface for connecting to Convai's AI-powered voice assistants,\n * managing real-time audio/video conversations, and handling various message types.\n *\n * @param config - Optional configuration to store for later connection\n * @returns {ConvaiClient & { activity: string; chatMessages: ChatMessage[] }} Complete client interface with connection state, methods, and message history\n *\n * @example\n * ```tsx\n * function App() {\n * const convaiClient = useConvaiClient({\n * apiKey: 'your-api-key',\n * characterId: 'your-character-id',\n * endUserId: 'user-uuid', // Optional: enables memory & analytics\n * enableVideo: true,\n * });\n *\n * return (\n * <ConvaiWidget convaiClient={convaiClient} />\n * );\n * }\n * ```\n */\nexport const useConvaiClient = (\n config?: ConvaiConfig,\n): IConvaiClient & {\n activity: string;\n chatMessages: ChatMessage[];\n isAudioMuted: boolean;\n isVideoEnabled: boolean;\n isScreenShareActive: boolean;\n} => {\n // Create client instance once. The config is handed to the client as well\n // as kept in the hook, so config-derived surfaces such as\n // `characterVersions` are usable before the first connect().\n const [client] = useState(() => new CoreConvaiClient(config));\n\n // React state for UI updates\n const [state, setState] = useState(client.state);\n const [activity, setActivity] = useState<string>(\"Idle\");\n const [chatMessages, setChatMessages] = useState<ChatMessage[]>(\n client.chatMessages,\n );\n const [userTranscription, setUserTranscription] = useState<string>(\n client.userTranscription,\n );\n const [isSpeaking, setIsSpeaking] = useState<boolean>(false);\n const [isBotReady, setIsBotReady] = useState<boolean>(client.isBotReady);\n \n // Reactive states for control managers\n const [isAudioMuted, setIsAudioMuted] = useState<boolean>(\n client.audioControls.isAudioMuted\n );\n const [isVideoEnabled, setIsVideoEnabled] = useState<boolean>(\n client.videoControls.isVideoEnabled\n );\n const [isScreenShareActive, setIsScreenShareActive] = useState<boolean>(\n client.screenShareControls.isScreenShareActive\n );\n\n // Store config if provided\n const [storedConfig] = useState<ConvaiConfig | undefined>(config);\n\n // Setup event listeners\n useEffect(() => {\n // State change listener\n const unsubStateChange = client.on(\"stateChange\", (newState) => {\n setState(newState);\n\n // Update activity based on state (priority: speaking > listening > thinking > connected)\n if (!newState.isConnected) {\n setActivity(\"Disconnected\");\n } else if (newState.isConnecting) {\n setActivity(\"Connecting...\");\n } else if (newState.isSpeaking) {\n setActivity(\"Speaking\");\n } else if (newState.isListening) {\n setActivity(\"Listening\");\n } else if (newState.isThinking) {\n setActivity(\"Thinking\");\n } else {\n setActivity(\"Connected\");\n }\n });\n\n // Messages change listener\n const unsubMessagesChange = client.on(\"messagesChange\", (messages) => {\n setChatMessages([...messages]);\n });\n\n // User transcription change listener\n const unsubUserTranscription = client.on(\n \"userTranscriptionChange\",\n (transcription) => {\n setUserTranscription(transcription);\n },\n );\n\n // Speaking change listener\n const unsubSpeaking = client.on(\"speakingChange\", (speaking) => {\n setIsSpeaking(speaking);\n });\n\n // Bot ready listener\n const unsubBotReady = client.on(\"botReady\", () => {\n setIsBotReady(true);\n setActivity(\"Connected\");\n });\n\n // Connect listener\n const unsubConnect = client.on(\"connect\", () => {\n setState(client.state);\n });\n\n // Disconnect listener\n const unsubDisconnect = client.on(\"disconnect\", () => {\n setIsBotReady(false);\n setState(client.state);\n setActivity(\"Disconnected\");\n setChatMessages([]);\n // Reset control states on disconnect\n setIsAudioMuted(true);\n setIsVideoEnabled(false);\n setIsScreenShareActive(false);\n });\n\n // WebSocket transport: play bot audio track via a hidden <audio> element\n const wsAudioEl = document.createElement(\"audio\");\n wsAudioEl.autoplay = true;\n wsAudioEl.dataset.convaiWs = \"true\";\n document.body.appendChild(wsAudioEl);\n\n const unsubBotAudioTrack = client.on(\n \"botAudioTrack\",\n (track: MediaStreamTrack) => {\n wsAudioEl.srcObject = new MediaStream([track]);\n },\n );\n\n // Disconnect listener also clears WS audio\n const unsubDisconnectWsAudio = client.on(\"disconnect\", () => {\n wsAudioEl.srcObject = null;\n });\n\n // Audio controls state change listener\n const unsubAudioState = client.audioControls.on(\"audioStateChange\", (audioState: any) => {\n if (audioState.isAudioMuted !== undefined) {\n setIsAudioMuted(audioState.isAudioMuted);\n }\n });\n\n // Video controls state change listener\n const unsubVideoState = client.videoControls.on(\"videoStateChange\", (videoState: any) => {\n if (videoState.isVideoEnabled !== undefined) {\n setIsVideoEnabled(videoState.isVideoEnabled);\n }\n });\n\n // Screen share controls state change listener\n const unsubScreenShareState = client.screenShareControls.on(\"screenShareStateChange\", (screenShareState: any) => {\n if (screenShareState.isScreenShareActive !== undefined) {\n setIsScreenShareActive(screenShareState.isScreenShareActive);\n }\n });\n\n // Cleanup listeners on unmount\n return () => {\n unsubStateChange();\n unsubMessagesChange();\n unsubUserTranscription();\n unsubSpeaking();\n unsubBotReady();\n unsubConnect();\n unsubDisconnect();\n unsubBotAudioTrack();\n unsubDisconnectWsAudio();\n unsubAudioState();\n unsubVideoState();\n unsubScreenShareState();\n wsAudioEl.srcObject = null;\n wsAudioEl.remove();\n };\n }, [client]);\n\n // Create enhanced connect function that can use stored config\n const connect = useCallback(\n async (configOverride?: ConvaiConfig) => {\n const finalConfig = configOverride || storedConfig;\n\n if (!finalConfig) {\n throw new Error(\n \"No configuration provided. Pass config to useConvaiClient() or to connect()\",\n );\n }\n\n await client.connect(finalConfig);\n },\n [client, storedConfig],\n );\n\n // Create reconnect function wrapper\n const reconnect = useCallback(async () => {\n await client.reconnect();\n }, [client]);\n\n // Return client interface with React-friendly additions\n return {\n state,\n connectionType: client.connectionType,\n ...(client.apiKey && { apiKey: client.apiKey }),\n ...(client.authToken && { authToken: client.authToken }),\n characterId: client.characterId,\n characterVersion: client.characterVersion,\n characterReference: client.characterReference,\n connect,\n connectWithConnectionData: client.connectWithConnectionData.bind(client),\n joinRoom: client.joinRoom.bind(client),\n appendMessage: client.appendMessage.bind(client),\n toggleUsageUpdates: client.toggleUsageUpdates.bind(client),\n setInteractionTarget: client.setInteractionTarget.bind(client),\n updateCharacterRoster: client.updateCharacterRoster.bind(client),\n roomSession: client.roomSession,\n characters: client.characters,\n activeMembershipId: client.activeMembershipId,\n disconnect: client.disconnect.bind(client),\n reconnect,\n resetSession: client.resetSession.bind(client),\n room: client.room,\n sendUserTextMessage: client.sendUserTextMessage.bind(client),\n sendTriggerMessage: client.sendTriggerMessage.bind(client),\n sendInterruptMessage: client.sendInterruptMessage.bind(client),\n resetIdleTimer: client.resetIdleTimer.bind(client),\n updateTemplateKeys: client.updateTemplateKeys.bind(client),\n updateDynamicInfo: client.updateDynamicInfo.bind(client),\n updateContext: client.updateContext.bind(client),\n updateEmotion: client.updateEmotion.bind(client),\n visionStatus: client.visionStatus.bind(client),\n visionTrigger: client.visionTrigger.bind(client),\n respondModeUpdate: client.respondModeUpdate.bind(client),\n updateSceneMetadata: client.updateSceneMetadata.bind(client),\n activity,\n chatMessages,\n userTranscription,\n characterSessionId: client.characterSessionId,\n isBotReady,\n audioControls: client.audioControls,\n videoControls: client.videoControls,\n screenShareControls: client.screenShareControls,\n blendshapeQueue: client.blendshapeQueue,\n conversationSessionId: client.conversationSessionId,\n memoryManager: client.memoryManager,\n characterVersions: client.characterVersions,\n toggleTts: client.toggleTts.bind(client),\n toggleStt: client.toggleStt.bind(client),\n uploadFile: client.uploadFile.bind(client),\n on: client.on.bind(client),\n off: client.off.bind(client),\n // Reactive control states\n isAudioMuted,\n isVideoEnabled,\n isScreenShareActive,\n };\n};\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline markdown tokenizer shared by the React and vanilla chat bubbles.
|
|
3
|
+
*
|
|
4
|
+
* Core-service can hand the client bot text with links preserved
|
|
5
|
+
* (`preserve_links_in_output` on /connect): `[text](url)`, `` and
|
|
6
|
+
* bare `http(s)://` URLs arrive verbatim, everything else is still flattened
|
|
7
|
+
* server-side. This turns one line of that text into nodes a renderer can
|
|
8
|
+
* map onto DOM or JSX.
|
|
9
|
+
*
|
|
10
|
+
* Only `http:` and `https:` become links or images. Anything else — including
|
|
11
|
+
* `javascript:` — stays literal text, regardless of what the server sent.
|
|
12
|
+
* A link whose closing paren has not streamed in yet is left as text too, so
|
|
13
|
+
* a bubble does not flicker between shapes mid-response.
|
|
14
|
+
*/
|
|
15
|
+
export type MediaKind = "image" | "video";
|
|
16
|
+
export type InlineNode = {
|
|
17
|
+
kind: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "strong";
|
|
21
|
+
text: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "em";
|
|
24
|
+
text: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "link";
|
|
27
|
+
href: string;
|
|
28
|
+
label: string;
|
|
29
|
+
media: MediaKind | null;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "image";
|
|
32
|
+
src: string;
|
|
33
|
+
alt: string;
|
|
34
|
+
};
|
|
35
|
+
/** Classify a URL by its path extension; null for anything not previewable. */
|
|
36
|
+
export declare function mediaKindForUrl(url: string): MediaKind | null;
|
|
37
|
+
export declare function parseInlineMarkdown(line: string): InlineNode[];
|
|
38
|
+
//# sourceMappingURL=inlineMarkdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inlineMarkdown.d.ts","sourceRoot":"","sources":["../../src/utils/inlineMarkdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1C,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAKhD,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAU7D;AAqCD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAkC9D"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline markdown tokenizer shared by the React and vanilla chat bubbles.
|
|
3
|
+
*
|
|
4
|
+
* Core-service can hand the client bot text with links preserved
|
|
5
|
+
* (`preserve_links_in_output` on /connect): `[text](url)`, `` and
|
|
6
|
+
* bare `http(s)://` URLs arrive verbatim, everything else is still flattened
|
|
7
|
+
* server-side. This turns one line of that text into nodes a renderer can
|
|
8
|
+
* map onto DOM or JSX.
|
|
9
|
+
*
|
|
10
|
+
* Only `http:` and `https:` become links or images. Anything else — including
|
|
11
|
+
* `javascript:` — stays literal text, regardless of what the server sent.
|
|
12
|
+
* A link whose closing paren has not streamed in yet is left as text too, so
|
|
13
|
+
* a bubble does not flicker between shapes mid-response.
|
|
14
|
+
*/
|
|
15
|
+
const IMAGE_EXT = /\.(png|jpe?g|gif|webp|avif)$/i;
|
|
16
|
+
const VIDEO_EXT = /\.(mp4|webm|mov|ogg)$/i;
|
|
17
|
+
/** Classify a URL by its path extension; null for anything not previewable. */
|
|
18
|
+
export function mediaKindForUrl(url) {
|
|
19
|
+
let pathname;
|
|
20
|
+
try {
|
|
21
|
+
pathname = new URL(url).pathname;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (IMAGE_EXT.test(pathname))
|
|
27
|
+
return "image";
|
|
28
|
+
if (VIDEO_EXT.test(pathname))
|
|
29
|
+
return "video";
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// Alternatives are tried in this order at every position:
|
|
33
|
+
// 1.  / [text](url) with an http(s) URL
|
|
34
|
+
// 2. bare http(s) URL, unless it is the target of a link still streaming in
|
|
35
|
+
// 3. **bold** / __bold__ / *em* / _em_
|
|
36
|
+
// Matching URLs before emphasis is what keeps `_` inside a URL literal.
|
|
37
|
+
const TOKEN = /(!?)\[([^\]]*)\]\((https?:\/\/[^\s)]+)\)|(?<!\]\()(https?:\/\/[^\s<>"'`]+)|(\*\*|__)(.*?)\5|\*([^*]+)\*|_([^_]+)_/g;
|
|
38
|
+
const TRAILING_PUNCTUATION = ".,;:!?'\"";
|
|
39
|
+
/**
|
|
40
|
+
* Sentence punctuation after a bare URL belongs to the sentence, and an
|
|
41
|
+
* unbalanced `)` belongs to the surrounding parenthetical.
|
|
42
|
+
*/
|
|
43
|
+
function splitTrailingPunctuation(raw) {
|
|
44
|
+
let url = raw;
|
|
45
|
+
while (url.length > 0) {
|
|
46
|
+
const last = url[url.length - 1];
|
|
47
|
+
if (TRAILING_PUNCTUATION.includes(last)) {
|
|
48
|
+
url = url.slice(0, -1);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (last === ")") {
|
|
52
|
+
const opens = (url.match(/\(/g) ?? []).length;
|
|
53
|
+
const closes = (url.match(/\)/g) ?? []).length;
|
|
54
|
+
if (closes > opens) {
|
|
55
|
+
url = url.slice(0, -1);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
return [url, raw.slice(url.length)];
|
|
62
|
+
}
|
|
63
|
+
export function parseInlineMarkdown(line) {
|
|
64
|
+
const nodes = [];
|
|
65
|
+
const pushText = (text) => {
|
|
66
|
+
if (!text)
|
|
67
|
+
return;
|
|
68
|
+
const last = nodes[nodes.length - 1];
|
|
69
|
+
if (last?.kind === "text")
|
|
70
|
+
last.text += text;
|
|
71
|
+
else
|
|
72
|
+
nodes.push({ kind: "text", text });
|
|
73
|
+
};
|
|
74
|
+
let lastIndex = 0;
|
|
75
|
+
TOKEN.lastIndex = 0;
|
|
76
|
+
let match;
|
|
77
|
+
while ((match = TOKEN.exec(line)) !== null) {
|
|
78
|
+
pushText(line.slice(lastIndex, match.index));
|
|
79
|
+
const [, bang, label, linkUrl, bareUrl, , boldText, starEm, underscoreEm] = match;
|
|
80
|
+
if (linkUrl !== undefined) {
|
|
81
|
+
if (bang)
|
|
82
|
+
nodes.push({ kind: "image", src: linkUrl, alt: label });
|
|
83
|
+
else
|
|
84
|
+
nodes.push({ kind: "link", href: linkUrl, label, media: mediaKindForUrl(linkUrl) });
|
|
85
|
+
}
|
|
86
|
+
else if (bareUrl !== undefined) {
|
|
87
|
+
const [href, rest] = splitTrailingPunctuation(bareUrl);
|
|
88
|
+
if (href)
|
|
89
|
+
nodes.push({ kind: "link", href, label: href, media: mediaKindForUrl(href) });
|
|
90
|
+
pushText(rest);
|
|
91
|
+
}
|
|
92
|
+
else if (boldText !== undefined) {
|
|
93
|
+
nodes.push({ kind: "strong", text: boldText });
|
|
94
|
+
}
|
|
95
|
+
else if (starEm !== undefined) {
|
|
96
|
+
nodes.push({ kind: "em", text: starEm });
|
|
97
|
+
}
|
|
98
|
+
else if (underscoreEm !== undefined) {
|
|
99
|
+
nodes.push({ kind: "em", text: underscoreEm });
|
|
100
|
+
}
|
|
101
|
+
lastIndex = match.index + match[0].length;
|
|
102
|
+
}
|
|
103
|
+
pushText(line.slice(lastIndex));
|
|
104
|
+
return nodes;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=inlineMarkdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inlineMarkdown.js","sourceRoot":"","sources":["../../src/utils/inlineMarkdown.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Inline markdown tokenizer shared by the React and vanilla chat bubbles.\n *\n * Core-service can hand the client bot text with links preserved\n * (`preserve_links_in_output` on /connect): `[text](url)`, `` and\n * bare `http(s)://` URLs arrive verbatim, everything else is still flattened\n * server-side. This turns one line of that text into nodes a renderer can\n * map onto DOM or JSX.\n *\n * Only `http:` and `https:` become links or images. Anything else — including\n * `javascript:` — stays literal text, regardless of what the server sent.\n * A link whose closing paren has not streamed in yet is left as text too, so\n * a bubble does not flicker between shapes mid-response.\n */\n\nexport type MediaKind = \"image\" | \"video\";\n\nexport type InlineNode =\n | { kind: \"text\"; text: string }\n | { kind: \"strong\"; text: string }\n | { kind: \"em\"; text: string }\n | { kind: \"link\"; href: string; label: string; media: MediaKind | null }\n | { kind: \"image\"; src: string; alt: string };\n\nconst IMAGE_EXT = /\\.(png|jpe?g|gif|webp|avif)$/i;\nconst VIDEO_EXT = /\\.(mp4|webm|mov|ogg)$/i;\n\n/** Classify a URL by its path extension; null for anything not previewable. */\nexport function mediaKindForUrl(url: string): MediaKind | null {\n let pathname: string;\n try {\n pathname = new URL(url).pathname;\n } catch {\n return null;\n }\n if (IMAGE_EXT.test(pathname)) return \"image\";\n if (VIDEO_EXT.test(pathname)) return \"video\";\n return null;\n}\n\n// Alternatives are tried in this order at every position:\n// 1.  / [text](url) with an http(s) URL\n// 2. bare http(s) URL, unless it is the target of a link still streaming in\n// 3. **bold** / __bold__ / *em* / _em_\n// Matching URLs before emphasis is what keeps `_` inside a URL literal.\nconst TOKEN =\n /(!?)\\[([^\\]]*)\\]\\((https?:\\/\\/[^\\s)]+)\\)|(?<!\\]\\()(https?:\\/\\/[^\\s<>\"'`]+)|(\\*\\*|__)(.*?)\\5|\\*([^*]+)\\*|_([^_]+)_/g;\n\nconst TRAILING_PUNCTUATION = \".,;:!?'\\\"\";\n\n/**\n * Sentence punctuation after a bare URL belongs to the sentence, and an\n * unbalanced `)` belongs to the surrounding parenthetical.\n */\nfunction splitTrailingPunctuation(raw: string): [string, string] {\n let url = raw;\n while (url.length > 0) {\n const last = url[url.length - 1];\n if (TRAILING_PUNCTUATION.includes(last)) {\n url = url.slice(0, -1);\n continue;\n }\n if (last === \")\") {\n const opens = (url.match(/\\(/g) ?? []).length;\n const closes = (url.match(/\\)/g) ?? []).length;\n if (closes > opens) {\n url = url.slice(0, -1);\n continue;\n }\n }\n break;\n }\n return [url, raw.slice(url.length)];\n}\n\nexport function parseInlineMarkdown(line: string): InlineNode[] {\n const nodes: InlineNode[] = [];\n const pushText = (text: string) => {\n if (!text) return;\n const last = nodes[nodes.length - 1];\n if (last?.kind === \"text\") last.text += text;\n else nodes.push({ kind: \"text\", text });\n };\n\n let lastIndex = 0;\n TOKEN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = TOKEN.exec(line)) !== null) {\n pushText(line.slice(lastIndex, match.index));\n const [, bang, label, linkUrl, bareUrl, , boldText, starEm, underscoreEm] = match;\n\n if (linkUrl !== undefined) {\n if (bang) nodes.push({ kind: \"image\", src: linkUrl, alt: label });\n else nodes.push({ kind: \"link\", href: linkUrl, label, media: mediaKindForUrl(linkUrl) });\n } else if (bareUrl !== undefined) {\n const [href, rest] = splitTrailingPunctuation(bareUrl);\n if (href) nodes.push({ kind: \"link\", href, label: href, media: mediaKindForUrl(href) });\n pushText(rest);\n } else if (boldText !== undefined) {\n nodes.push({ kind: \"strong\", text: boldText });\n } else if (starEm !== undefined) {\n nodes.push({ kind: \"em\", text: starEm });\n } else if (underscoreEm !== undefined) {\n nodes.push({ kind: \"em\", text: underscoreEm });\n }\n lastIndex = match.index + match[0].length;\n }\n pushText(line.slice(lastIndex));\n return nodes;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioRenderer.d.ts","sourceRoot":"","sources":["../../src/vanilla/AudioRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAwD,MAAM,gBAAgB,CAAC;AAG5F;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAA+B;IAGpD,OAAO,CAAC,0BAA0B,CAAM;IACxC,OAAO,CAAC,4BAA4B,CAAM;IAC1C,OAAO,CAAC,kCAAkC,CAAM;gBAEpC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,WAAW,CAAA;KAAE;IAmB7D;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAM3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAU5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAU7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAU/B;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAMrC;;OAEG;IACH,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"AudioRenderer.d.ts","sourceRoot":"","sources":["../../src/vanilla/AudioRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAwD,MAAM,gBAAgB,CAAC;AAG5F;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAA+B;IAGpD,OAAO,CAAC,0BAA0B,CAAM;IACxC,OAAO,CAAC,4BAA4B,CAAM;IAC1C,OAAO,CAAC,kCAAkC,CAAM;gBAEpC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,WAAW,CAAA;KAAE;IAmB7D;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAM3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAU5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAU7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAU/B;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAMrC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqCxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,eAAe,CAAiC;IAExD;;OAEG;IACH,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IASnD,oBAAoB,IAAI,IAAI;IAS5B;;OAEG;IACH,OAAO,IAAI,IAAI;CAmBhB"}
|
|
@@ -84,6 +84,12 @@ export class AudioRenderer {
|
|
|
84
84
|
// Create audio element
|
|
85
85
|
const audioElement = document.createElement('audio');
|
|
86
86
|
audioElement.id = `audio-${trackSid}`;
|
|
87
|
+
// Expose who owns this element, so a host page can find a member's
|
|
88
|
+
// audio by identity (`character:{membershipId}` in a roster room).
|
|
89
|
+
audioElement.dataset.participantIdentity = participant.identity;
|
|
90
|
+
if (participant.identity.startsWith('character:')) {
|
|
91
|
+
audioElement.dataset.membershipId = participant.identity.slice('character:'.length);
|
|
92
|
+
}
|
|
87
93
|
audioElement.autoplay = true;
|
|
88
94
|
// Direct track attachment - play audio immediately
|
|
89
95
|
track.attach(audioElement);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioRenderer.js","sourceRoot":"","sources":["../../src/vanilla/AudioRenderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { Room, RoomEvent, RemoteTrackPublication, RemoteParticipant } from 'livekit-client';\nimport { logger } from '../utils/logger';\n\n/**\n * AudioRenderer - Manages audio playback for remote participants\n * Automatically creates and attaches audio elements for remote audio tracks\n * This replaces React's RoomAudioRenderer for vanilla implementations\n */\nexport class AudioRenderer {\n private room: Room;\n private audioElements: Map<string, HTMLAudioElement> = new Map();\n private container: HTMLElement;\n private rendererId: string; // Unique ID for debugging\n private livekitTracks: Map<string, any> = new Map(); // Store track references\n\n // Store bound event handlers so we can properly remove them\n private boundHandleTrackSubscribed: any;\n private boundHandleTrackUnsubscribed: any;\n private boundHandleParticipantDisconnected: any;\n\n constructor(room: Room, options?: { container?: HTMLElement }) {\n this.room = room;\n this.rendererId = `renderer-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n \n // Bind event handlers once and store references\n this.boundHandleTrackSubscribed = this.handleTrackSubscribed.bind(this);\n this.boundHandleTrackUnsubscribed = this.handleTrackUnsubscribed.bind(this);\n this.boundHandleParticipantDisconnected = this.handleParticipantDisconnected.bind(this);\n \n // Create a hidden container for audio elements\n this.container = document.createElement('div');\n this.container.id = `convai-audio-renderer-${this.rendererId}`;\n this.container.style.display = 'none';\n (options?.container ?? document.body).appendChild(this.container);\n\n this.setupEventListeners();\n this.attachExistingTracks();\n }\n\n /**\n * Setup event listeners for room events\n */\n private setupEventListeners(): void {\n this.room.on(RoomEvent.TrackSubscribed, this.boundHandleTrackSubscribed);\n this.room.on(RoomEvent.TrackUnsubscribed, this.boundHandleTrackUnsubscribed);\n this.room.on(RoomEvent.ParticipantDisconnected, this.boundHandleParticipantDisconnected);\n }\n\n /**\n * Attach audio elements for existing tracks (if any)\n */\n private attachExistingTracks(): void {\n this.room.remoteParticipants.forEach((participant) => {\n participant.audioTrackPublications.forEach((publication) => {\n if (publication.track && publication.isSubscribed) {\n this.attachAudioTrack(publication, participant);\n }\n });\n });\n }\n\n /**\n * Handle track subscribed event\n */\n private handleTrackSubscribed(\n track: any,\n publication: RemoteTrackPublication,\n participant: RemoteParticipant\n ): void {\n if (track.kind === 'audio') {\n this.attachAudioTrack(publication, participant);\n }\n }\n\n /**\n * Handle track unsubscribed event\n */\n private handleTrackUnsubscribed(\n track: any,\n publication: RemoteTrackPublication,\n participant: RemoteParticipant\n ): void {\n if (track.kind === 'audio') {\n this.detachAudioTrack(publication.trackSid);\n }\n }\n\n /**\n * Handle participant disconnected event\n */\n private handleParticipantDisconnected(participant: RemoteParticipant): void {\n participant.audioTrackPublications.forEach((publication) => {\n this.detachAudioTrack(publication.trackSid);\n });\n }\n\n /**\n * Attach an audio track to an audio element\n */\n private attachAudioTrack(\n publication: RemoteTrackPublication,\n participant: RemoteParticipant\n ): void {\n const track = publication.track;\n if (!track || track.kind !== 'audio') return;\n\n const trackSid = publication.trackSid;\n\n // Skip if already attached\n if (this.audioElements.has(trackSid)) {\n return;\n }\n\n try {\n // Create audio element\n const audioElement = document.createElement('audio');\n audioElement.id = `audio-${trackSid}`;\n audioElement.autoplay = true;\n \n // Direct track attachment - play audio immediately\n track.attach(audioElement);\n \n this.container.appendChild(audioElement);\n this.audioElements.set(trackSid, audioElement);\n this.livekitTracks.set(trackSid, track);\n } catch (error) {\n logger.error('Failed to attach audio track:', error);\n }\n }\n\n /**\n * Detach an audio track and remove its element\n */\n private detachAudioTrack(trackSid: string): void {\n // Remove audio element\n const audioElement = this.audioElements.get(trackSid);\n if (audioElement) {\n // Detach from LiveKit track\n const livekitTrack = this.livekitTracks.get(trackSid);\n if (livekitTrack) {\n livekitTrack.detach(audioElement);\n this.livekitTracks.delete(trackSid);\n }\n\n audioElement.pause();\n audioElement.srcObject = null;\n audioElement.remove();\n }\n \n this.audioElements.delete(trackSid);\n }\n\n private _wsAudioElement: HTMLAudioElement | null = null;\n\n /**\n * Attach a MediaStreamTrack from WebSocket transport for bot audio playback.\n */\n attachWebSocketTrack(track: MediaStreamTrack): void {\n this.detachWebSocketTrack();\n const el = document.createElement('audio');\n el.autoplay = true;\n el.srcObject = new MediaStream([track]);\n this.container.appendChild(el);\n this._wsAudioElement = el;\n }\n\n detachWebSocketTrack(): void {\n if (this._wsAudioElement) {\n this._wsAudioElement.pause();\n this._wsAudioElement.srcObject = null;\n this._wsAudioElement.remove();\n this._wsAudioElement = null;\n }\n }\n\n /**\n * Cleanup and destroy the audio renderer\n */\n destroy(): void {\n this.detachWebSocketTrack();\n\n // Remove all audio elements\n this.audioElements.forEach((element, trackSid) => {\n this.detachAudioTrack(trackSid);\n });\n this.audioElements.clear();\n\n // Remove container\n if (this.container && this.container.parentElement) {\n this.container.remove();\n }\n\n // Remove event listeners using stored bound references\n this.room.off(RoomEvent.TrackSubscribed, this.boundHandleTrackSubscribed);\n this.room.off(RoomEvent.TrackUnsubscribed, this.boundHandleTrackUnsubscribed);\n this.room.off(RoomEvent.ParticipantDisconnected, this.boundHandleParticipantDisconnected);\n }\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"AudioRenderer.js","sourceRoot":"","sources":["../../src/vanilla/AudioRenderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { Room, RoomEvent, RemoteTrackPublication, RemoteParticipant } from 'livekit-client';\nimport { logger } from '../utils/logger';\n\n/**\n * AudioRenderer - Manages audio playback for remote participants\n * Automatically creates and attaches audio elements for remote audio tracks\n * This replaces React's RoomAudioRenderer for vanilla implementations\n */\nexport class AudioRenderer {\n private room: Room;\n private audioElements: Map<string, HTMLAudioElement> = new Map();\n private container: HTMLElement;\n private rendererId: string; // Unique ID for debugging\n private livekitTracks: Map<string, any> = new Map(); // Store track references\n\n // Store bound event handlers so we can properly remove them\n private boundHandleTrackSubscribed: any;\n private boundHandleTrackUnsubscribed: any;\n private boundHandleParticipantDisconnected: any;\n\n constructor(room: Room, options?: { container?: HTMLElement }) {\n this.room = room;\n this.rendererId = `renderer-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n \n // Bind event handlers once and store references\n this.boundHandleTrackSubscribed = this.handleTrackSubscribed.bind(this);\n this.boundHandleTrackUnsubscribed = this.handleTrackUnsubscribed.bind(this);\n this.boundHandleParticipantDisconnected = this.handleParticipantDisconnected.bind(this);\n \n // Create a hidden container for audio elements\n this.container = document.createElement('div');\n this.container.id = `convai-audio-renderer-${this.rendererId}`;\n this.container.style.display = 'none';\n (options?.container ?? document.body).appendChild(this.container);\n\n this.setupEventListeners();\n this.attachExistingTracks();\n }\n\n /**\n * Setup event listeners for room events\n */\n private setupEventListeners(): void {\n this.room.on(RoomEvent.TrackSubscribed, this.boundHandleTrackSubscribed);\n this.room.on(RoomEvent.TrackUnsubscribed, this.boundHandleTrackUnsubscribed);\n this.room.on(RoomEvent.ParticipantDisconnected, this.boundHandleParticipantDisconnected);\n }\n\n /**\n * Attach audio elements for existing tracks (if any)\n */\n private attachExistingTracks(): void {\n this.room.remoteParticipants.forEach((participant) => {\n participant.audioTrackPublications.forEach((publication) => {\n if (publication.track && publication.isSubscribed) {\n this.attachAudioTrack(publication, participant);\n }\n });\n });\n }\n\n /**\n * Handle track subscribed event\n */\n private handleTrackSubscribed(\n track: any,\n publication: RemoteTrackPublication,\n participant: RemoteParticipant\n ): void {\n if (track.kind === 'audio') {\n this.attachAudioTrack(publication, participant);\n }\n }\n\n /**\n * Handle track unsubscribed event\n */\n private handleTrackUnsubscribed(\n track: any,\n publication: RemoteTrackPublication,\n participant: RemoteParticipant\n ): void {\n if (track.kind === 'audio') {\n this.detachAudioTrack(publication.trackSid);\n }\n }\n\n /**\n * Handle participant disconnected event\n */\n private handleParticipantDisconnected(participant: RemoteParticipant): void {\n participant.audioTrackPublications.forEach((publication) => {\n this.detachAudioTrack(publication.trackSid);\n });\n }\n\n /**\n * Attach an audio track to an audio element\n */\n private attachAudioTrack(\n publication: RemoteTrackPublication,\n participant: RemoteParticipant\n ): void {\n const track = publication.track;\n if (!track || track.kind !== 'audio') return;\n\n const trackSid = publication.trackSid;\n\n // Skip if already attached\n if (this.audioElements.has(trackSid)) {\n return;\n }\n\n try {\n // Create audio element\n const audioElement = document.createElement('audio');\n audioElement.id = `audio-${trackSid}`;\n // Expose who owns this element, so a host page can find a member's\n // audio by identity (`character:{membershipId}` in a roster room).\n audioElement.dataset.participantIdentity = participant.identity;\n if (participant.identity.startsWith('character:')) {\n audioElement.dataset.membershipId = participant.identity.slice('character:'.length);\n }\n audioElement.autoplay = true;\n \n // Direct track attachment - play audio immediately\n track.attach(audioElement);\n \n this.container.appendChild(audioElement);\n this.audioElements.set(trackSid, audioElement);\n this.livekitTracks.set(trackSid, track);\n } catch (error) {\n logger.error('Failed to attach audio track:', error);\n }\n }\n\n /**\n * Detach an audio track and remove its element\n */\n private detachAudioTrack(trackSid: string): void {\n // Remove audio element\n const audioElement = this.audioElements.get(trackSid);\n if (audioElement) {\n // Detach from LiveKit track\n const livekitTrack = this.livekitTracks.get(trackSid);\n if (livekitTrack) {\n livekitTrack.detach(audioElement);\n this.livekitTracks.delete(trackSid);\n }\n\n audioElement.pause();\n audioElement.srcObject = null;\n audioElement.remove();\n }\n \n this.audioElements.delete(trackSid);\n }\n\n private _wsAudioElement: HTMLAudioElement | null = null;\n\n /**\n * Attach a MediaStreamTrack from WebSocket transport for bot audio playback.\n */\n attachWebSocketTrack(track: MediaStreamTrack): void {\n this.detachWebSocketTrack();\n const el = document.createElement('audio');\n el.autoplay = true;\n el.srcObject = new MediaStream([track]);\n this.container.appendChild(el);\n this._wsAudioElement = el;\n }\n\n detachWebSocketTrack(): void {\n if (this._wsAudioElement) {\n this._wsAudioElement.pause();\n this._wsAudioElement.srcObject = null;\n this._wsAudioElement.remove();\n this._wsAudioElement = null;\n }\n }\n\n /**\n * Cleanup and destroy the audio renderer\n */\n destroy(): void {\n this.detachWebSocketTrack();\n\n // Remove all audio elements\n this.audioElements.forEach((element, trackSid) => {\n this.detachAudioTrack(trackSid);\n });\n this.audioElements.clear();\n\n // Remove container\n if (this.container && this.container.parentElement) {\n this.container.remove();\n }\n\n // Remove event listeners using stored bound references\n this.room.off(RoomEvent.TrackSubscribed, this.boundHandleTrackSubscribed);\n this.room.off(RoomEvent.TrackUnsubscribed, this.boundHandleTrackUnsubscribed);\n this.room.off(RoomEvent.ParticipantDisconnected, this.boundHandleParticipantDisconnected);\n }\n}\n\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConvaiWidget.d.ts","sourceRoot":"","sources":["../../src/vanilla/ConvaiWidget.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EAEd,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ConvaiWidget.d.ts","sourceRoot":"","sources":["../../src/vanilla/ConvaiWidget.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EAEd,MAAM,SAAS,CAAC;AA4HjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAymFf;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAE/D"}
|
|
@@ -6,6 +6,7 @@ import { AudioRenderer } from "./AudioRenderer.js";
|
|
|
6
6
|
import { aeroTheme, injectGlobalStyles, injectKeyframes, } from "./styles.js";
|
|
7
7
|
import { Icons } from "./icons.js";
|
|
8
8
|
import { ConvaiClient } from "../core/ConvaiClient.js";
|
|
9
|
+
import { parseInlineMarkdown } from "../utils/inlineMarkdown.js";
|
|
9
10
|
/**
|
|
10
11
|
* Positioning declarations for the widget root, keyed by `placement`. The
|
|
11
12
|
* `bottom-right` entry must stay byte-identical to the literals it replaces --
|
|
@@ -1997,59 +1998,65 @@ export function createConvaiWidget(container, options) {
|
|
|
1997
1998
|
const lines = normalizedText.split("\n");
|
|
1998
1999
|
lines.forEach((line, lineIndex) => {
|
|
1999
2000
|
// Process markdown within each line
|
|
2001
|
+
// Bold, italic, links, images and bare URLs — see utils/inlineMarkdown.
|
|
2002
|
+
const mediaPreview = (src, alt, media) => {
|
|
2003
|
+
const el = media === "image"
|
|
2004
|
+
? Object.assign(document.createElement("img"), { src, alt, loading: "lazy" })
|
|
2005
|
+
: Object.assign(document.createElement("video"), { src, controls: true, preload: "metadata" });
|
|
2006
|
+
el.setAttribute("part", "message-media");
|
|
2007
|
+
el.style.cssText = `
|
|
2008
|
+
display: block;
|
|
2009
|
+
max-width: 100%;
|
|
2010
|
+
max-height: 240px;
|
|
2011
|
+
margin-top: 6px;
|
|
2012
|
+
border-radius: 8px;
|
|
2013
|
+
`;
|
|
2014
|
+
return el;
|
|
2015
|
+
};
|
|
2000
2016
|
const processLine = (text) => {
|
|
2001
2017
|
const parts = [];
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
parts.push(document.createTextNode(textBefore));
|
|
2018
|
+
for (const node of parseInlineMarkdown(text)) {
|
|
2019
|
+
switch (node.kind) {
|
|
2020
|
+
case "strong": {
|
|
2021
|
+
// matches React UserBubble styling
|
|
2022
|
+
const strong = document.createElement("strong");
|
|
2023
|
+
strong.textContent = node.text;
|
|
2024
|
+
strong.style.cssText = `
|
|
2025
|
+
font-weight: 600;
|
|
2026
|
+
color: var(--convai-panel-fg, ${aeroTheme.colors.text.primary});
|
|
2027
|
+
`;
|
|
2028
|
+
parts.push(strong);
|
|
2029
|
+
break;
|
|
2015
2030
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
}
|
|
2046
|
-
lastIndex = match.index + match[0].length;
|
|
2047
|
-
}
|
|
2048
|
-
// Add remaining text
|
|
2049
|
-
if (lastIndex < remaining.length) {
|
|
2050
|
-
const textAfter = remaining.substring(lastIndex);
|
|
2051
|
-
if (textAfter) {
|
|
2052
|
-
parts.push(document.createTextNode(textAfter));
|
|
2031
|
+
case "em": {
|
|
2032
|
+
const em = document.createElement("em");
|
|
2033
|
+
em.textContent = node.text;
|
|
2034
|
+
em.style.cssText = `font-style: italic;`;
|
|
2035
|
+
parts.push(em);
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
case "link": {
|
|
2039
|
+
const a = document.createElement("a");
|
|
2040
|
+
a.href = node.href;
|
|
2041
|
+
a.textContent = node.label;
|
|
2042
|
+
a.target = "_blank";
|
|
2043
|
+
a.rel = "noopener noreferrer";
|
|
2044
|
+
a.setAttribute("part", "message-link");
|
|
2045
|
+
a.style.cssText = `
|
|
2046
|
+
color: #10b981;
|
|
2047
|
+
text-decoration: underline;
|
|
2048
|
+
word-break: break-all;
|
|
2049
|
+
`;
|
|
2050
|
+
parts.push(a);
|
|
2051
|
+
if (node.media)
|
|
2052
|
+
parts.push(mediaPreview(node.href, node.label, node.media));
|
|
2053
|
+
break;
|
|
2054
|
+
}
|
|
2055
|
+
case "image":
|
|
2056
|
+
parts.push(mediaPreview(node.src, node.alt, "image"));
|
|
2057
|
+
break;
|
|
2058
|
+
default:
|
|
2059
|
+
parts.push(document.createTextNode(node.text));
|
|
2053
2060
|
}
|
|
2054
2061
|
}
|
|
2055
2062
|
return parts;
|
|
@@ -2485,13 +2492,20 @@ export function createConvaiWidget(container, options) {
|
|
|
2485
2492
|
// message never reaches the character and is never billed as an LLM
|
|
2486
2493
|
// turn. The `local-` id keeps it from colliding with a server-issued id.
|
|
2487
2494
|
appendMessage: (message) => {
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
+
// Shared core path, so React and vanilla insert identically. A
|
|
2496
|
+
// hand-rolled IConvaiClient without it falls back to the old local push.
|
|
2497
|
+
if (typeof client.appendMessage === "function") {
|
|
2498
|
+
client.appendMessage(message);
|
|
2499
|
+
}
|
|
2500
|
+
else {
|
|
2501
|
+
const chatMessage = {
|
|
2502
|
+
id: `local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,
|
|
2503
|
+
type: message.sender === "user" ? "user-llm-text" : "bot-llm-text",
|
|
2504
|
+
content: message.content,
|
|
2505
|
+
timestamp: new Date().toISOString(),
|
|
2506
|
+
};
|
|
2507
|
+
client.chatMessages.push(chatMessage);
|
|
2508
|
+
}
|
|
2495
2509
|
updateMessageList();
|
|
2496
2510
|
},
|
|
2497
2511
|
destroy: () => {
|