@burdenoff/website-sdk 2026.828.4 → 2026.828.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/dist/components/explore-cta.js +9 -7
- package/dist/components/explore-cta.js.map +1 -1
- package/dist/components/explore-cta.mjs +9 -7
- package/dist/components/explore-cta.mjs.map +1 -1
- package/dist/components/explore.d.mts +11 -1
- package/dist/components/explore.d.ts +11 -1
- package/dist/components/explore.js +561 -57
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +563 -60
- package/dist/components/explore.mjs.map +1 -1
- package/dist/hooks/index.d.mts +24 -1
- package/dist/hooks/index.d.ts +24 -1
- package/dist/hooks/index.js +298 -5
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +298 -6
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +569 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +570 -65
- package/dist/index.mjs.map +1 -1
- package/dist/{index-CUJTTlYx.d.mts → use-explore-chat-DhvZhH8f.d.mts} +21 -2
- package/dist/{index-CUJTTlYx.d.ts → use-explore-chat-DhvZhH8f.d.ts} +21 -2
- package/package.json +1 -1
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1 +1,24 @@
|
|
|
1
|
-
export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS,
|
|
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';
|
|
2
|
+
|
|
3
|
+
interface UseSpeechInputOptions {
|
|
4
|
+
/** Receives settled text as it is recognised. Append it to the draft. */
|
|
5
|
+
onFinalTranscript: (text: string) => void;
|
|
6
|
+
/** BCP-47 tag. Defaults to the browser's language. */
|
|
7
|
+
lang?: string;
|
|
8
|
+
/** Called when the recogniser reports a problem worth showing. */
|
|
9
|
+
onError?: (message: string) => void;
|
|
10
|
+
}
|
|
11
|
+
interface UseSpeechInputResult {
|
|
12
|
+
/** False when the browser has no speech recogniser — hide the control entirely. */
|
|
13
|
+
supported: boolean;
|
|
14
|
+
listening: boolean;
|
|
15
|
+
/** Words recognised but not yet settled; render as a live hint, do not store. */
|
|
16
|
+
interim: string;
|
|
17
|
+
error: string | null;
|
|
18
|
+
start: () => void;
|
|
19
|
+
stop: () => void;
|
|
20
|
+
toggle: () => void;
|
|
21
|
+
}
|
|
22
|
+
declare function useSpeechInput({ onFinalTranscript, lang, onError, }: UseSpeechInputOptions): UseSpeechInputResult;
|
|
23
|
+
|
|
24
|
+
export { type UseSpeechInputOptions, type UseSpeechInputResult, useSpeechInput };
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1 +1,24 @@
|
|
|
1
|
-
export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS,
|
|
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';
|
|
2
|
+
|
|
3
|
+
interface UseSpeechInputOptions {
|
|
4
|
+
/** Receives settled text as it is recognised. Append it to the draft. */
|
|
5
|
+
onFinalTranscript: (text: string) => void;
|
|
6
|
+
/** BCP-47 tag. Defaults to the browser's language. */
|
|
7
|
+
lang?: string;
|
|
8
|
+
/** Called when the recogniser reports a problem worth showing. */
|
|
9
|
+
onError?: (message: string) => void;
|
|
10
|
+
}
|
|
11
|
+
interface UseSpeechInputResult {
|
|
12
|
+
/** False when the browser has no speech recogniser — hide the control entirely. */
|
|
13
|
+
supported: boolean;
|
|
14
|
+
listening: boolean;
|
|
15
|
+
/** Words recognised but not yet settled; render as a live hint, do not store. */
|
|
16
|
+
interim: string;
|
|
17
|
+
error: string | null;
|
|
18
|
+
start: () => void;
|
|
19
|
+
stop: () => void;
|
|
20
|
+
toggle: () => void;
|
|
21
|
+
}
|
|
22
|
+
declare function useSpeechInput({ onFinalTranscript, lang, onError, }: UseSpeechInputOptions): UseSpeechInputResult;
|
|
23
|
+
|
|
24
|
+
export { type UseSpeechInputOptions, type UseSpeechInputResult, useSpeechInput };
|
package/dist/hooks/index.js
CHANGED
|
@@ -561,6 +561,8 @@ async function getRecaptchaV3Token(siteKey, action = EXPLORE_RECAPTCHA_ACTION) {
|
|
|
561
561
|
|
|
562
562
|
// src/hooks/use-explore-chat.ts
|
|
563
563
|
var DEFAULT_STORAGE_KEY = "boff.explore.v1";
|
|
564
|
+
var HISTORY_SUFFIX = ".history";
|
|
565
|
+
var MAX_ARCHIVED_CONVERSATIONS = 15;
|
|
564
566
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
565
567
|
var DEFAULT_POLL_TIMEOUT_MS = 9e4;
|
|
566
568
|
var POLL_REQUEST_TIMEOUT_MS = 1e4;
|
|
@@ -699,10 +701,58 @@ function classifyExploreError(code, serverMessage) {
|
|
|
699
701
|
retryable: true
|
|
700
702
|
};
|
|
701
703
|
}
|
|
704
|
+
function historyKey(key) {
|
|
705
|
+
return `${key}${HISTORY_SUFFIX}`;
|
|
706
|
+
}
|
|
707
|
+
function readArchive(key) {
|
|
708
|
+
if (typeof window === "undefined") return [];
|
|
709
|
+
try {
|
|
710
|
+
const raw = window.localStorage.getItem(historyKey(key));
|
|
711
|
+
if (!raw) return [];
|
|
712
|
+
const parsed = JSON.parse(raw);
|
|
713
|
+
if (!Array.isArray(parsed)) return [];
|
|
714
|
+
return parsed.filter(
|
|
715
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.token === "string" && Array.isArray(entry.messages)
|
|
716
|
+
);
|
|
717
|
+
} catch {
|
|
718
|
+
return [];
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
function writeArchive(key, entries) {
|
|
722
|
+
if (typeof window === "undefined") return;
|
|
723
|
+
try {
|
|
724
|
+
if (entries.length === 0) {
|
|
725
|
+
window.localStorage.removeItem(historyKey(key));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
window.localStorage.setItem(historyKey(key), JSON.stringify(entries));
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function upsertArchive(key, thread) {
|
|
733
|
+
if (!thread.token) return readArchive(key);
|
|
734
|
+
const real = thread.messages.filter(
|
|
735
|
+
(m) => m.content.trim() !== "" || m.role === "USER"
|
|
736
|
+
);
|
|
737
|
+
if (real.length === 0) return readArchive(key);
|
|
738
|
+
const firstUser = real.find((m) => m.role === "USER");
|
|
739
|
+
const entry = {
|
|
740
|
+
token: thread.token,
|
|
741
|
+
title: (firstUser?.content ?? "Conversation").trim().slice(0, 80),
|
|
742
|
+
updatedAt: real[real.length - 1]?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
743
|
+
messageCount: real.length,
|
|
744
|
+
focusProduct: thread.focusProduct,
|
|
745
|
+
messages: real.slice(-20)
|
|
746
|
+
};
|
|
747
|
+
const rest = readArchive(key).filter((e) => e.token !== entry.token);
|
|
748
|
+
const next = [entry, ...rest].slice(0, MAX_ARCHIVED_CONVERSATIONS);
|
|
749
|
+
writeArchive(key, next);
|
|
750
|
+
return next;
|
|
751
|
+
}
|
|
702
752
|
function readPersisted(key) {
|
|
703
753
|
if (typeof window === "undefined") return null;
|
|
704
754
|
try {
|
|
705
|
-
const raw = window.
|
|
755
|
+
const raw = window.localStorage.getItem(key);
|
|
706
756
|
if (!raw) return null;
|
|
707
757
|
const parsed = JSON.parse(raw);
|
|
708
758
|
if (!parsed || typeof parsed !== "object") return null;
|
|
@@ -747,6 +797,8 @@ function useExploreChat(options = {}) {
|
|
|
747
797
|
const [remainingToday, setRemainingToday] = react.useState(null);
|
|
748
798
|
const [focusProduct, setFocusProductState] = react.useState(null);
|
|
749
799
|
const [turnCount, setTurnCount] = react.useState(0);
|
|
800
|
+
const messagesRef = react.useRef([]);
|
|
801
|
+
const [archive, setArchive] = react.useState([]);
|
|
750
802
|
const [hydrated, setHydrated] = react.useState(false);
|
|
751
803
|
const mountedRef = react.useRef(true);
|
|
752
804
|
const pollGenerationRef = react.useRef(0);
|
|
@@ -784,6 +836,7 @@ function useExploreChat(options = {}) {
|
|
|
784
836
|
setHydrated(true);
|
|
785
837
|
return;
|
|
786
838
|
}
|
|
839
|
+
setArchive(readArchive(storageKey));
|
|
787
840
|
const stored = readPersisted(storageKey);
|
|
788
841
|
if (stored) {
|
|
789
842
|
conversationTokenRef.current = stored.conversationToken;
|
|
@@ -812,13 +865,28 @@ function useExploreChat(options = {}) {
|
|
|
812
865
|
focusProduct
|
|
813
866
|
};
|
|
814
867
|
if (!payload.conversationToken && payload.messages.length === 0 && !payload.focusProduct) {
|
|
815
|
-
window.
|
|
868
|
+
window.localStorage.removeItem(storageKey);
|
|
816
869
|
return;
|
|
817
870
|
}
|
|
818
|
-
window.
|
|
871
|
+
window.localStorage.setItem(storageKey, JSON.stringify(payload));
|
|
819
872
|
} catch {
|
|
820
873
|
}
|
|
821
874
|
}, [persist, hydrated, storageKey, messages, focusProduct]);
|
|
875
|
+
react.useEffect(() => {
|
|
876
|
+
messagesRef.current = messages;
|
|
877
|
+
if (!persist || !hydrated) return;
|
|
878
|
+
const settled = messages.some(
|
|
879
|
+
(m) => m.role === "ASSISTANT" && (m.status === "COMPLETED" || m.status === "FAILED")
|
|
880
|
+
);
|
|
881
|
+
if (!settled) return;
|
|
882
|
+
setArchive(
|
|
883
|
+
upsertArchive(storageKey, {
|
|
884
|
+
token: conversationTokenRef.current,
|
|
885
|
+
messages,
|
|
886
|
+
focusProduct: focusProductRef.current
|
|
887
|
+
})
|
|
888
|
+
);
|
|
889
|
+
}, [messages, persist, hydrated, storageKey]);
|
|
822
890
|
react.useEffect(() => {
|
|
823
891
|
let cancelled = false;
|
|
824
892
|
const load = async () => {
|
|
@@ -1104,6 +1172,13 @@ function useExploreChat(options = {}) {
|
|
|
1104
1172
|
void send(text);
|
|
1105
1173
|
}, [cancelPolling, send]);
|
|
1106
1174
|
const reset = react.useCallback(() => {
|
|
1175
|
+
setArchive(
|
|
1176
|
+
upsertArchive(storageKey, {
|
|
1177
|
+
token: conversationTokenRef.current,
|
|
1178
|
+
messages: messagesRef.current,
|
|
1179
|
+
focusProduct: focusProductRef.current
|
|
1180
|
+
})
|
|
1181
|
+
);
|
|
1107
1182
|
cancelPolling();
|
|
1108
1183
|
inFlightRef.current = false;
|
|
1109
1184
|
conversationTokenRef.current = null;
|
|
@@ -1116,11 +1191,48 @@ function useExploreChat(options = {}) {
|
|
|
1116
1191
|
setPhase(catalogRef.current.enabled ? "ready" : "disabled");
|
|
1117
1192
|
if (typeof window !== "undefined") {
|
|
1118
1193
|
try {
|
|
1119
|
-
window.
|
|
1194
|
+
window.localStorage.removeItem(storageKey);
|
|
1120
1195
|
} catch {
|
|
1121
1196
|
}
|
|
1122
1197
|
}
|
|
1123
1198
|
}, [cancelPolling, storageKey]);
|
|
1199
|
+
const openConversation = react.useCallback(
|
|
1200
|
+
(token) => {
|
|
1201
|
+
const entry = readArchive(storageKey).find((e) => e.token === token);
|
|
1202
|
+
if (!entry) return;
|
|
1203
|
+
upsertArchive(storageKey, {
|
|
1204
|
+
token: conversationTokenRef.current,
|
|
1205
|
+
messages: messagesRef.current,
|
|
1206
|
+
focusProduct: focusProductRef.current
|
|
1207
|
+
});
|
|
1208
|
+
cancelPolling();
|
|
1209
|
+
inFlightRef.current = false;
|
|
1210
|
+
conversationTokenRef.current = entry.token;
|
|
1211
|
+
turnCountRef.current = entry.messages.filter(
|
|
1212
|
+
(m) => m.role === "USER"
|
|
1213
|
+
).length;
|
|
1214
|
+
setTurnCount(turnCountRef.current);
|
|
1215
|
+
setMessages(entry.messages);
|
|
1216
|
+
focusProductRef.current = entry.focusProduct;
|
|
1217
|
+
setFocusProductState(entry.focusProduct);
|
|
1218
|
+
setError(null);
|
|
1219
|
+
setPhase(catalogRef.current.enabled ? "ready" : "disabled");
|
|
1220
|
+
setArchive(readArchive(storageKey));
|
|
1221
|
+
},
|
|
1222
|
+
[cancelPolling, storageKey]
|
|
1223
|
+
);
|
|
1224
|
+
const deleteConversation = react.useCallback(
|
|
1225
|
+
(token) => {
|
|
1226
|
+
const next = readArchive(storageKey).filter((e) => e.token !== token);
|
|
1227
|
+
writeArchive(storageKey, next);
|
|
1228
|
+
setArchive(next);
|
|
1229
|
+
},
|
|
1230
|
+
[storageKey]
|
|
1231
|
+
);
|
|
1232
|
+
const clearHistory = react.useCallback(() => {
|
|
1233
|
+
writeArchive(storageKey, []);
|
|
1234
|
+
setArchive([]);
|
|
1235
|
+
}, [storageKey]);
|
|
1124
1236
|
const setFocusProduct = react.useCallback((slug) => {
|
|
1125
1237
|
focusProductRef.current = slug;
|
|
1126
1238
|
setFocusProductState(slug);
|
|
@@ -1149,7 +1261,11 @@ function useExploreChat(options = {}) {
|
|
|
1149
1261
|
stop,
|
|
1150
1262
|
retry,
|
|
1151
1263
|
reset,
|
|
1152
|
-
canSend
|
|
1264
|
+
canSend,
|
|
1265
|
+
history: archive,
|
|
1266
|
+
openConversation,
|
|
1267
|
+
deleteConversation,
|
|
1268
|
+
clearHistory
|
|
1153
1269
|
};
|
|
1154
1270
|
}
|
|
1155
1271
|
var optimisticCounter = 0;
|
|
@@ -1157,10 +1273,187 @@ function makeOptimisticId() {
|
|
|
1157
1273
|
optimisticCounter += 1;
|
|
1158
1274
|
return `boff-explore-local-${Date.now()}-${optimisticCounter}`;
|
|
1159
1275
|
}
|
|
1276
|
+
function getRecognitionCtor() {
|
|
1277
|
+
if (typeof window === "undefined") return null;
|
|
1278
|
+
const w = window;
|
|
1279
|
+
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
1280
|
+
}
|
|
1281
|
+
var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
1282
|
+
async function ensureMicrophoneAccess() {
|
|
1283
|
+
const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
|
|
1284
|
+
if (!media?.getUserMedia) return { ok: true };
|
|
1285
|
+
try {
|
|
1286
|
+
const status = await navigator.permissions?.query({
|
|
1287
|
+
name: "microphone"
|
|
1288
|
+
});
|
|
1289
|
+
if (status?.state === "granted") return { ok: true };
|
|
1290
|
+
if (status?.state === "denied")
|
|
1291
|
+
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
try {
|
|
1295
|
+
const stream = await media.getUserMedia({ audio: true });
|
|
1296
|
+
for (const track of stream.getTracks()) track.stop();
|
|
1297
|
+
return { ok: true };
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
1300
|
+
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
1301
|
+
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
1302
|
+
}
|
|
1303
|
+
if (name === "NotFoundError" || name === "DevicesNotFoundError") {
|
|
1304
|
+
return { ok: false, message: "No microphone was found." };
|
|
1305
|
+
}
|
|
1306
|
+
return {
|
|
1307
|
+
ok: false,
|
|
1308
|
+
message: "Dictation could not start. You can type instead."
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function describeError(code) {
|
|
1313
|
+
switch (code) {
|
|
1314
|
+
case "not-allowed":
|
|
1315
|
+
case "service-not-allowed":
|
|
1316
|
+
return MIC_DENIED_MESSAGE;
|
|
1317
|
+
case "no-speech":
|
|
1318
|
+
return "I didn't catch anything \u2014 try again a little closer to the mic.";
|
|
1319
|
+
case "audio-capture":
|
|
1320
|
+
return "No microphone was found.";
|
|
1321
|
+
case "network":
|
|
1322
|
+
return "Speech recognition needs a network connection.";
|
|
1323
|
+
case "aborted":
|
|
1324
|
+
return "";
|
|
1325
|
+
default:
|
|
1326
|
+
return "Dictation stopped unexpectedly. You can type instead.";
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
function useSpeechInput({
|
|
1330
|
+
onFinalTranscript,
|
|
1331
|
+
lang,
|
|
1332
|
+
onError
|
|
1333
|
+
}) {
|
|
1334
|
+
const [supported] = react.useState(() => getRecognitionCtor() !== null);
|
|
1335
|
+
const [listening, setListening] = react.useState(false);
|
|
1336
|
+
const [interim, setInterim] = react.useState("");
|
|
1337
|
+
const [error, setError] = react.useState(null);
|
|
1338
|
+
const recognitionRef = react.useRef(null);
|
|
1339
|
+
const startTokenRef = react.useRef(0);
|
|
1340
|
+
const beginRecognitionRef = react.useRef(null);
|
|
1341
|
+
const beginRecognition = react.useCallback(
|
|
1342
|
+
(Ctor, token) => {
|
|
1343
|
+
beginRecognitionRef.current?.(Ctor, token);
|
|
1344
|
+
},
|
|
1345
|
+
[]
|
|
1346
|
+
);
|
|
1347
|
+
const finalRef = react.useRef(onFinalTranscript);
|
|
1348
|
+
const errorRef = react.useRef(onError);
|
|
1349
|
+
finalRef.current = onFinalTranscript;
|
|
1350
|
+
errorRef.current = onError;
|
|
1351
|
+
const stop = react.useCallback(() => {
|
|
1352
|
+
startTokenRef.current += 1;
|
|
1353
|
+
setListening(false);
|
|
1354
|
+
setInterim("");
|
|
1355
|
+
const recognition = recognitionRef.current;
|
|
1356
|
+
if (!recognition) return;
|
|
1357
|
+
try {
|
|
1358
|
+
recognition.stop();
|
|
1359
|
+
} catch {
|
|
1360
|
+
}
|
|
1361
|
+
setListening(false);
|
|
1362
|
+
setInterim("");
|
|
1363
|
+
}, []);
|
|
1364
|
+
const start = react.useCallback(() => {
|
|
1365
|
+
const Ctor = getRecognitionCtor();
|
|
1366
|
+
if (!Ctor) return;
|
|
1367
|
+
if (recognitionRef.current) stop();
|
|
1368
|
+
setError(null);
|
|
1369
|
+
setListening(true);
|
|
1370
|
+
startTokenRef.current += 1;
|
|
1371
|
+
const token = startTokenRef.current;
|
|
1372
|
+
void ensureMicrophoneAccess().then((access) => {
|
|
1373
|
+
if (token !== startTokenRef.current) return;
|
|
1374
|
+
if (!access.ok) {
|
|
1375
|
+
setListening(false);
|
|
1376
|
+
setError(access.message);
|
|
1377
|
+
errorRef.current?.(access.message);
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
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 = () => {
|
|
1391
|
+
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 = 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
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
[lang]
|
|
1429
|
+
);
|
|
1430
|
+
beginRecognitionRef.current = beginRecognitionImpl;
|
|
1431
|
+
const toggle = react.useCallback(() => {
|
|
1432
|
+
if (listening) stop();
|
|
1433
|
+
else start();
|
|
1434
|
+
}, [listening, start, stop]);
|
|
1435
|
+
react.useEffect(
|
|
1436
|
+
() => () => {
|
|
1437
|
+
const recognition = recognitionRef.current;
|
|
1438
|
+
if (!recognition) return;
|
|
1439
|
+
recognition.onresult = null;
|
|
1440
|
+
recognition.onerror = null;
|
|
1441
|
+
recognition.onend = null;
|
|
1442
|
+
recognition.onstart = null;
|
|
1443
|
+
try {
|
|
1444
|
+
recognition.abort();
|
|
1445
|
+
} catch {
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
[]
|
|
1449
|
+
);
|
|
1450
|
+
return { supported, listening, interim, error, start, stop, toggle };
|
|
1451
|
+
}
|
|
1160
1452
|
|
|
1161
1453
|
exports.DEFAULT_EXPLORE_EXAMPLE_PROMPTS = DEFAULT_EXPLORE_EXAMPLE_PROMPTS;
|
|
1162
1454
|
exports.FALLBACK_EXPLORE_LIMITS = FALLBACK_EXPLORE_LIMITS;
|
|
1163
1455
|
exports.classifyExploreError = classifyExploreError;
|
|
1164
1456
|
exports.useExploreChat = useExploreChat;
|
|
1457
|
+
exports.useSpeechInput = useSpeechInput;
|
|
1165
1458
|
//# sourceMappingURL=index.js.map
|
|
1166
1459
|
//# sourceMappingURL=index.js.map
|