@burdenoff/website-sdk 2026.828.5 → 2026.828.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/dist/components/explore.d.mts +11 -1
- package/dist/components/explore.d.ts +11 -1
- package/dist/components/explore.js +175 -57
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +175 -58
- package/dist/components/explore.mjs.map +1 -1
- package/dist/hooks/index.js +104 -41
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +104 -41
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.js +174 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +174 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/hooks/index.mjs
CHANGED
|
@@ -1276,11 +1276,40 @@ function getRecognitionCtor() {
|
|
|
1276
1276
|
const w = window;
|
|
1277
1277
|
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
1278
1278
|
}
|
|
1279
|
+
var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
1280
|
+
async function ensureMicrophoneAccess() {
|
|
1281
|
+
const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
|
|
1282
|
+
if (!media?.getUserMedia) return { ok: true, confirmed: false };
|
|
1283
|
+
try {
|
|
1284
|
+
const status = await navigator.permissions?.query({
|
|
1285
|
+
name: "microphone"
|
|
1286
|
+
});
|
|
1287
|
+
if (status?.state === "granted") return { ok: true, confirmed: true };
|
|
1288
|
+
} catch {
|
|
1289
|
+
}
|
|
1290
|
+
try {
|
|
1291
|
+
const stream = await media.getUserMedia({ audio: true });
|
|
1292
|
+
for (const track of stream.getTracks()) track.stop();
|
|
1293
|
+
return { ok: true, confirmed: true };
|
|
1294
|
+
} catch (error) {
|
|
1295
|
+
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
1296
|
+
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
1297
|
+
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
1298
|
+
}
|
|
1299
|
+
if (name === "NotFoundError" || name === "DevicesNotFoundError") {
|
|
1300
|
+
return { ok: false, message: "No microphone was found." };
|
|
1301
|
+
}
|
|
1302
|
+
return {
|
|
1303
|
+
ok: false,
|
|
1304
|
+
message: "Dictation could not start. You can type instead."
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1279
1308
|
function describeError(code) {
|
|
1280
1309
|
switch (code) {
|
|
1281
1310
|
case "not-allowed":
|
|
1282
1311
|
case "service-not-allowed":
|
|
1283
|
-
return
|
|
1312
|
+
return MIC_DENIED_MESSAGE;
|
|
1284
1313
|
case "no-speech":
|
|
1285
1314
|
return "I didn't catch anything \u2014 try again a little closer to the mic.";
|
|
1286
1315
|
case "audio-capture":
|
|
@@ -1303,11 +1332,23 @@ function useSpeechInput({
|
|
|
1303
1332
|
const [interim, setInterim] = useState("");
|
|
1304
1333
|
const [error, setError] = useState(null);
|
|
1305
1334
|
const recognitionRef = useRef(null);
|
|
1335
|
+
const startTokenRef = useRef(0);
|
|
1336
|
+
const micConfirmedRef = useRef(false);
|
|
1337
|
+
const beginRecognitionRef = useRef(null);
|
|
1338
|
+
const beginRecognition = useCallback(
|
|
1339
|
+
(Ctor, token) => {
|
|
1340
|
+
beginRecognitionRef.current?.(Ctor, token);
|
|
1341
|
+
},
|
|
1342
|
+
[]
|
|
1343
|
+
);
|
|
1306
1344
|
const finalRef = useRef(onFinalTranscript);
|
|
1307
1345
|
const errorRef = useRef(onError);
|
|
1308
1346
|
finalRef.current = onFinalTranscript;
|
|
1309
1347
|
errorRef.current = onError;
|
|
1310
1348
|
const stop = useCallback(() => {
|
|
1349
|
+
startTokenRef.current += 1;
|
|
1350
|
+
setListening(false);
|
|
1351
|
+
setInterim("");
|
|
1311
1352
|
const recognition = recognitionRef.current;
|
|
1312
1353
|
if (!recognition) return;
|
|
1313
1354
|
try {
|
|
@@ -1321,48 +1362,70 @@ function useSpeechInput({
|
|
|
1321
1362
|
const Ctor = getRecognitionCtor();
|
|
1322
1363
|
if (!Ctor) return;
|
|
1323
1364
|
if (recognitionRef.current) stop();
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
1337
|
-
const result = event.results[i];
|
|
1338
|
-
if (!result) continue;
|
|
1339
|
-
const text = result[0]?.transcript ?? "";
|
|
1340
|
-
if (result.isFinal) settled += text;
|
|
1341
|
-
else pending += text;
|
|
1365
|
+
setError(null);
|
|
1366
|
+
setListening(true);
|
|
1367
|
+
startTokenRef.current += 1;
|
|
1368
|
+
const token = startTokenRef.current;
|
|
1369
|
+
void ensureMicrophoneAccess().then((access) => {
|
|
1370
|
+
if (token !== startTokenRef.current) return;
|
|
1371
|
+
micConfirmedRef.current = access.ok && access.confirmed;
|
|
1372
|
+
if (!access.ok) {
|
|
1373
|
+
setListening(false);
|
|
1374
|
+
setError(access.message);
|
|
1375
|
+
errorRef.current?.(access.message);
|
|
1376
|
+
return;
|
|
1342
1377
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1378
|
+
beginRecognition(Ctor, token);
|
|
1379
|
+
});
|
|
1380
|
+
}, [beginRecognition, stop]);
|
|
1381
|
+
const beginRecognitionImpl = useCallback(
|
|
1382
|
+
(Ctor, token) => {
|
|
1383
|
+
const recognition = new Ctor();
|
|
1384
|
+
recognition.lang = lang ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
1385
|
+
recognition.continuous = true;
|
|
1386
|
+
recognition.interimResults = true;
|
|
1387
|
+
recognition.maxAlternatives = 1;
|
|
1388
|
+
recognition.onstart = () => {
|
|
1389
|
+
if (token !== startTokenRef.current) return;
|
|
1390
|
+
setError(null);
|
|
1391
|
+
setListening(true);
|
|
1392
|
+
};
|
|
1393
|
+
recognition.onresult = (event) => {
|
|
1394
|
+
let settled = "";
|
|
1395
|
+
let pending = "";
|
|
1396
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
1397
|
+
const result = event.results[i];
|
|
1398
|
+
if (!result) continue;
|
|
1399
|
+
const text = result[0]?.transcript ?? "";
|
|
1400
|
+
if (result.isFinal) settled += text;
|
|
1401
|
+
else pending += text;
|
|
1402
|
+
}
|
|
1403
|
+
setInterim(pending);
|
|
1404
|
+
if (settled.trim() !== "") finalRef.current(settled);
|
|
1405
|
+
};
|
|
1406
|
+
recognition.onerror = (event) => {
|
|
1407
|
+
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);
|
|
1408
|
+
setListening(false);
|
|
1409
|
+
setInterim("");
|
|
1410
|
+
if (message !== "") {
|
|
1411
|
+
setError(message);
|
|
1412
|
+
errorRef.current?.(message);
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
recognition.onend = () => {
|
|
1416
|
+
setListening(false);
|
|
1417
|
+
setInterim("");
|
|
1418
|
+
};
|
|
1419
|
+
recognitionRef.current = recognition;
|
|
1420
|
+
try {
|
|
1421
|
+
recognition.start();
|
|
1422
|
+
} catch {
|
|
1423
|
+
setListening(false);
|
|
1353
1424
|
}
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
};
|
|
1359
|
-
recognitionRef.current = recognition;
|
|
1360
|
-
try {
|
|
1361
|
-
recognition.start();
|
|
1362
|
-
} catch {
|
|
1363
|
-
setListening(false);
|
|
1364
|
-
}
|
|
1365
|
-
}, [lang, stop]);
|
|
1425
|
+
},
|
|
1426
|
+
[lang]
|
|
1427
|
+
);
|
|
1428
|
+
beginRecognitionRef.current = beginRecognitionImpl;
|
|
1366
1429
|
const toggle = useCallback(() => {
|
|
1367
1430
|
if (listening) stop();
|
|
1368
1431
|
else start();
|