@burdenoff/website-sdk 2026.828.7 → 2026.829.2

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.
@@ -1,4 +1,4 @@
1
- export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-DhvZhH8f.mjs';
1
+ export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-8eL7WdLc.mjs';
2
2
 
3
3
  interface UseSpeechInputOptions {
4
4
  /** Receives settled text as it is recognised. Append it to the draft. */
@@ -1,4 +1,4 @@
1
- export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-DhvZhH8f.js';
1
+ export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, F as FALLBACK_EXPLORE_LIMITS, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from '../use-explore-chat-8eL7WdLc.js';
2
2
 
3
3
  interface UseSpeechInputOptions {
4
4
  /** Receives settled text as it is recognised. Append it to the draft. */
@@ -244,6 +244,7 @@ var EXPLORE_MESSAGE_FIELDS = (
244
244
  description
245
245
  product
246
246
  imageUrl
247
+ index
247
248
  }
248
249
  ctas {
249
250
  kind
@@ -251,6 +252,8 @@ var EXPLORE_MESSAGE_FIELDS = (
251
252
  url
252
253
  product
253
254
  }
255
+ followUps
256
+ answered
254
257
  errorCode
255
258
  createdAt
256
259
  completedAt
@@ -316,6 +319,16 @@ var SEND_EXPLORE_MESSAGE_MUTATION = (
316
319
  }
317
320
  `
318
321
  );
322
+ var RECORD_EXPLORE_CLICK_MUTATION = (
323
+ /* GraphQL */
324
+ `
325
+ mutation RecordExploreClick($input: RecordExploreClickInput!) {
326
+ recordExploreClick(input: $input) {
327
+ recorded
328
+ }
329
+ }
330
+ `
331
+ );
319
332
 
320
333
  // src/data/products.ts
321
334
  var ECOSYSTEM_PRODUCTS = [
@@ -1248,6 +1261,20 @@ function useExploreChat(options = {}) {
1248
1261
  return null;
1249
1262
  }, [messages]);
1250
1263
  const canSend = (phase === "ready" || phase === "error") && catalog.enabled && turnCount < catalog.limits.maxTurnsPerConversation;
1264
+ const recordClick = react.useCallback(
1265
+ (messageId, kind, url) => {
1266
+ const token = conversationTokenRef.current;
1267
+ if (!token) return;
1268
+ try {
1269
+ void client.mutate(RECORD_EXPLORE_CLICK_MUTATION, {
1270
+ input: { conversationToken: token, messageId, kind, url }
1271
+ }).catch(() => {
1272
+ });
1273
+ } catch {
1274
+ }
1275
+ },
1276
+ [client]
1277
+ );
1251
1278
  return {
1252
1279
  phase,
1253
1280
  catalog,
@@ -1265,7 +1292,8 @@ function useExploreChat(options = {}) {
1265
1292
  history: archive,
1266
1293
  openConversation,
1267
1294
  deleteConversation,
1268
- clearHistory
1295
+ clearHistory,
1296
+ recordClick
1269
1297
  };
1270
1298
  }
1271
1299
  var optimisticCounter = 0;
@@ -1278,21 +1306,37 @@ function getRecognitionCtor() {
1278
1306
  const w = window;
1279
1307
  return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
1280
1308
  }
1309
+ function isTouchDevice() {
1310
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1311
+ try {
1312
+ return window.matchMedia("(pointer: coarse)").matches;
1313
+ } catch {
1314
+ return false;
1315
+ }
1316
+ }
1281
1317
  var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
1318
+ var MIC_UNAVAILABLE_MESSAGE = "Dictation isn't available in this browser. You can type instead.";
1319
+ var RELEASE_SETTLE_MS = 250;
1320
+ var RETRY_DELAY_MS = 350;
1321
+ var sleep = (ms) => new Promise((resolve) => {
1322
+ setTimeout(resolve, ms);
1323
+ });
1282
1324
  async function ensureMicrophoneAccess() {
1283
1325
  const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
1284
- if (!media?.getUserMedia) return { ok: true, confirmed: false };
1326
+ if (!media?.getUserMedia)
1327
+ return { ok: true, confirmed: false, primed: false };
1285
1328
  try {
1286
1329
  const status = await navigator.permissions?.query({
1287
1330
  name: "microphone"
1288
1331
  });
1289
- if (status?.state === "granted") return { ok: true, confirmed: true };
1332
+ if (status?.state === "granted")
1333
+ return { ok: true, confirmed: true, primed: false };
1290
1334
  } catch {
1291
1335
  }
1292
1336
  try {
1293
1337
  const stream = await media.getUserMedia({ audio: true });
1294
1338
  for (const track of stream.getTracks()) track.stop();
1295
- return { ok: true, confirmed: true };
1339
+ return { ok: true, confirmed: true, primed: true };
1296
1340
  } catch (error) {
1297
1341
  const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
1298
1342
  if (name === "NotAllowedError" || name === "SecurityError") {
@@ -1336,19 +1380,17 @@ function useSpeechInput({
1336
1380
  const recognitionRef = react.useRef(null);
1337
1381
  const startTokenRef = react.useRef(0);
1338
1382
  const micConfirmedRef = react.useRef(false);
1339
- const beginRecognitionRef = react.useRef(null);
1340
- const beginRecognition = react.useCallback(
1341
- (Ctor, token) => {
1342
- beginRecognitionRef.current?.(Ctor, token);
1343
- },
1344
- []
1345
- );
1383
+ const wantsListeningRef = react.useRef(false);
1384
+ const retriedRef = react.useRef(false);
1346
1385
  const finalRef = react.useRef(onFinalTranscript);
1347
1386
  const errorRef = react.useRef(onError);
1348
1387
  finalRef.current = onFinalTranscript;
1349
1388
  errorRef.current = onError;
1389
+ const langRef = react.useRef(lang);
1390
+ langRef.current = lang;
1350
1391
  const stop = react.useCallback(() => {
1351
1392
  startTokenRef.current += 1;
1393
+ wantsListeningRef.current = false;
1352
1394
  setListening(false);
1353
1395
  setInterim("");
1354
1396
  const recognition = recognitionRef.current;
@@ -1357,83 +1399,111 @@ function useSpeechInput({
1357
1399
  recognition.stop();
1358
1400
  } catch {
1359
1401
  }
1360
- setListening(false);
1361
- setInterim("");
1362
1402
  }, []);
1363
- const start = react.useCallback(() => {
1403
+ const openSession = react.useCallback((token) => {
1364
1404
  const Ctor = getRecognitionCtor();
1365
- if (!Ctor) return;
1366
- if (recognitionRef.current) stop();
1367
- setError(null);
1368
- setListening(true);
1405
+ if (!Ctor || token !== startTokenRef.current) return;
1406
+ const recognition = new Ctor();
1407
+ recognition.lang = langRef.current ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
1408
+ recognition.continuous = !isTouchDevice();
1409
+ recognition.interimResults = true;
1410
+ recognition.maxAlternatives = 1;
1411
+ recognition.onstart = () => {
1412
+ if (token !== startTokenRef.current) return;
1413
+ setError(null);
1414
+ setListening(true);
1415
+ };
1416
+ recognition.onresult = (event) => {
1417
+ if (token !== startTokenRef.current) return;
1418
+ let settled = "";
1419
+ let pending = "";
1420
+ for (let i = event.resultIndex; i < event.results.length; i += 1) {
1421
+ const result = event.results[i];
1422
+ if (!result) continue;
1423
+ const text = result[0]?.transcript ?? "";
1424
+ if (result.isFinal) settled += text;
1425
+ else pending += text;
1426
+ }
1427
+ setInterim(pending);
1428
+ if (settled.trim() !== "") finalRef.current(settled);
1429
+ };
1430
+ recognition.onerror = (event) => {
1431
+ if (token !== startTokenRef.current) return;
1432
+ if ((event.error === "not-allowed" || event.error === "service-not-allowed") && micConfirmedRef.current && !retriedRef.current) {
1433
+ retriedRef.current = true;
1434
+ void sleep(RETRY_DELAY_MS).then(() => {
1435
+ if (token !== startTokenRef.current || !wantsListeningRef.current)
1436
+ return;
1437
+ openSessionRef.current?.(token);
1438
+ });
1439
+ return;
1440
+ }
1441
+ const message = micConfirmedRef.current && (event.error === "not-allowed" || event.error === "service-not-allowed") ? MIC_UNAVAILABLE_MESSAGE : describeError(event.error);
1442
+ wantsListeningRef.current = false;
1443
+ setListening(false);
1444
+ setInterim("");
1445
+ if (message !== "") {
1446
+ setError(message);
1447
+ errorRef.current?.(message);
1448
+ }
1449
+ };
1450
+ recognition.onend = () => {
1451
+ if (token !== startTokenRef.current) return;
1452
+ setInterim("");
1453
+ if (wantsListeningRef.current && isTouchDevice()) {
1454
+ void sleep(120).then(() => {
1455
+ if (token !== startTokenRef.current || !wantsListeningRef.current)
1456
+ return;
1457
+ openSessionRef.current?.(token);
1458
+ });
1459
+ return;
1460
+ }
1461
+ setListening(false);
1462
+ };
1463
+ recognitionRef.current = recognition;
1464
+ try {
1465
+ recognition.start();
1466
+ } catch {
1467
+ wantsListeningRef.current = false;
1468
+ setListening(false);
1469
+ }
1470
+ }, []);
1471
+ const openSessionRef = react.useRef(null);
1472
+ openSessionRef.current = openSession;
1473
+ const start = react.useCallback(() => {
1474
+ if (!getRecognitionCtor()) return;
1369
1475
  startTokenRef.current += 1;
1370
1476
  const token = startTokenRef.current;
1371
- void ensureMicrophoneAccess().then((access) => {
1477
+ wantsListeningRef.current = true;
1478
+ retriedRef.current = false;
1479
+ micConfirmedRef.current = false;
1480
+ setError(null);
1481
+ setListening(true);
1482
+ void ensureMicrophoneAccess().then(async (access) => {
1372
1483
  if (token !== startTokenRef.current) return;
1373
- micConfirmedRef.current = access.ok && access.confirmed;
1374
1484
  if (!access.ok) {
1485
+ wantsListeningRef.current = false;
1375
1486
  setListening(false);
1376
1487
  setError(access.message);
1377
1488
  errorRef.current?.(access.message);
1378
1489
  return;
1379
1490
  }
1380
- beginRecognition(Ctor, token);
1381
- });
1382
- }, [beginRecognition, stop]);
1383
- const beginRecognitionImpl = react.useCallback(
1384
- (Ctor, token) => {
1385
- const recognition = new Ctor();
1386
- recognition.lang = lang ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
1387
- recognition.continuous = true;
1388
- recognition.interimResults = true;
1389
- recognition.maxAlternatives = 1;
1390
- recognition.onstart = () => {
1491
+ micConfirmedRef.current = access.confirmed;
1492
+ if (access.primed) {
1493
+ await sleep(RELEASE_SETTLE_MS);
1391
1494
  if (token !== startTokenRef.current) return;
1392
- setError(null);
1393
- setListening(true);
1394
- };
1395
- recognition.onresult = (event) => {
1396
- let settled = "";
1397
- let pending = "";
1398
- for (let i = event.resultIndex; i < event.results.length; i += 1) {
1399
- const result = event.results[i];
1400
- if (!result) continue;
1401
- const text = result[0]?.transcript ?? "";
1402
- if (result.isFinal) settled += text;
1403
- else pending += text;
1404
- }
1405
- setInterim(pending);
1406
- if (settled.trim() !== "") finalRef.current(settled);
1407
- };
1408
- recognition.onerror = (event) => {
1409
- const message = micConfirmedRef.current && (event.error === "not-allowed" || event.error === "service-not-allowed") ? "Dictation isn't available in this browser. You can type instead." : describeError(event.error);
1410
- setListening(false);
1411
- setInterim("");
1412
- if (message !== "") {
1413
- setError(message);
1414
- errorRef.current?.(message);
1415
- }
1416
- };
1417
- recognition.onend = () => {
1418
- setListening(false);
1419
- setInterim("");
1420
- };
1421
- recognitionRef.current = recognition;
1422
- try {
1423
- recognition.start();
1424
- } catch {
1425
- setListening(false);
1426
1495
  }
1427
- },
1428
- [lang]
1429
- );
1430
- beginRecognitionRef.current = beginRecognitionImpl;
1496
+ openSessionRef.current?.(token);
1497
+ });
1498
+ }, []);
1431
1499
  const toggle = react.useCallback(() => {
1432
1500
  if (listening) stop();
1433
1501
  else start();
1434
1502
  }, [listening, start, stop]);
1435
1503
  react.useEffect(
1436
1504
  () => () => {
1505
+ startTokenRef.current += 1;
1506
+ wantsListeningRef.current = false;
1437
1507
  const recognition = recognitionRef.current;
1438
1508
  if (!recognition) return;
1439
1509
  recognition.onresult = null;