@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.js
CHANGED
|
@@ -1278,11 +1278,40 @@ function getRecognitionCtor() {
|
|
|
1278
1278
|
const w = window;
|
|
1279
1279
|
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
1280
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, confirmed: false };
|
|
1285
|
+
try {
|
|
1286
|
+
const status = await navigator.permissions?.query({
|
|
1287
|
+
name: "microphone"
|
|
1288
|
+
});
|
|
1289
|
+
if (status?.state === "granted") return { ok: true, confirmed: true };
|
|
1290
|
+
} catch {
|
|
1291
|
+
}
|
|
1292
|
+
try {
|
|
1293
|
+
const stream = await media.getUserMedia({ audio: true });
|
|
1294
|
+
for (const track of stream.getTracks()) track.stop();
|
|
1295
|
+
return { ok: true, confirmed: true };
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
1298
|
+
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
1299
|
+
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
1300
|
+
}
|
|
1301
|
+
if (name === "NotFoundError" || name === "DevicesNotFoundError") {
|
|
1302
|
+
return { ok: false, message: "No microphone was found." };
|
|
1303
|
+
}
|
|
1304
|
+
return {
|
|
1305
|
+
ok: false,
|
|
1306
|
+
message: "Dictation could not start. You can type instead."
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1281
1310
|
function describeError(code) {
|
|
1282
1311
|
switch (code) {
|
|
1283
1312
|
case "not-allowed":
|
|
1284
1313
|
case "service-not-allowed":
|
|
1285
|
-
return
|
|
1314
|
+
return MIC_DENIED_MESSAGE;
|
|
1286
1315
|
case "no-speech":
|
|
1287
1316
|
return "I didn't catch anything \u2014 try again a little closer to the mic.";
|
|
1288
1317
|
case "audio-capture":
|
|
@@ -1305,11 +1334,23 @@ function useSpeechInput({
|
|
|
1305
1334
|
const [interim, setInterim] = react.useState("");
|
|
1306
1335
|
const [error, setError] = react.useState(null);
|
|
1307
1336
|
const recognitionRef = react.useRef(null);
|
|
1337
|
+
const startTokenRef = react.useRef(0);
|
|
1338
|
+
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
|
+
);
|
|
1308
1346
|
const finalRef = react.useRef(onFinalTranscript);
|
|
1309
1347
|
const errorRef = react.useRef(onError);
|
|
1310
1348
|
finalRef.current = onFinalTranscript;
|
|
1311
1349
|
errorRef.current = onError;
|
|
1312
1350
|
const stop = react.useCallback(() => {
|
|
1351
|
+
startTokenRef.current += 1;
|
|
1352
|
+
setListening(false);
|
|
1353
|
+
setInterim("");
|
|
1313
1354
|
const recognition = recognitionRef.current;
|
|
1314
1355
|
if (!recognition) return;
|
|
1315
1356
|
try {
|
|
@@ -1323,48 +1364,70 @@ function useSpeechInput({
|
|
|
1323
1364
|
const Ctor = getRecognitionCtor();
|
|
1324
1365
|
if (!Ctor) return;
|
|
1325
1366
|
if (recognitionRef.current) stop();
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
1339
|
-
const result = event.results[i];
|
|
1340
|
-
if (!result) continue;
|
|
1341
|
-
const text = result[0]?.transcript ?? "";
|
|
1342
|
-
if (result.isFinal) settled += text;
|
|
1343
|
-
else pending += text;
|
|
1367
|
+
setError(null);
|
|
1368
|
+
setListening(true);
|
|
1369
|
+
startTokenRef.current += 1;
|
|
1370
|
+
const token = startTokenRef.current;
|
|
1371
|
+
void ensureMicrophoneAccess().then((access) => {
|
|
1372
|
+
if (token !== startTokenRef.current) return;
|
|
1373
|
+
micConfirmedRef.current = access.ok && access.confirmed;
|
|
1374
|
+
if (!access.ok) {
|
|
1375
|
+
setListening(false);
|
|
1376
|
+
setError(access.message);
|
|
1377
|
+
errorRef.current?.(access.message);
|
|
1378
|
+
return;
|
|
1344
1379
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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 = 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);
|
|
1355
1426
|
}
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
};
|
|
1361
|
-
recognitionRef.current = recognition;
|
|
1362
|
-
try {
|
|
1363
|
-
recognition.start();
|
|
1364
|
-
} catch {
|
|
1365
|
-
setListening(false);
|
|
1366
|
-
}
|
|
1367
|
-
}, [lang, stop]);
|
|
1427
|
+
},
|
|
1428
|
+
[lang]
|
|
1429
|
+
);
|
|
1430
|
+
beginRecognitionRef.current = beginRecognitionImpl;
|
|
1368
1431
|
const toggle = react.useCallback(() => {
|
|
1369
1432
|
if (listening) stop();
|
|
1370
1433
|
else start();
|