@convai/web-sdk 1.8.0-beta.6 → 1.8.0-beta.7
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 +285 -1
- package/dist/core/ConvaiClient.d.ts +59 -2
- package/dist/core/ConvaiClient.d.ts.map +1 -1
- package/dist/core/ConvaiClient.js +457 -6
- package/dist/core/ConvaiClient.js.map +1 -1
- package/dist/core/MessageHandler.d.ts +11 -0
- package/dist/core/MessageHandler.d.ts.map +1 -1
- package/dist/core/MessageHandler.js +325 -30
- package/dist/core/MessageHandler.js.map +1 -1
- package/dist/core/SSESession.js +2 -2
- package/dist/core/SSESession.js.map +1 -1
- package/dist/core/connectRequest.d.ts +38 -3
- package/dist/core/connectRequest.d.ts.map +1 -1
- package/dist/core/connectRequest.js +57 -11
- package/dist/core/connectRequest.js.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/logicalTurn.d.ts +4 -1
- package/dist/core/logicalTurn.d.ts.map +1 -1
- package/dist/core/logicalTurn.js +35 -4
- package/dist/core/logicalTurn.js.map +1 -1
- package/dist/core/modelOutput.d.ts +40 -0
- package/dist/core/modelOutput.d.ts.map +1 -0
- package/dist/core/modelOutput.js +187 -0
- package/dist/core/modelOutput.js.map +1 -0
- package/dist/core/publishedChat.d.ts +9 -0
- package/dist/core/publishedChat.d.ts.map +1 -0
- package/dist/core/publishedChat.js +13 -0
- package/dist/core/publishedChat.js.map +1 -0
- package/dist/core/types.d.ts +225 -25
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/embed/ConvaiChatElement.d.ts +27 -0
- package/dist/embed/ConvaiChatElement.d.ts.map +1 -0
- package/dist/embed/ConvaiChatElement.js +300 -0
- package/dist/embed/ConvaiChatElement.js.map +1 -0
- package/dist/embed/browser.d.ts +3 -0
- package/dist/embed/browser.d.ts.map +1 -0
- package/dist/embed/browser.js +5 -0
- package/dist/embed/browser.js.map +1 -0
- package/dist/embed/chat-embed-v1.js +100 -0
- package/dist/embed/chat-embed-v1.js.map +7 -0
- package/dist/embed/index.d.ts +9 -0
- package/dist/embed/index.d.ts.map +1 -0
- package/dist/embed/index.js +9 -0
- package/dist/embed/index.js.map +1 -0
- package/dist/embed/publicationApi.d.ts +23 -0
- package/dist/embed/publicationApi.d.ts.map +1 -0
- package/dist/embed/publicationApi.js +141 -0
- package/dist/embed/publicationApi.js.map +1 -0
- package/dist/react/components/ConvaiWidget.d.ts +11 -1
- package/dist/react/components/ConvaiWidget.d.ts.map +1 -1
- package/dist/react/components/ConvaiWidget.js +67 -11
- package/dist/react/components/ConvaiWidget.js.map +1 -1
- package/dist/react/components/rtc-widget/components/conviComponents/SettingsTray.d.ts +1 -1
- package/dist/react/components/rtc-widget/components/conviComponents/SettingsTray.d.ts.map +1 -1
- package/dist/react/components/rtc-widget/components/conviComponents/SettingsTray.js +2 -2
- package/dist/react/components/rtc-widget/components/conviComponents/SettingsTray.js.map +1 -1
- package/dist/react/hooks/useConvaiClient.d.ts +2 -0
- package/dist/react/hooks/useConvaiClient.d.ts.map +1 -1
- package/dist/react/hooks/useConvaiClient.js +8 -0
- package/dist/react/hooks/useConvaiClient.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/vanilla/ConvaiWidget.d.ts.map +1 -1
- package/dist/vanilla/ConvaiWidget.js +153 -32
- package/dist/vanilla/ConvaiWidget.js.map +1 -1
- package/dist/vanilla/WidgetEventSubscriptions.d.ts +15 -0
- package/dist/vanilla/WidgetEventSubscriptions.d.ts.map +1 -0
- package/dist/vanilla/WidgetEventSubscriptions.js +37 -0
- package/dist/vanilla/WidgetEventSubscriptions.js.map +1 -0
- package/dist/vanilla/index.d.ts +1 -1
- package/dist/vanilla/index.d.ts.map +1 -1
- package/dist/vanilla/index.js +1 -1
- package/dist/vanilla/index.js.map +1 -1
- package/dist/vanilla/types.d.ts +8 -1
- package/dist/vanilla/types.d.ts.map +1 -1
- package/dist/vanilla/types.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 +19 -6
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
import { AudioRenderer } from "./AudioRenderer.js";
|
|
6
6
|
import { aeroTheme, injectGlobalStyles, injectKeyframes, } from "./styles.js";
|
|
7
7
|
import { Icons } from "./icons.js";
|
|
8
|
+
import { WidgetEventSubscriptions } from "./WidgetEventSubscriptions.js";
|
|
8
9
|
import { ConvaiClient } from "../core/ConvaiClient.js";
|
|
9
10
|
import { parseInlineMarkdown } from "../utils/inlineMarkdown.js";
|
|
11
|
+
import { freshPublicationGrantOptions } from "../core/publishedChat.js";
|
|
10
12
|
/**
|
|
11
13
|
* Positioning declarations for the widget root, keyed by `placement`. The
|
|
12
14
|
* `bottom-right` entry must stay byte-identical to the literals it replaces --
|
|
@@ -171,7 +173,7 @@ export function createConvaiWidget(container, options) {
|
|
|
171
173
|
blendshapeConfig: options.blendshapeConfig,
|
|
172
174
|
});
|
|
173
175
|
})();
|
|
174
|
-
const { showVideo = true, showScreenShare = true, defaultVoiceMode = true, onConnect, onDisconnect, onMessage, } = options;
|
|
176
|
+
const { showVideo = true, showScreenShare = true, defaultVoiceMode = true, requestPublicationGrant, publishedChatConnectOptions, publishedChatDescriptor, onConnect, onDisconnect, onMessage, } = options;
|
|
175
177
|
// Unknown/typo'd values fall back to the default rather than throwing --
|
|
176
178
|
// a bad `placement` attribute on a customer's page must not blank the
|
|
177
179
|
// widget.
|
|
@@ -183,12 +185,14 @@ export function createConvaiWidget(container, options) {
|
|
|
183
185
|
// State
|
|
184
186
|
let isOpen = false;
|
|
185
187
|
let isSettingsOpen = false;
|
|
186
|
-
let isVoiceMode = defaultVoiceMode
|
|
188
|
+
let isVoiceMode = defaultVoiceMode &&
|
|
189
|
+
!requestPublicationGrant &&
|
|
190
|
+
client.connectionType !== "text";
|
|
187
191
|
let isMuted = false;
|
|
188
192
|
let isVideoVisible = false;
|
|
189
193
|
let inputValue = "";
|
|
190
|
-
let characterName = "Character";
|
|
191
|
-
let characterImage = "";
|
|
194
|
+
let characterName = publishedChatDescriptor?.display_name || "Character";
|
|
195
|
+
let characterImage = publishedChatDescriptor?.image_url || "";
|
|
192
196
|
let audioRenderer = null;
|
|
193
197
|
let hasEnteredDefaultVoiceMode = false;
|
|
194
198
|
/** True after we have connected at least once; used to call reconnect() instead of connect() when opening after disconnect */
|
|
@@ -209,6 +213,9 @@ export function createConvaiWidget(container, options) {
|
|
|
209
213
|
* so a cancellation during the await is honoured.
|
|
210
214
|
*/
|
|
211
215
|
let pendingOpen = false;
|
|
216
|
+
/** Prevent an in-flight publication grant request from connecting after teardown. */
|
|
217
|
+
let isDestroyed = false;
|
|
218
|
+
const eventSubscriptions = new WidgetEventSubscriptions(() => isDestroyed);
|
|
212
219
|
// DOM elements (will be created below)
|
|
213
220
|
let rootElement;
|
|
214
221
|
let morphingContainer;
|
|
@@ -237,6 +244,8 @@ export function createConvaiWidget(container, options) {
|
|
|
237
244
|
let source = null;
|
|
238
245
|
// Fetch character info - matches React useCharacterInfo hook
|
|
239
246
|
const fetchCharacterInfo = async () => {
|
|
247
|
+
if (publishedChatDescriptor)
|
|
248
|
+
return;
|
|
240
249
|
// Prefer authToken over apiKey, matching the precedence ConvaiClient
|
|
241
250
|
// uses when building /connect headers -- a client carrying both must
|
|
242
251
|
// behave identically in both places.
|
|
@@ -1204,7 +1213,8 @@ export function createConvaiWidget(container, options) {
|
|
|
1204
1213
|
if (inputValue.length > 0) {
|
|
1205
1214
|
handleSend();
|
|
1206
1215
|
}
|
|
1207
|
-
else
|
|
1216
|
+
else if (!requestPublicationGrant &&
|
|
1217
|
+
client.connectionType !== "text") {
|
|
1208
1218
|
// Toggle Voice Mode
|
|
1209
1219
|
client.sendInterruptMessage();
|
|
1210
1220
|
try {
|
|
@@ -1319,7 +1329,9 @@ export function createConvaiWidget(container, options) {
|
|
|
1319
1329
|
const resetIcon = Icons.Redo("md");
|
|
1320
1330
|
resetIcon.style.width = "18px";
|
|
1321
1331
|
resetIcon.style.height = "18px";
|
|
1322
|
-
|
|
1332
|
+
if (!client.isPublishedChatSession || requestPublicationGrant) {
|
|
1333
|
+
settingsRow.appendChild(createOption(resetIcon, "Reset", handleReset));
|
|
1334
|
+
}
|
|
1323
1335
|
// Video - only show if connection type is video
|
|
1324
1336
|
const connectionType = client.connectionType;
|
|
1325
1337
|
if (connectionType === "video" && showVideo) {
|
|
@@ -1350,7 +1362,9 @@ export function createConvaiWidget(container, options) {
|
|
|
1350
1362
|
const disconnectIcon = document.createElement("span");
|
|
1351
1363
|
disconnectIcon.innerHTML = "⏻";
|
|
1352
1364
|
disconnectIcon.style.fontSize = "18px";
|
|
1353
|
-
|
|
1365
|
+
if (!client.isPublishedChatSession || requestPublicationGrant) {
|
|
1366
|
+
settingsRow.appendChild(createOption(disconnectIcon, "Disconnect", handleDisconnect, false, true));
|
|
1367
|
+
}
|
|
1354
1368
|
tray.appendChild(settingsRow);
|
|
1355
1369
|
return tray;
|
|
1356
1370
|
};
|
|
@@ -1419,7 +1433,9 @@ export function createConvaiWidget(container, options) {
|
|
|
1419
1433
|
const resetIcon = Icons.Redo("md");
|
|
1420
1434
|
resetIcon.style.width = "18px";
|
|
1421
1435
|
resetIcon.style.height = "18px";
|
|
1422
|
-
|
|
1436
|
+
if (!client.isPublishedChatSession || requestPublicationGrant) {
|
|
1437
|
+
settingsRow.appendChild(createOption(resetIcon, "Reset", handleReset));
|
|
1438
|
+
}
|
|
1423
1439
|
// Video - Always show if showVideo is true and connection type is video
|
|
1424
1440
|
const connectionType = client.connectionType;
|
|
1425
1441
|
if (connectionType === "video" && showVideo) {
|
|
@@ -1450,7 +1466,9 @@ export function createConvaiWidget(container, options) {
|
|
|
1450
1466
|
const disconnectIcon = document.createElement("span");
|
|
1451
1467
|
disconnectIcon.innerHTML = "⏻";
|
|
1452
1468
|
disconnectIcon.style.fontSize = "18px";
|
|
1453
|
-
|
|
1469
|
+
if (!client.isPublishedChatSession || requestPublicationGrant) {
|
|
1470
|
+
settingsRow.appendChild(createOption(disconnectIcon, "Disconnect", handleDisconnect, false, true));
|
|
1471
|
+
}
|
|
1454
1472
|
};
|
|
1455
1473
|
// Create floating video - Matches React FloatingVideo component
|
|
1456
1474
|
const createFloatingVideo = () => {
|
|
@@ -1643,6 +1661,8 @@ export function createConvaiWidget(container, options) {
|
|
|
1643
1661
|
};
|
|
1644
1662
|
// Event handlers
|
|
1645
1663
|
const handleToggle = async () => {
|
|
1664
|
+
if (isDestroyed)
|
|
1665
|
+
return;
|
|
1646
1666
|
if (isOpen || pendingOpen) {
|
|
1647
1667
|
// Already open, or an open is already in flight: nothing to do. The
|
|
1648
1668
|
// `pendingOpen` half is what keeps a repeat call from falling through
|
|
@@ -1654,24 +1674,54 @@ export function createConvaiWidget(container, options) {
|
|
|
1654
1674
|
if (!client.state.isConnected && !client.state.isConnecting) {
|
|
1655
1675
|
pendingOpen = true;
|
|
1656
1676
|
try {
|
|
1657
|
-
|
|
1658
|
-
|
|
1677
|
+
if (requestPublicationGrant || client.isPublishedChatSession) {
|
|
1678
|
+
if (!requestPublicationGrant) {
|
|
1679
|
+
throw new Error("Published chat widgets require requestPublicationGrant to start a new session");
|
|
1680
|
+
}
|
|
1681
|
+
if (!client.connectWithPublicationGrant) {
|
|
1682
|
+
throw new Error("This Convai client does not support published chat connections");
|
|
1683
|
+
}
|
|
1684
|
+
const launchToken = await requestPublicationGrant();
|
|
1685
|
+
// destroy() can run while the grant endpoint is in flight. A one-time
|
|
1686
|
+
// grant must not start a session for a widget which no longer exists.
|
|
1687
|
+
if (isDestroyed) {
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
await client.connectWithPublicationGrant(launchToken, freshPublicationGrantOptions(publishedChatConnectOptions));
|
|
1691
|
+
}
|
|
1692
|
+
else if (hasConnectedBefore && typeof client.reconnect === "function") {
|
|
1659
1693
|
await client.reconnect();
|
|
1660
1694
|
}
|
|
1661
1695
|
else {
|
|
1662
1696
|
await client.connect();
|
|
1663
1697
|
}
|
|
1698
|
+
if (isDestroyed) {
|
|
1699
|
+
// destroy() can also run after the grant is exchanged but before
|
|
1700
|
+
// Core or LiveKit finishes connecting. Disconnect that late session
|
|
1701
|
+
// instead of leaving an invisible room alive.
|
|
1702
|
+
await client.disconnect();
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1664
1705
|
// Re-checked *after* the await: a close()/toggle()/launcher click
|
|
1665
1706
|
// during the connect clears the flag, and that cancellation has to
|
|
1666
1707
|
// win. The client stays connected either way -- only the panel is
|
|
1667
1708
|
// held back -- so a later open() takes the already-connected branch
|
|
1668
1709
|
// and opens instantly.
|
|
1710
|
+
if (isDestroyed) {
|
|
1711
|
+
// destroy() can also run after the grant is exchanged but before
|
|
1712
|
+
// Core or LiveKit finishes connecting. Disconnect that late session
|
|
1713
|
+
// instead of leaving an invisible room alive.
|
|
1714
|
+
await client.disconnect();
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1669
1717
|
if (!pendingOpen)
|
|
1670
1718
|
return;
|
|
1671
1719
|
setIsOpen(true);
|
|
1672
1720
|
}
|
|
1673
1721
|
catch (error) {
|
|
1674
|
-
|
|
1722
|
+
if (!isDestroyed) {
|
|
1723
|
+
console.error("Failed to connect:", error);
|
|
1724
|
+
}
|
|
1675
1725
|
}
|
|
1676
1726
|
finally {
|
|
1677
1727
|
pendingOpen = false;
|
|
@@ -1697,8 +1747,14 @@ export function createConvaiWidget(container, options) {
|
|
|
1697
1747
|
pendingOpen = false;
|
|
1698
1748
|
setIsOpen(false);
|
|
1699
1749
|
};
|
|
1750
|
+
// Published sessions must wait for Core's bot-ready handshake. A normal
|
|
1751
|
+
// authenticated text session still gets the text-only UI, but can send as
|
|
1752
|
+
// soon as it connects.
|
|
1753
|
+
const requiresBotReadyForText = () => Boolean(requestPublicationGrant) || Boolean(client.isPublishedChatSession);
|
|
1754
|
+
const isTextInputReady = () => client.state.isConnected &&
|
|
1755
|
+
(!requiresBotReadyForText() || client.isBotReady);
|
|
1700
1756
|
const handleSend = () => {
|
|
1701
|
-
if (inputValue.trim() &&
|
|
1757
|
+
if (inputValue.trim() && isTextInputReady()) {
|
|
1702
1758
|
client.sendUserTextMessage(inputValue);
|
|
1703
1759
|
inputValue = "";
|
|
1704
1760
|
inputElement.value = "";
|
|
@@ -1805,6 +1861,8 @@ export function createConvaiWidget(container, options) {
|
|
|
1805
1861
|
chatContent.style.pointerEvents = "auto";
|
|
1806
1862
|
// Auto-enter voice mode if defaultVoiceMode is true and not already entered
|
|
1807
1863
|
if (defaultVoiceMode &&
|
|
1864
|
+
!requestPublicationGrant &&
|
|
1865
|
+
client.connectionType !== "text" &&
|
|
1808
1866
|
!hasEnteredDefaultVoiceMode &&
|
|
1809
1867
|
client.state.isConnected) {
|
|
1810
1868
|
setTimeout(async () => {
|
|
@@ -1848,7 +1906,15 @@ export function createConvaiWidget(container, options) {
|
|
|
1848
1906
|
return;
|
|
1849
1907
|
// Clear previous content
|
|
1850
1908
|
sendButton.innerHTML = "";
|
|
1851
|
-
|
|
1909
|
+
// A published session must wait for Core's bot-ready handshake before
|
|
1910
|
+
// accepting text. Preserve the established interactive-widget behavior
|
|
1911
|
+
// for authenticated voice/video sessions.
|
|
1912
|
+
const isPublishedTextChat = requiresBotReadyForText();
|
|
1913
|
+
const textInputReady = isTextInputReady();
|
|
1914
|
+
inputElement.disabled = isPublishedTextChat && !textInputReady;
|
|
1915
|
+
if (inputValue.length > 0 ||
|
|
1916
|
+
requestPublicationGrant ||
|
|
1917
|
+
client.connectionType === "text") {
|
|
1852
1918
|
// Show Send Button
|
|
1853
1919
|
sendButton.appendChild(Icons.Send("md"));
|
|
1854
1920
|
sendButton.style.background = `var(--convai-accent, ${aeroTheme.colors.text.primary})`;
|
|
@@ -1861,7 +1927,20 @@ export function createConvaiWidget(container, options) {
|
|
|
1861
1927
|
// must contrast with each other, since this is the one place they meet.
|
|
1862
1928
|
sendButton.style.color = "var(--convai-panel-bg, white)";
|
|
1863
1929
|
sendButton.style.border = "none";
|
|
1864
|
-
sendButton.
|
|
1930
|
+
sendButton.disabled =
|
|
1931
|
+
isPublishedTextChat && (inputValue.length === 0 || !textInputReady);
|
|
1932
|
+
if (isPublishedTextChat) {
|
|
1933
|
+
sendButton.style.opacity =
|
|
1934
|
+
inputValue.length > 0 && textInputReady ? "1" : "0.5";
|
|
1935
|
+
sendButton.title = textInputReady
|
|
1936
|
+
? inputValue.length > 0
|
|
1937
|
+
? "Send"
|
|
1938
|
+
: "Type a message"
|
|
1939
|
+
: "Waiting for character";
|
|
1940
|
+
}
|
|
1941
|
+
else {
|
|
1942
|
+
sendButton.title = "Send";
|
|
1943
|
+
}
|
|
1865
1944
|
}
|
|
1866
1945
|
else {
|
|
1867
1946
|
// Show Voice Mode Toggle
|
|
@@ -1869,6 +1948,8 @@ export function createConvaiWidget(container, options) {
|
|
|
1869
1948
|
sendButton.style.background = "transparent";
|
|
1870
1949
|
sendButton.style.color = `var(--convai-panel-fg, ${aeroTheme.colors.text.primary})`;
|
|
1871
1950
|
sendButton.style.border = `1px solid ${aeroTheme.colors.neutral[300]}`;
|
|
1951
|
+
sendButton.style.opacity = "1";
|
|
1952
|
+
sendButton.disabled = false;
|
|
1872
1953
|
sendButton.title = "Voice Mode";
|
|
1873
1954
|
}
|
|
1874
1955
|
};
|
|
@@ -2270,7 +2351,17 @@ export function createConvaiWidget(container, options) {
|
|
|
2270
2351
|
};
|
|
2271
2352
|
// Setup event listeners for client state changes
|
|
2272
2353
|
const setupClientListeners = () => {
|
|
2273
|
-
client.on("stateChange", () => {
|
|
2354
|
+
eventSubscriptions.register((listener) => client.on("stateChange", listener), () => {
|
|
2355
|
+
// Published chat (connectionType "text") has no microphone: never let
|
|
2356
|
+
// a voice-mode transition leave the panel in a mode it cannot honour.
|
|
2357
|
+
if (client.connectionType === "text") {
|
|
2358
|
+
if (isVoiceMode) {
|
|
2359
|
+
isVoiceMode = false;
|
|
2360
|
+
hasEnteredDefaultVoiceMode = true;
|
|
2361
|
+
void updateVoiceMode();
|
|
2362
|
+
}
|
|
2363
|
+
updateSendButton();
|
|
2364
|
+
}
|
|
2274
2365
|
updateHeader();
|
|
2275
2366
|
// Update pulse animation based on connecting state
|
|
2276
2367
|
if (client.state.isConnecting) {
|
|
@@ -2328,7 +2419,7 @@ export function createConvaiWidget(container, options) {
|
|
|
2328
2419
|
});
|
|
2329
2420
|
// Audio state changes now only affect voice mode (microphone button removed)
|
|
2330
2421
|
// Video state change listener
|
|
2331
|
-
client.videoControls.on("videoStateChange", (videoState) => {
|
|
2422
|
+
eventSubscriptions.register((listener) => client.videoControls.on("videoStateChange", listener), (videoState) => {
|
|
2332
2423
|
if (videoState.isVideoEnabled !== undefined) {
|
|
2333
2424
|
isVideoVisible = videoState.isVideoEnabled;
|
|
2334
2425
|
applyVoiceMode(); // synchronous listener; see applyVoiceMode()
|
|
@@ -2381,19 +2472,15 @@ export function createConvaiWidget(container, options) {
|
|
|
2381
2472
|
}
|
|
2382
2473
|
}
|
|
2383
2474
|
});
|
|
2384
|
-
client.on("messagesChange", () => {
|
|
2475
|
+
eventSubscriptions.register((listener) => client.on("messagesChange", listener), () => {
|
|
2385
2476
|
updateMessageList();
|
|
2386
|
-
// Call onMessage callback if provided
|
|
2387
|
-
if (onMessage && client.chatMessages.length > 0) {
|
|
2388
|
-
const lastMessage = client.chatMessages[client.chatMessages.length - 1];
|
|
2389
|
-
onMessage(lastMessage);
|
|
2390
|
-
}
|
|
2391
2477
|
});
|
|
2392
2478
|
// Bot ready listener - updates UI when bot becomes ready
|
|
2393
|
-
client.on("botReady", () => {
|
|
2479
|
+
eventSubscriptions.register((listener) => client.on("botReady", listener), () => {
|
|
2394
2480
|
updateHeader(); // Update header to show green status
|
|
2481
|
+
updateSendButton();
|
|
2395
2482
|
});
|
|
2396
|
-
client.on("connect", () => {
|
|
2483
|
+
eventSubscriptions.register((listener) => client.on("connect", listener), () => {
|
|
2397
2484
|
hasConnectedBefore = true;
|
|
2398
2485
|
// Initialize audio renderer on connection
|
|
2399
2486
|
// NOTE: the audio elements live under rootElement, so playback ends if
|
|
@@ -2411,7 +2498,7 @@ export function createConvaiWidget(container, options) {
|
|
|
2411
2498
|
onConnect();
|
|
2412
2499
|
}
|
|
2413
2500
|
});
|
|
2414
|
-
client.on("disconnect", () => {
|
|
2501
|
+
eventSubscriptions.register((listener) => client.on("disconnect", listener), () => {
|
|
2415
2502
|
// Cleanup audio renderer
|
|
2416
2503
|
if (audioRenderer) {
|
|
2417
2504
|
audioRenderer.destroy();
|
|
@@ -2423,6 +2510,42 @@ export function createConvaiWidget(container, options) {
|
|
|
2423
2510
|
onDisconnect();
|
|
2424
2511
|
}
|
|
2425
2512
|
});
|
|
2513
|
+
// `message` carries the row actually updated. That can differ from the
|
|
2514
|
+
// list tail when correlated v2 streams interleave, which is why onMessage
|
|
2515
|
+
// no longer reads the last element of chatMessages.
|
|
2516
|
+
eventSubscriptions.register((listener) => client.on("message", listener), (message) => {
|
|
2517
|
+
const row = message;
|
|
2518
|
+
if (onMessage && row) {
|
|
2519
|
+
onMessage(row);
|
|
2520
|
+
}
|
|
2521
|
+
});
|
|
2522
|
+
// Screen share state change listener
|
|
2523
|
+
eventSubscriptions.register((listener) => client.screenShareControls.on("screenShareStateChange", listener), (screenShareState) => {
|
|
2524
|
+
if (screenShareState.isScreenShareActive !== undefined) {
|
|
2525
|
+
const isSharing = screenShareState.isScreenShareActive;
|
|
2526
|
+
const shareBtn = document.getElementById("convai-settings-share-btn");
|
|
2527
|
+
if (shareBtn) {
|
|
2528
|
+
const isActive = isSharing;
|
|
2529
|
+
shareBtn.style.backgroundColor = isActive
|
|
2530
|
+
? "rgba(16, 185, 129, 0.15)"
|
|
2531
|
+
: "transparent";
|
|
2532
|
+
shareBtn.style.color = isActive
|
|
2533
|
+
? "#10b981"
|
|
2534
|
+
: aeroTheme.colors.text.primary;
|
|
2535
|
+
// Update Icon - first child is icon container
|
|
2536
|
+
const iconContainer = shareBtn.querySelector("div");
|
|
2537
|
+
if (iconContainer) {
|
|
2538
|
+
iconContainer.innerHTML = "";
|
|
2539
|
+
const icon = isActive
|
|
2540
|
+
? Icons.StopScreenShare("md")
|
|
2541
|
+
: Icons.ScreenShare("md");
|
|
2542
|
+
icon.style.width = "18px";
|
|
2543
|
+
icon.style.height = "18px";
|
|
2544
|
+
iconContainer.appendChild(icon);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2426
2549
|
};
|
|
2427
2550
|
// Initialize
|
|
2428
2551
|
createDOM();
|
|
@@ -2509,6 +2632,7 @@ export function createConvaiWidget(container, options) {
|
|
|
2509
2632
|
updateMessageList();
|
|
2510
2633
|
},
|
|
2511
2634
|
destroy: () => {
|
|
2635
|
+
isDestroyed = true;
|
|
2512
2636
|
// Cleanup audio renderer
|
|
2513
2637
|
if (audioRenderer) {
|
|
2514
2638
|
audioRenderer.destroy();
|
|
@@ -2523,12 +2647,9 @@ export function createConvaiWidget(container, options) {
|
|
|
2523
2647
|
if (floatingVideo.parentElement) {
|
|
2524
2648
|
floatingVideo.remove();
|
|
2525
2649
|
}
|
|
2526
|
-
// Unsubscribe
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
client.off("botReady", () => { });
|
|
2530
|
-
client.off("connect", () => { });
|
|
2531
|
-
client.off("disconnect", () => { });
|
|
2650
|
+
// Unsubscribe using the exact callbacks registered above. The guards
|
|
2651
|
+
// also suppress an event that was queued just before teardown.
|
|
2652
|
+
eventSubscriptions.unsubscribeAll();
|
|
2532
2653
|
},
|
|
2533
2654
|
};
|
|
2534
2655
|
return widget;
|