@burdenoff/website-sdk 2026.828.6 → 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 +402 -190
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +404 -192
- package/dist/components/explore.mjs.map +1 -1
- package/dist/hooks/index.js +111 -69
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +111 -69
- 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 +402 -190
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +402 -190
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/hooks/index.mjs
CHANGED
|
@@ -1276,23 +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")
|
|
1288
|
-
|
|
1289
|
-
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
1302
|
+
if (status?.state === "granted")
|
|
1303
|
+
return { ok: true, confirmed: true, primed: false };
|
|
1290
1304
|
} catch {
|
|
1291
1305
|
}
|
|
1292
1306
|
try {
|
|
1293
1307
|
const stream = await media.getUserMedia({ audio: true });
|
|
1294
1308
|
for (const track of stream.getTracks()) track.stop();
|
|
1295
|
-
return { ok: true };
|
|
1309
|
+
return { ok: true, confirmed: true, primed: true };
|
|
1296
1310
|
} catch (error) {
|
|
1297
1311
|
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
1298
1312
|
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
@@ -1335,19 +1349,18 @@ function useSpeechInput({
|
|
|
1335
1349
|
const [error, setError] = useState(null);
|
|
1336
1350
|
const recognitionRef = useRef(null);
|
|
1337
1351
|
const startTokenRef = useRef(0);
|
|
1338
|
-
const
|
|
1339
|
-
const
|
|
1340
|
-
|
|
1341
|
-
beginRecognitionRef.current?.(Ctor, token);
|
|
1342
|
-
},
|
|
1343
|
-
[]
|
|
1344
|
-
);
|
|
1352
|
+
const micConfirmedRef = useRef(false);
|
|
1353
|
+
const wantsListeningRef = useRef(false);
|
|
1354
|
+
const retriedRef = useRef(false);
|
|
1345
1355
|
const finalRef = useRef(onFinalTranscript);
|
|
1346
1356
|
const errorRef = useRef(onError);
|
|
1347
1357
|
finalRef.current = onFinalTranscript;
|
|
1348
1358
|
errorRef.current = onError;
|
|
1359
|
+
const langRef = useRef(lang);
|
|
1360
|
+
langRef.current = lang;
|
|
1349
1361
|
const stop = useCallback(() => {
|
|
1350
1362
|
startTokenRef.current += 1;
|
|
1363
|
+
wantsListeningRef.current = false;
|
|
1351
1364
|
setListening(false);
|
|
1352
1365
|
setInterim("");
|
|
1353
1366
|
const recognition = recognitionRef.current;
|
|
@@ -1356,82 +1369,111 @@ function useSpeechInput({
|
|
|
1356
1369
|
recognition.stop();
|
|
1357
1370
|
} catch {
|
|
1358
1371
|
}
|
|
1359
|
-
setListening(false);
|
|
1360
|
-
setInterim("");
|
|
1361
1372
|
}, []);
|
|
1362
|
-
const
|
|
1373
|
+
const openSession = useCallback((token) => {
|
|
1363
1374
|
const Ctor = getRecognitionCtor();
|
|
1364
|
-
if (!Ctor) return;
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
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;
|
|
1368
1445
|
startTokenRef.current += 1;
|
|
1369
1446
|
const token = startTokenRef.current;
|
|
1370
|
-
|
|
1447
|
+
wantsListeningRef.current = true;
|
|
1448
|
+
retriedRef.current = false;
|
|
1449
|
+
micConfirmedRef.current = false;
|
|
1450
|
+
setError(null);
|
|
1451
|
+
setListening(true);
|
|
1452
|
+
void ensureMicrophoneAccess().then(async (access) => {
|
|
1371
1453
|
if (token !== startTokenRef.current) return;
|
|
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 = 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;
|