@burdenoff/website-sdk 2026.828.7 → 2026.829.1
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.js +401 -189
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +403 -191
- package/dist/components/explore.mjs.map +1 -1
- package/dist/hooks/index.js +110 -68
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +110 -68
- 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 +401 -189
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +401 -189
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/hooks/index.mjs
CHANGED
|
@@ -1276,21 +1276,37 @@ function getRecognitionCtor() {
|
|
|
1276
1276
|
const w = window;
|
|
1277
1277
|
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
1278
1278
|
}
|
|
1279
|
+
function isTouchDevice() {
|
|
1280
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
1281
|
+
try {
|
|
1282
|
+
return window.matchMedia("(pointer: coarse)").matches;
|
|
1283
|
+
} catch {
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1279
1287
|
var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
1288
|
+
var MIC_UNAVAILABLE_MESSAGE = "Dictation isn't available in this browser. You can type instead.";
|
|
1289
|
+
var RELEASE_SETTLE_MS = 250;
|
|
1290
|
+
var RETRY_DELAY_MS = 350;
|
|
1291
|
+
var sleep = (ms) => new Promise((resolve) => {
|
|
1292
|
+
setTimeout(resolve, ms);
|
|
1293
|
+
});
|
|
1280
1294
|
async function ensureMicrophoneAccess() {
|
|
1281
1295
|
const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
|
|
1282
|
-
if (!media?.getUserMedia)
|
|
1296
|
+
if (!media?.getUserMedia)
|
|
1297
|
+
return { ok: true, confirmed: false, primed: false };
|
|
1283
1298
|
try {
|
|
1284
1299
|
const status = await navigator.permissions?.query({
|
|
1285
1300
|
name: "microphone"
|
|
1286
1301
|
});
|
|
1287
|
-
if (status?.state === "granted")
|
|
1302
|
+
if (status?.state === "granted")
|
|
1303
|
+
return { ok: true, confirmed: true, primed: false };
|
|
1288
1304
|
} catch {
|
|
1289
1305
|
}
|
|
1290
1306
|
try {
|
|
1291
1307
|
const stream = await media.getUserMedia({ audio: true });
|
|
1292
1308
|
for (const track of stream.getTracks()) track.stop();
|
|
1293
|
-
return { ok: true, confirmed: true };
|
|
1309
|
+
return { ok: true, confirmed: true, primed: true };
|
|
1294
1310
|
} catch (error) {
|
|
1295
1311
|
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
1296
1312
|
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
@@ -1334,19 +1350,17 @@ function useSpeechInput({
|
|
|
1334
1350
|
const recognitionRef = useRef(null);
|
|
1335
1351
|
const startTokenRef = useRef(0);
|
|
1336
1352
|
const micConfirmedRef = useRef(false);
|
|
1337
|
-
const
|
|
1338
|
-
const
|
|
1339
|
-
(Ctor, token) => {
|
|
1340
|
-
beginRecognitionRef.current?.(Ctor, token);
|
|
1341
|
-
},
|
|
1342
|
-
[]
|
|
1343
|
-
);
|
|
1353
|
+
const wantsListeningRef = useRef(false);
|
|
1354
|
+
const retriedRef = useRef(false);
|
|
1344
1355
|
const finalRef = useRef(onFinalTranscript);
|
|
1345
1356
|
const errorRef = useRef(onError);
|
|
1346
1357
|
finalRef.current = onFinalTranscript;
|
|
1347
1358
|
errorRef.current = onError;
|
|
1359
|
+
const langRef = useRef(lang);
|
|
1360
|
+
langRef.current = lang;
|
|
1348
1361
|
const stop = useCallback(() => {
|
|
1349
1362
|
startTokenRef.current += 1;
|
|
1363
|
+
wantsListeningRef.current = false;
|
|
1350
1364
|
setListening(false);
|
|
1351
1365
|
setInterim("");
|
|
1352
1366
|
const recognition = recognitionRef.current;
|
|
@@ -1355,83 +1369,111 @@ function useSpeechInput({
|
|
|
1355
1369
|
recognition.stop();
|
|
1356
1370
|
} catch {
|
|
1357
1371
|
}
|
|
1358
|
-
setListening(false);
|
|
1359
|
-
setInterim("");
|
|
1360
1372
|
}, []);
|
|
1361
|
-
const
|
|
1373
|
+
const openSession = useCallback((token) => {
|
|
1362
1374
|
const Ctor = getRecognitionCtor();
|
|
1363
|
-
if (!Ctor) return;
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1375
|
+
if (!Ctor || token !== startTokenRef.current) return;
|
|
1376
|
+
const recognition = new Ctor();
|
|
1377
|
+
recognition.lang = langRef.current ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
1378
|
+
recognition.continuous = !isTouchDevice();
|
|
1379
|
+
recognition.interimResults = true;
|
|
1380
|
+
recognition.maxAlternatives = 1;
|
|
1381
|
+
recognition.onstart = () => {
|
|
1382
|
+
if (token !== startTokenRef.current) return;
|
|
1383
|
+
setError(null);
|
|
1384
|
+
setListening(true);
|
|
1385
|
+
};
|
|
1386
|
+
recognition.onresult = (event) => {
|
|
1387
|
+
if (token !== startTokenRef.current) return;
|
|
1388
|
+
let settled = "";
|
|
1389
|
+
let pending = "";
|
|
1390
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
1391
|
+
const result = event.results[i];
|
|
1392
|
+
if (!result) continue;
|
|
1393
|
+
const text = result[0]?.transcript ?? "";
|
|
1394
|
+
if (result.isFinal) settled += text;
|
|
1395
|
+
else pending += text;
|
|
1396
|
+
}
|
|
1397
|
+
setInterim(pending);
|
|
1398
|
+
if (settled.trim() !== "") finalRef.current(settled);
|
|
1399
|
+
};
|
|
1400
|
+
recognition.onerror = (event) => {
|
|
1401
|
+
if (token !== startTokenRef.current) return;
|
|
1402
|
+
if ((event.error === "not-allowed" || event.error === "service-not-allowed") && micConfirmedRef.current && !retriedRef.current) {
|
|
1403
|
+
retriedRef.current = true;
|
|
1404
|
+
void sleep(RETRY_DELAY_MS).then(() => {
|
|
1405
|
+
if (token !== startTokenRef.current || !wantsListeningRef.current)
|
|
1406
|
+
return;
|
|
1407
|
+
openSessionRef.current?.(token);
|
|
1408
|
+
});
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
const message = micConfirmedRef.current && (event.error === "not-allowed" || event.error === "service-not-allowed") ? MIC_UNAVAILABLE_MESSAGE : describeError(event.error);
|
|
1412
|
+
wantsListeningRef.current = false;
|
|
1413
|
+
setListening(false);
|
|
1414
|
+
setInterim("");
|
|
1415
|
+
if (message !== "") {
|
|
1416
|
+
setError(message);
|
|
1417
|
+
errorRef.current?.(message);
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
recognition.onend = () => {
|
|
1421
|
+
if (token !== startTokenRef.current) return;
|
|
1422
|
+
setInterim("");
|
|
1423
|
+
if (wantsListeningRef.current && isTouchDevice()) {
|
|
1424
|
+
void sleep(120).then(() => {
|
|
1425
|
+
if (token !== startTokenRef.current || !wantsListeningRef.current)
|
|
1426
|
+
return;
|
|
1427
|
+
openSessionRef.current?.(token);
|
|
1428
|
+
});
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
setListening(false);
|
|
1432
|
+
};
|
|
1433
|
+
recognitionRef.current = recognition;
|
|
1434
|
+
try {
|
|
1435
|
+
recognition.start();
|
|
1436
|
+
} catch {
|
|
1437
|
+
wantsListeningRef.current = false;
|
|
1438
|
+
setListening(false);
|
|
1439
|
+
}
|
|
1440
|
+
}, []);
|
|
1441
|
+
const openSessionRef = useRef(null);
|
|
1442
|
+
openSessionRef.current = openSession;
|
|
1443
|
+
const start = useCallback(() => {
|
|
1444
|
+
if (!getRecognitionCtor()) return;
|
|
1367
1445
|
startTokenRef.current += 1;
|
|
1368
1446
|
const token = startTokenRef.current;
|
|
1369
|
-
|
|
1447
|
+
wantsListeningRef.current = true;
|
|
1448
|
+
retriedRef.current = false;
|
|
1449
|
+
micConfirmedRef.current = false;
|
|
1450
|
+
setError(null);
|
|
1451
|
+
setListening(true);
|
|
1452
|
+
void ensureMicrophoneAccess().then(async (access) => {
|
|
1370
1453
|
if (token !== startTokenRef.current) return;
|
|
1371
|
-
micConfirmedRef.current = access.ok && access.confirmed;
|
|
1372
1454
|
if (!access.ok) {
|
|
1455
|
+
wantsListeningRef.current = false;
|
|
1373
1456
|
setListening(false);
|
|
1374
1457
|
setError(access.message);
|
|
1375
1458
|
errorRef.current?.(access.message);
|
|
1376
1459
|
return;
|
|
1377
1460
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
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 = () => {
|
|
1461
|
+
micConfirmedRef.current = access.confirmed;
|
|
1462
|
+
if (access.primed) {
|
|
1463
|
+
await sleep(RELEASE_SETTLE_MS);
|
|
1389
1464
|
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);
|
|
1424
1465
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
);
|
|
1428
|
-
beginRecognitionRef.current = beginRecognitionImpl;
|
|
1466
|
+
openSessionRef.current?.(token);
|
|
1467
|
+
});
|
|
1468
|
+
}, []);
|
|
1429
1469
|
const toggle = useCallback(() => {
|
|
1430
1470
|
if (listening) stop();
|
|
1431
1471
|
else start();
|
|
1432
1472
|
}, [listening, start, stop]);
|
|
1433
1473
|
useEffect(
|
|
1434
1474
|
() => () => {
|
|
1475
|
+
startTokenRef.current += 1;
|
|
1476
|
+
wantsListeningRef.current = false;
|
|
1435
1477
|
const recognition = recognitionRef.current;
|
|
1436
1478
|
if (!recognition) return;
|
|
1437
1479
|
recognition.onresult = null;
|