@burdenoff/website-sdk 2026.922.4 → 2026.923.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.d.mts +4 -158
- package/dist/components/explore.d.ts +4 -158
- package/dist/components/explore.js +1464 -263
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +1466 -266
- package/dist/components/explore.mjs.map +1 -1
- package/dist/explore-D25zoXy9.d.mts +240 -0
- package/dist/explore-I4QnY4J9.d.ts +240 -0
- package/dist/{explore-types-DvQPiL1t.d.mts → explore-types-B5sdOC5M.d.mts} +75 -2
- package/dist/{explore-types-DvQPiL1t.d.ts → explore-types-B5sdOC5M.d.ts} +75 -2
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +481 -8
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +479 -9
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +433 -25
- package/dist/index.d.ts +433 -25
- package/dist/index.js +2839 -409
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2817 -412
- package/dist/index.mjs.map +1 -1
- package/dist/{use-explore-chat-Dgm-jf96.d.mts → use-progressive-reveal-Cmy8bA7L.d.mts} +88 -7
- package/dist/{use-explore-chat-CNEdKBc1.d.ts → use-progressive-reveal-DHPGGxZu.d.ts} +88 -7
- package/package.json +3 -2
package/dist/hooks/index.mjs
CHANGED
|
@@ -224,6 +224,10 @@ function useWebSDK() {
|
|
|
224
224
|
}
|
|
225
225
|
return client;
|
|
226
226
|
}
|
|
227
|
+
function useWebSDKConfig() {
|
|
228
|
+
const client = useWebSDK();
|
|
229
|
+
return client.getConfig();
|
|
230
|
+
}
|
|
227
231
|
|
|
228
232
|
// src/components/explore-types.ts
|
|
229
233
|
var EXPLORE_MESSAGE_FIELDS = (
|
|
@@ -383,6 +387,22 @@ var SUBMIT_EXPLORE_FEEDBACK_MUTATION = (
|
|
|
383
387
|
}
|
|
384
388
|
`
|
|
385
389
|
);
|
|
390
|
+
var ESCALATE_EXPLORE_CONVERSATION_MUTATION = (
|
|
391
|
+
/* GraphQL */
|
|
392
|
+
`
|
|
393
|
+
mutation EscalateExploreConversation(
|
|
394
|
+
$input: EscalateExploreConversationInput!
|
|
395
|
+
) {
|
|
396
|
+
escalateExploreConversation(input: $input) {
|
|
397
|
+
created
|
|
398
|
+
escalation {
|
|
399
|
+
email
|
|
400
|
+
createdAt
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
`
|
|
405
|
+
);
|
|
386
406
|
var CREATE_EXPLORE_SHARE_LINK_MUTATION = (
|
|
387
407
|
/* GraphQL */
|
|
388
408
|
`
|
|
@@ -749,6 +769,25 @@ var VALIDATION_CODES = /* @__PURE__ */ new Set([
|
|
|
749
769
|
"CONVERSATION_LIMIT",
|
|
750
770
|
"INVALID_INPUT"
|
|
751
771
|
]);
|
|
772
|
+
function escalateOutcomeForCode(code) {
|
|
773
|
+
switch (code) {
|
|
774
|
+
case "INVALID_EMAIL":
|
|
775
|
+
return "invalid-email";
|
|
776
|
+
case "RATE_LIMITED":
|
|
777
|
+
case "HTTP_429":
|
|
778
|
+
return "rate-limited";
|
|
779
|
+
case "CAPTCHA_REQUIRED":
|
|
780
|
+
case "CAPTCHA_FAILED":
|
|
781
|
+
return "captcha";
|
|
782
|
+
// The token owns nothing, or owns a different conversation. The server answers NOT_FOUND
|
|
783
|
+
// rather than FORBIDDEN so the endpoint is not an oracle for whether a thread exists;
|
|
784
|
+
// either way there is nothing this visitor can do about it, so it reads as a failure.
|
|
785
|
+
case "NOT_FOUND":
|
|
786
|
+
return "failed";
|
|
787
|
+
default:
|
|
788
|
+
return "failed";
|
|
789
|
+
}
|
|
790
|
+
}
|
|
752
791
|
function classifyExploreError(code, serverMessage) {
|
|
753
792
|
const c = code ?? "UNKNOWN";
|
|
754
793
|
if (RATE_LIMIT_CODES.has(c)) {
|
|
@@ -915,6 +954,7 @@ function useExploreChat(options = {}) {
|
|
|
915
954
|
onSend
|
|
916
955
|
} = options;
|
|
917
956
|
const client = useWebSDK();
|
|
957
|
+
const { productId } = useWebSDKConfig();
|
|
918
958
|
const promptKey = JSON.stringify(examplePrompts ?? []);
|
|
919
959
|
const welcomeKey = JSON.stringify(fallbackWelcome ?? null);
|
|
920
960
|
const fallbackCatalog = useMemo(
|
|
@@ -938,6 +978,7 @@ function useExploreChat(options = {}) {
|
|
|
938
978
|
const pollGenerationRef = useRef(0);
|
|
939
979
|
const pollTimerRef = useRef(null);
|
|
940
980
|
const conversationTokenRef = useRef(null);
|
|
981
|
+
const [hasConversation, setHasConversation] = useState(false);
|
|
941
982
|
const shareTokenRef = useRef(void 0);
|
|
942
983
|
if (shareTokenRef.current === void 0) {
|
|
943
984
|
shareTokenRef.current = readShareToken();
|
|
@@ -952,6 +993,10 @@ function useExploreChat(options = {}) {
|
|
|
952
993
|
);
|
|
953
994
|
const inFlightRef = useRef(false);
|
|
954
995
|
catalogRef.current = catalog;
|
|
996
|
+
const rememberConversationToken = useCallback((token) => {
|
|
997
|
+
conversationTokenRef.current = token;
|
|
998
|
+
setHasConversation(token !== null);
|
|
999
|
+
}, []);
|
|
955
1000
|
const clearPollTimer = useCallback(() => {
|
|
956
1001
|
if (pollTimerRef.current !== null) {
|
|
957
1002
|
clearTimeout(pollTimerRef.current);
|
|
@@ -1018,7 +1063,7 @@ function useExploreChat(options = {}) {
|
|
|
1018
1063
|
setArchive(readArchive(storageKey));
|
|
1019
1064
|
const stored = readPersisted(storageKey);
|
|
1020
1065
|
if (stored) {
|
|
1021
|
-
|
|
1066
|
+
rememberConversationToken(stored.conversationToken);
|
|
1022
1067
|
focusProductRef.current = stored.focusProduct;
|
|
1023
1068
|
const settled = stored.messages.flatMap((m) => {
|
|
1024
1069
|
if (m.role !== "ASSISTANT") return [m];
|
|
@@ -1034,7 +1079,7 @@ function useExploreChat(options = {}) {
|
|
|
1034
1079
|
setTurnCount(turns);
|
|
1035
1080
|
}
|
|
1036
1081
|
setHydrated(true);
|
|
1037
|
-
}, [persist, storageKey]);
|
|
1082
|
+
}, [persist, rememberConversationToken, storageKey]);
|
|
1038
1083
|
useEffect(() => {
|
|
1039
1084
|
if (!persist || !hydrated || typeof window === "undefined") return;
|
|
1040
1085
|
try {
|
|
@@ -1227,7 +1272,7 @@ function useExploreChat(options = {}) {
|
|
|
1227
1272
|
setPhase(classified.kind === "rate-limit" ? "limited" : "error");
|
|
1228
1273
|
return;
|
|
1229
1274
|
}
|
|
1230
|
-
|
|
1275
|
+
rememberConversationToken(result.conversation.token);
|
|
1231
1276
|
turnCountRef.current = result.conversation.turnCount;
|
|
1232
1277
|
setTurnCount(result.conversation.turnCount);
|
|
1233
1278
|
setRemainingToday(result.remainingToday ?? null);
|
|
@@ -1253,7 +1298,14 @@ function useExploreChat(options = {}) {
|
|
|
1253
1298
|
setPhase("streaming");
|
|
1254
1299
|
startPolling(result.conversation.token, assistantMessage.id);
|
|
1255
1300
|
},
|
|
1256
|
-
[
|
|
1301
|
+
[
|
|
1302
|
+
client,
|
|
1303
|
+
locale,
|
|
1304
|
+
pageUrl,
|
|
1305
|
+
productSlug,
|
|
1306
|
+
rememberConversationToken,
|
|
1307
|
+
startPolling
|
|
1308
|
+
]
|
|
1257
1309
|
);
|
|
1258
1310
|
const send = useCallback(
|
|
1259
1311
|
async (message, attachments = []) => {
|
|
@@ -1370,7 +1422,7 @@ function useExploreChat(options = {}) {
|
|
|
1370
1422
|
);
|
|
1371
1423
|
cancelPolling();
|
|
1372
1424
|
inFlightRef.current = false;
|
|
1373
|
-
|
|
1425
|
+
rememberConversationToken(null);
|
|
1374
1426
|
lastSentTextRef.current = null;
|
|
1375
1427
|
lastSentAttachmentsRef.current = [];
|
|
1376
1428
|
turnCountRef.current = 0;
|
|
@@ -1385,7 +1437,7 @@ function useExploreChat(options = {}) {
|
|
|
1385
1437
|
} catch {
|
|
1386
1438
|
}
|
|
1387
1439
|
}
|
|
1388
|
-
}, [cancelPolling, storageKey]);
|
|
1440
|
+
}, [cancelPolling, rememberConversationToken, storageKey]);
|
|
1389
1441
|
const openConversation = useCallback(
|
|
1390
1442
|
(token) => {
|
|
1391
1443
|
const entry = readArchive(storageKey).find((e) => e.token === token);
|
|
@@ -1397,7 +1449,7 @@ function useExploreChat(options = {}) {
|
|
|
1397
1449
|
});
|
|
1398
1450
|
cancelPolling();
|
|
1399
1451
|
inFlightRef.current = false;
|
|
1400
|
-
|
|
1452
|
+
rememberConversationToken(entry.token);
|
|
1401
1453
|
turnCountRef.current = entry.messages.filter(
|
|
1402
1454
|
(m) => m.role === "USER"
|
|
1403
1455
|
).length;
|
|
@@ -1409,7 +1461,7 @@ function useExploreChat(options = {}) {
|
|
|
1409
1461
|
setPhase(catalogRef.current.enabled ? "ready" : "disabled");
|
|
1410
1462
|
setArchive(readArchive(storageKey));
|
|
1411
1463
|
},
|
|
1412
|
-
[cancelPolling, storageKey]
|
|
1464
|
+
[cancelPolling, rememberConversationToken, storageKey]
|
|
1413
1465
|
);
|
|
1414
1466
|
const deleteConversation = useCallback(
|
|
1415
1467
|
(token) => {
|
|
@@ -1498,6 +1550,69 @@ function useExploreChat(options = {}) {
|
|
|
1498
1550
|
},
|
|
1499
1551
|
[client]
|
|
1500
1552
|
);
|
|
1553
|
+
const escalate = useCallback(
|
|
1554
|
+
async (email, note) => {
|
|
1555
|
+
const token = conversationTokenRef.current;
|
|
1556
|
+
if (!token) {
|
|
1557
|
+
return { outcome: "no-conversation", email: null, createdAt: null };
|
|
1558
|
+
}
|
|
1559
|
+
const attempt = async (captchaAttempt) => {
|
|
1560
|
+
const activeCatalog = catalogRef.current;
|
|
1561
|
+
let recaptchaToken;
|
|
1562
|
+
if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {
|
|
1563
|
+
try {
|
|
1564
|
+
recaptchaToken = await getRecaptchaV3Token(
|
|
1565
|
+
activeCatalog.recaptchaSiteKey
|
|
1566
|
+
);
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
console.warn(
|
|
1569
|
+
"[WebSDK] reCAPTCHA v3 token could not be minted",
|
|
1570
|
+
err
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
const input = {
|
|
1575
|
+
conversationToken: token,
|
|
1576
|
+
email: email.trim(),
|
|
1577
|
+
note: note?.trim() ? note.trim() : null,
|
|
1578
|
+
...recaptchaToken ? { recaptchaToken } : {},
|
|
1579
|
+
...productId ? { productId } : {}
|
|
1580
|
+
};
|
|
1581
|
+
try {
|
|
1582
|
+
const res = await client.mutate(
|
|
1583
|
+
ESCALATE_EXPLORE_CONVERSATION_MUTATION,
|
|
1584
|
+
{ input }
|
|
1585
|
+
);
|
|
1586
|
+
const first = res?.errors?.[0];
|
|
1587
|
+
const result = res?.data?.escalateExploreConversation;
|
|
1588
|
+
if (first || !result) {
|
|
1589
|
+
const code = typeof first?.extensions?.code === "string" ? first.extensions.code : "ESCALATION_FAILED";
|
|
1590
|
+
if (code === "CAPTCHA_FAILED" && captchaAttempt === 0) {
|
|
1591
|
+
return attempt(1);
|
|
1592
|
+
}
|
|
1593
|
+
return {
|
|
1594
|
+
outcome: escalateOutcomeForCode(code),
|
|
1595
|
+
email: null,
|
|
1596
|
+
createdAt: null
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
return {
|
|
1600
|
+
// `created: false` is the double-click, the retry, the reload. The handover
|
|
1601
|
+
// exists, so this is a success — and the address comes from the SERVER, because
|
|
1602
|
+
// on a repeat submit the reply goes to whatever the FIRST handover used.
|
|
1603
|
+
outcome: result.created ? "sent" : "already-handed-over",
|
|
1604
|
+
email: result.escalation?.email ?? null,
|
|
1605
|
+
createdAt: result.escalation?.createdAt ?? null
|
|
1606
|
+
};
|
|
1607
|
+
} catch {
|
|
1608
|
+
return { outcome: "failed", email: null, createdAt: null };
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
return attempt(0);
|
|
1612
|
+
},
|
|
1613
|
+
[client, productId]
|
|
1614
|
+
);
|
|
1615
|
+
const canEscalate = hasConversation && !isSharedView;
|
|
1501
1616
|
return {
|
|
1502
1617
|
phase,
|
|
1503
1618
|
catalog,
|
|
@@ -1518,6 +1633,8 @@ function useExploreChat(options = {}) {
|
|
|
1518
1633
|
clearHistory,
|
|
1519
1634
|
recordClick,
|
|
1520
1635
|
submitFeedback,
|
|
1636
|
+
escalate,
|
|
1637
|
+
canEscalate,
|
|
1521
1638
|
createShareLink,
|
|
1522
1639
|
isSharedView
|
|
1523
1640
|
};
|
|
@@ -1745,7 +1862,360 @@ function useSpeechInput({
|
|
|
1745
1862
|
);
|
|
1746
1863
|
return { supported, listening, interim, error, start, stop, toggle };
|
|
1747
1864
|
}
|
|
1865
|
+
var SILENT_FAILURE_MS = 1500;
|
|
1866
|
+
function speechPlatform() {
|
|
1867
|
+
if (typeof window === "undefined") return null;
|
|
1868
|
+
const scope = window;
|
|
1869
|
+
const synth = scope.speechSynthesis;
|
|
1870
|
+
const Utterance = scope.SpeechSynthesisUtterance;
|
|
1871
|
+
if (!synth || typeof synth.speak !== "function") return null;
|
|
1872
|
+
if (typeof Utterance !== "function") return null;
|
|
1873
|
+
return { synth, Utterance };
|
|
1874
|
+
}
|
|
1875
|
+
function normaliseTag(tag) {
|
|
1876
|
+
return tag.trim().toLowerCase().replace(/_/g, "-");
|
|
1877
|
+
}
|
|
1878
|
+
function primaryOf(tag) {
|
|
1879
|
+
return normaliseTag(tag).split("-")[0] ?? "";
|
|
1880
|
+
}
|
|
1881
|
+
function pickVoice(voices, lang) {
|
|
1882
|
+
const wanted = normaliseTag(lang);
|
|
1883
|
+
if (wanted === "") return null;
|
|
1884
|
+
const primary = primaryOf(wanted);
|
|
1885
|
+
const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);
|
|
1886
|
+
const related = voices.filter((voice) => primaryOf(voice.lang) === primary);
|
|
1887
|
+
return exact.find((voice) => voice.default) ?? exact[0] ?? related.find((voice) => voice.default) ?? related[0] ?? null;
|
|
1888
|
+
}
|
|
1889
|
+
var activeOwner = null;
|
|
1890
|
+
var activeStop = null;
|
|
1891
|
+
function stopSpeaking() {
|
|
1892
|
+
activeStop?.();
|
|
1893
|
+
}
|
|
1894
|
+
function useSpeechOutput({
|
|
1895
|
+
lang
|
|
1896
|
+
} = {}) {
|
|
1897
|
+
const [available, setAvailable] = useState(() => speechPlatform() !== null);
|
|
1898
|
+
const [speaking, setSpeaking] = useState(false);
|
|
1899
|
+
const utteranceRef = useRef(null);
|
|
1900
|
+
const watchdogRef = useRef(null);
|
|
1901
|
+
const ownerRef = useRef({});
|
|
1902
|
+
const stopRef = useRef(() => void 0);
|
|
1903
|
+
const langRef = useRef(lang);
|
|
1904
|
+
langRef.current = lang;
|
|
1905
|
+
const clearWatchdog = useCallback(() => {
|
|
1906
|
+
if (watchdogRef.current === null) return;
|
|
1907
|
+
clearTimeout(watchdogRef.current);
|
|
1908
|
+
watchdogRef.current = null;
|
|
1909
|
+
}, []);
|
|
1910
|
+
const settle = useCallback(() => {
|
|
1911
|
+
clearWatchdog();
|
|
1912
|
+
utteranceRef.current = null;
|
|
1913
|
+
if (activeOwner === ownerRef.current) {
|
|
1914
|
+
activeOwner = null;
|
|
1915
|
+
activeStop = null;
|
|
1916
|
+
}
|
|
1917
|
+
setSpeaking(false);
|
|
1918
|
+
}, [clearWatchdog]);
|
|
1919
|
+
const stop = useCallback(() => {
|
|
1920
|
+
const owned = utteranceRef.current !== null;
|
|
1921
|
+
settle();
|
|
1922
|
+
if (owned) speechPlatform()?.synth.cancel();
|
|
1923
|
+
}, [settle]);
|
|
1924
|
+
stopRef.current = stop;
|
|
1925
|
+
const speak = useCallback(
|
|
1926
|
+
(text) => {
|
|
1927
|
+
const platform = speechPlatform();
|
|
1928
|
+
if (!platform) {
|
|
1929
|
+
setAvailable(false);
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
const spoken = text.trim();
|
|
1933
|
+
if (spoken === "") return;
|
|
1934
|
+
if (activeStop && activeOwner !== ownerRef.current) activeStop();
|
|
1935
|
+
platform.synth.cancel();
|
|
1936
|
+
const utterance = new platform.Utterance(spoken);
|
|
1937
|
+
const wanted = langRef.current;
|
|
1938
|
+
if (wanted !== void 0 && wanted !== "") {
|
|
1939
|
+
const voice = pickVoice(platform.synth.getVoices(), wanted);
|
|
1940
|
+
utterance.lang = voice?.lang ?? wanted;
|
|
1941
|
+
if (voice) utterance.voice = voice;
|
|
1942
|
+
}
|
|
1943
|
+
const mine = () => utteranceRef.current === utterance;
|
|
1944
|
+
utterance.onstart = () => {
|
|
1945
|
+
if (!mine()) return;
|
|
1946
|
+
clearWatchdog();
|
|
1947
|
+
setSpeaking(true);
|
|
1948
|
+
};
|
|
1949
|
+
utterance.onend = () => {
|
|
1950
|
+
if (!mine()) return;
|
|
1951
|
+
settle();
|
|
1952
|
+
};
|
|
1953
|
+
utterance.onerror = (event) => {
|
|
1954
|
+
if (!mine()) return;
|
|
1955
|
+
const code = typeof event?.error === "string" ? event.error : "";
|
|
1956
|
+
settle();
|
|
1957
|
+
if (code !== "interrupted" && code !== "canceled") setAvailable(false);
|
|
1958
|
+
};
|
|
1959
|
+
utteranceRef.current = utterance;
|
|
1960
|
+
activeOwner = ownerRef.current;
|
|
1961
|
+
activeStop = () => {
|
|
1962
|
+
stopRef.current();
|
|
1963
|
+
};
|
|
1964
|
+
setSpeaking(true);
|
|
1965
|
+
platform.synth.speak(utterance);
|
|
1966
|
+
clearWatchdog();
|
|
1967
|
+
watchdogRef.current = setTimeout(() => {
|
|
1968
|
+
watchdogRef.current = null;
|
|
1969
|
+
if (!mine()) return;
|
|
1970
|
+
if (platform.synth.speaking) return;
|
|
1971
|
+
settle();
|
|
1972
|
+
setAvailable(false);
|
|
1973
|
+
}, SILENT_FAILURE_MS);
|
|
1974
|
+
},
|
|
1975
|
+
[clearWatchdog, settle]
|
|
1976
|
+
);
|
|
1977
|
+
const toggle = useCallback(
|
|
1978
|
+
(text) => {
|
|
1979
|
+
if (speaking) stop();
|
|
1980
|
+
else speak(text);
|
|
1981
|
+
},
|
|
1982
|
+
[speak, speaking, stop]
|
|
1983
|
+
);
|
|
1984
|
+
useEffect(() => {
|
|
1985
|
+
if (!speaking || typeof window === "undefined") return;
|
|
1986
|
+
const onHide = () => {
|
|
1987
|
+
stopRef.current();
|
|
1988
|
+
};
|
|
1989
|
+
window.addEventListener("pagehide", onHide);
|
|
1990
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
1991
|
+
}, [speaking]);
|
|
1992
|
+
useEffect(
|
|
1993
|
+
() => () => {
|
|
1994
|
+
stopRef.current();
|
|
1995
|
+
},
|
|
1996
|
+
[]
|
|
1997
|
+
);
|
|
1998
|
+
return { supported: available, speaking, speak, stop, toggle };
|
|
1999
|
+
}
|
|
2000
|
+
var DRAIN_TARGET_MS = 1200;
|
|
2001
|
+
var MIN_CHARS_PER_SECOND = 90;
|
|
2002
|
+
var MAX_CHARS_PER_SECOND = 2200;
|
|
2003
|
+
var MAX_STEP_MS = 250;
|
|
2004
|
+
var COMMIT_INTERVAL_MS = 45;
|
|
2005
|
+
var FALLBACK_STEP_MS = 50;
|
|
2006
|
+
var FENCE = /^ {0,3}(?:```|~~~)/;
|
|
2007
|
+
var TABLE_ROW = /^ {0,3}\|/;
|
|
2008
|
+
var EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\d{1,9}[.)])\s*$/;
|
|
2009
|
+
function isWordCharacter(char) {
|
|
2010
|
+
return char !== void 0 && /[\p{L}\p{N}]/u.test(char);
|
|
2011
|
+
}
|
|
2012
|
+
function clamp(value, min, max) {
|
|
2013
|
+
return Math.min(Math.max(value, min), max);
|
|
2014
|
+
}
|
|
2015
|
+
function now() {
|
|
2016
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
2017
|
+
}
|
|
2018
|
+
function isHighSurrogate(code) {
|
|
2019
|
+
return code >= 55296 && code <= 56319;
|
|
2020
|
+
}
|
|
2021
|
+
function safeInlineEnd(line) {
|
|
2022
|
+
let codeSpan = null;
|
|
2023
|
+
let link = null;
|
|
2024
|
+
const emphasis = [];
|
|
2025
|
+
let at = 0;
|
|
2026
|
+
while (at < line.length) {
|
|
2027
|
+
const char = line[at] ?? "";
|
|
2028
|
+
if (char === "\\") {
|
|
2029
|
+
at += 2;
|
|
2030
|
+
continue;
|
|
2031
|
+
}
|
|
2032
|
+
if (char === "`") {
|
|
2033
|
+
let run = 1;
|
|
2034
|
+
while (line[at + run] === "`") run += 1;
|
|
2035
|
+
codeSpan = codeSpan === null ? at : null;
|
|
2036
|
+
at += run;
|
|
2037
|
+
continue;
|
|
2038
|
+
}
|
|
2039
|
+
if (codeSpan !== null) {
|
|
2040
|
+
at += 1;
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
if (char === "[") {
|
|
2044
|
+
if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
|
|
2045
|
+
at += 1;
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
2048
|
+
if (char === "]") {
|
|
2049
|
+
if (link !== null && line[at + 1] !== "(") link = null;
|
|
2050
|
+
at += 1;
|
|
2051
|
+
continue;
|
|
2052
|
+
}
|
|
2053
|
+
if (char === ")") {
|
|
2054
|
+
link = null;
|
|
2055
|
+
at += 1;
|
|
2056
|
+
continue;
|
|
2057
|
+
}
|
|
2058
|
+
if (char === "*" || char === "_" || char === "~") {
|
|
2059
|
+
let run = 1;
|
|
2060
|
+
while (line[at + run] === char) run += 1;
|
|
2061
|
+
const start = at;
|
|
2062
|
+
at += run;
|
|
2063
|
+
const before = line[start - 1];
|
|
2064
|
+
const after = line[start + run];
|
|
2065
|
+
if (run < 2) continue;
|
|
2066
|
+
if (char === "_" && isWordCharacter(before) && isWordCharacter(after)) {
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
const marker = char.repeat(run);
|
|
2070
|
+
const open2 = emphasis.findIndex((entry) => entry.marker === marker);
|
|
2071
|
+
if (open2 !== -1) {
|
|
2072
|
+
emphasis.splice(open2, 1);
|
|
2073
|
+
continue;
|
|
2074
|
+
}
|
|
2075
|
+
if (after !== void 0 && /\s/.test(after)) continue;
|
|
2076
|
+
emphasis.push({ marker, start });
|
|
2077
|
+
continue;
|
|
2078
|
+
}
|
|
2079
|
+
at += 1;
|
|
2080
|
+
}
|
|
2081
|
+
const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
|
|
2082
|
+
(value) => value !== null
|
|
2083
|
+
);
|
|
2084
|
+
return open.length === 0 ? line.length : Math.min(...open);
|
|
2085
|
+
}
|
|
2086
|
+
function revealBoundary(text, limit) {
|
|
2087
|
+
if (limit <= 0) return 0;
|
|
2088
|
+
const caughtUp = limit >= text.length;
|
|
2089
|
+
const head = caughtUp ? text : text.slice(0, limit);
|
|
2090
|
+
const lineStart = head.lastIndexOf("\n") + 1;
|
|
2091
|
+
const line = head.slice(lineStart);
|
|
2092
|
+
let fenceStart = null;
|
|
2093
|
+
let tableStart = null;
|
|
2094
|
+
let tableRows = 0;
|
|
2095
|
+
let at = 0;
|
|
2096
|
+
while (at < lineStart) {
|
|
2097
|
+
const breakAt = head.indexOf("\n", at);
|
|
2098
|
+
const end = breakAt === -1 ? lineStart : breakAt;
|
|
2099
|
+
const row = head.slice(at, end);
|
|
2100
|
+
if (fenceStart !== null) {
|
|
2101
|
+
if (FENCE.test(row)) fenceStart = null;
|
|
2102
|
+
} else if (FENCE.test(row)) {
|
|
2103
|
+
fenceStart = at;
|
|
2104
|
+
} else if (TABLE_ROW.test(row)) {
|
|
2105
|
+
if (tableStart === null) {
|
|
2106
|
+
tableStart = at;
|
|
2107
|
+
tableRows = 0;
|
|
2108
|
+
}
|
|
2109
|
+
tableRows += 1;
|
|
2110
|
+
} else if (tableStart !== null) {
|
|
2111
|
+
tableStart = null;
|
|
2112
|
+
tableRows = 0;
|
|
2113
|
+
}
|
|
2114
|
+
at = end + 1;
|
|
2115
|
+
}
|
|
2116
|
+
if (fenceStart === null) {
|
|
2117
|
+
const lastIsRow = TABLE_ROW.test(line);
|
|
2118
|
+
if (lastIsRow && tableStart === null) {
|
|
2119
|
+
tableStart = lineStart;
|
|
2120
|
+
tableRows = 0;
|
|
2121
|
+
} else if (!lastIsRow && line !== "") {
|
|
2122
|
+
tableStart = null;
|
|
2123
|
+
tableRows = 0;
|
|
2124
|
+
}
|
|
2125
|
+
if (tableStart !== null && tableRows < 2) return tableStart;
|
|
2126
|
+
}
|
|
2127
|
+
if (caughtUp) return text.length;
|
|
2128
|
+
if (fenceStart !== null) return fenceStart;
|
|
2129
|
+
if (TABLE_ROW.test(line)) return lineStart;
|
|
2130
|
+
if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;
|
|
2131
|
+
const cut = lineStart + safeInlineEnd(line);
|
|
2132
|
+
return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;
|
|
2133
|
+
}
|
|
2134
|
+
function prefersReducedMotion() {
|
|
2135
|
+
if (typeof window === "undefined") return true;
|
|
2136
|
+
try {
|
|
2137
|
+
return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
2138
|
+
} catch {
|
|
2139
|
+
return false;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
function useProgressiveReveal(text, settled) {
|
|
2143
|
+
const [, setFrame] = useState(0);
|
|
2144
|
+
const [reduced] = useState(prefersReducedMotion);
|
|
2145
|
+
const immediate = settled || reduced;
|
|
2146
|
+
const textRef = useRef(text);
|
|
2147
|
+
const cursorRef = useRef(immediate ? text.length : 0);
|
|
2148
|
+
const cutRef = useRef(immediate ? text.length : 0);
|
|
2149
|
+
const committedRef = useRef(0);
|
|
2150
|
+
if (text !== textRef.current) {
|
|
2151
|
+
const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));
|
|
2152
|
+
if (revealed !== "" && !text.startsWith(revealed)) {
|
|
2153
|
+
cursorRef.current = text.length;
|
|
2154
|
+
}
|
|
2155
|
+
cursorRef.current = Math.min(cursorRef.current, text.length);
|
|
2156
|
+
cutRef.current = Math.min(cutRef.current, text.length);
|
|
2157
|
+
textRef.current = text;
|
|
2158
|
+
}
|
|
2159
|
+
if (immediate) cursorRef.current = text.length;
|
|
2160
|
+
const cut = immediate ? text.length : Math.max(
|
|
2161
|
+
cutRef.current,
|
|
2162
|
+
revealBoundary(text, Math.floor(cursorRef.current))
|
|
2163
|
+
);
|
|
2164
|
+
cutRef.current = cut;
|
|
2165
|
+
useEffect(() => {
|
|
2166
|
+
if (immediate) return;
|
|
2167
|
+
if (cursorRef.current >= text.length) return;
|
|
2168
|
+
let cancelled = false;
|
|
2169
|
+
let cancelStep = null;
|
|
2170
|
+
let previous = now();
|
|
2171
|
+
const commit = (at, force) => {
|
|
2172
|
+
if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;
|
|
2173
|
+
committedRef.current = at;
|
|
2174
|
+
setFrame((value) => value + 1);
|
|
2175
|
+
};
|
|
2176
|
+
const step = () => {
|
|
2177
|
+
cancelStep = null;
|
|
2178
|
+
if (cancelled) return;
|
|
2179
|
+
const at = now();
|
|
2180
|
+
const elapsed = clamp(at - previous, 0, MAX_STEP_MS);
|
|
2181
|
+
previous = at;
|
|
2182
|
+
const length = textRef.current.length;
|
|
2183
|
+
const pending = length - cursorRef.current;
|
|
2184
|
+
if (pending <= 0) {
|
|
2185
|
+
commit(at, true);
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
const perSecond = clamp(
|
|
2189
|
+
pending / (DRAIN_TARGET_MS / 1e3),
|
|
2190
|
+
MIN_CHARS_PER_SECOND,
|
|
2191
|
+
MAX_CHARS_PER_SECOND
|
|
2192
|
+
);
|
|
2193
|
+
cursorRef.current = Math.min(
|
|
2194
|
+
length,
|
|
2195
|
+
cursorRef.current + perSecond * elapsed / 1e3
|
|
2196
|
+
);
|
|
2197
|
+
const done = cursorRef.current >= length;
|
|
2198
|
+
commit(at, done);
|
|
2199
|
+
if (!done) schedule();
|
|
2200
|
+
};
|
|
2201
|
+
const schedule = () => {
|
|
2202
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
2203
|
+
const id2 = window.requestAnimationFrame(step);
|
|
2204
|
+
cancelStep = () => window.cancelAnimationFrame(id2);
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
const id = setTimeout(step, FALLBACK_STEP_MS);
|
|
2208
|
+
cancelStep = () => clearTimeout(id);
|
|
2209
|
+
};
|
|
2210
|
+
schedule();
|
|
2211
|
+
return () => {
|
|
2212
|
+
cancelled = true;
|
|
2213
|
+
cancelStep?.();
|
|
2214
|
+
};
|
|
2215
|
+
}, [text, immediate]);
|
|
2216
|
+
return text.slice(0, cut);
|
|
2217
|
+
}
|
|
1748
2218
|
|
|
1749
|
-
export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS, FALLBACK_EXPLORE_LIMITS, classifyExploreError, useExploreChat, useSpeechInput };
|
|
2219
|
+
export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS, FALLBACK_EXPLORE_LIMITS, classifyExploreError, stopSpeaking, useExploreChat, useProgressiveReveal, useSpeechInput, useSpeechOutput };
|
|
1750
2220
|
//# sourceMappingURL=index.mjs.map
|
|
1751
2221
|
//# sourceMappingURL=index.mjs.map
|