@burdenoff/website-sdk 2026.922.3 → 2026.922.5
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 +92 -2
- package/dist/components/explore.d.ts +92 -2
- package/dist/components/explore.js +1171 -17
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +1169 -20
- package/dist/components/explore.mjs.map +1 -1
- package/dist/{explore-types-CPF1QoMn.d.mts → explore-types-DvQPiL1t.d.mts} +58 -2
- package/dist/{explore-types-CPF1QoMn.d.ts → explore-types-DvQPiL1t.d.ts} +58 -2
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +417 -8
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +415 -9
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +53 -3
- package/dist/index.d.ts +53 -3
- package/dist/index.js +1179 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1177 -28
- package/dist/index.mjs.map +1 -1
- package/dist/{use-explore-chat--CxNtkrJ.d.ts → use-progressive-reveal-BfU_IHip.d.ts} +36 -2
- package/dist/{use-explore-chat-C6Y8JQMj.d.mts → use-progressive-reveal-Ha3gYPfs.d.mts} +36 -2
- package/package.json +1 -1
package/dist/hooks/index.mjs
CHANGED
|
@@ -275,11 +275,9 @@ var EXPLORE_OWN_MESSAGE_FIELDS = (
|
|
|
275
275
|
}
|
|
276
276
|
`
|
|
277
277
|
);
|
|
278
|
-
var
|
|
278
|
+
var EXPLORE_CATALOG_ESTABLISHED_FIELDS = (
|
|
279
279
|
/* GraphQL */
|
|
280
280
|
`
|
|
281
|
-
query ExploreCatalog($productSlug: String) {
|
|
282
|
-
exploreCatalog(productSlug: $productSlug) {
|
|
283
281
|
enabled
|
|
284
282
|
products {
|
|
285
283
|
slug
|
|
@@ -297,11 +295,35 @@ var EXPLORE_CATALOG_QUERY = (
|
|
|
297
295
|
historyTurns
|
|
298
296
|
}
|
|
299
297
|
captchaRequired
|
|
300
|
-
recaptchaSiteKey
|
|
298
|
+
recaptchaSiteKey`
|
|
299
|
+
);
|
|
300
|
+
var EXPLORE_CATALOG_HIGHLIGHT_FIELD = (
|
|
301
|
+
/* GraphQL */
|
|
302
|
+
`
|
|
303
|
+
conceptHighlights {
|
|
304
|
+
product
|
|
305
|
+
productName
|
|
306
|
+
title
|
|
307
|
+
url
|
|
308
|
+
imageUrl
|
|
309
|
+
alt
|
|
310
|
+
}`
|
|
311
|
+
);
|
|
312
|
+
function exploreCatalogDocument(extraFields) {
|
|
313
|
+
return (
|
|
314
|
+
/* GraphQL */
|
|
315
|
+
`
|
|
316
|
+
query ExploreCatalog($productSlug: String) {
|
|
317
|
+
exploreCatalog(productSlug: $productSlug) {${EXPLORE_CATALOG_ESTABLISHED_FIELDS}${extraFields}
|
|
301
318
|
}
|
|
302
319
|
}
|
|
303
320
|
`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
var EXPLORE_CATALOG_QUERY = exploreCatalogDocument(
|
|
324
|
+
EXPLORE_CATALOG_HIGHLIGHT_FIELD
|
|
304
325
|
);
|
|
326
|
+
var EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS = exploreCatalogDocument("");
|
|
305
327
|
var EXPLORE_MESSAGE_QUERY = (
|
|
306
328
|
/* GraphQL */
|
|
307
329
|
`
|
|
@@ -665,7 +687,12 @@ function buildFallbackCatalog(examplePrompts, welcome) {
|
|
|
665
687
|
welcomeBody: welcome?.body ?? FALLBACK_WELCOME_BODY,
|
|
666
688
|
limits: FALLBACK_EXPLORE_LIMITS,
|
|
667
689
|
captchaRequired: false,
|
|
668
|
-
recaptchaSiteKey: null
|
|
690
|
+
recaptchaSiteKey: null,
|
|
691
|
+
// Empty, and deliberately not synthesised from `ECOSYSTEM_PRODUCTS`: a concept highlight
|
|
692
|
+
// needs a real published illustration URL, and this SDK has no registry of them. An
|
|
693
|
+
// invented path would render 38 broken frames on the one screen every prerender captures.
|
|
694
|
+
// The strip renders nothing at all when the list is empty, which is the honest fallback.
|
|
695
|
+
conceptHighlights: []
|
|
669
696
|
};
|
|
670
697
|
}
|
|
671
698
|
function normalizeCatalog(raw, fallback) {
|
|
@@ -682,9 +709,27 @@ function normalizeCatalog(raw, fallback) {
|
|
|
682
709
|
historyTurns: raw.limits?.historyTurns || fallback.limits.historyTurns
|
|
683
710
|
},
|
|
684
711
|
captchaRequired: raw.captchaRequired === true,
|
|
685
|
-
recaptchaSiteKey: raw.recaptchaSiteKey ?? null
|
|
712
|
+
recaptchaSiteKey: raw.recaptchaSiteKey ?? null,
|
|
713
|
+
// `[ExploreConceptHighlight!]!` on the wire, so a gateway that KNOWS the field always
|
|
714
|
+
// sends an array. The one response that arrives without it is the retry against an older
|
|
715
|
+
// supergraph (`EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS`) — that gateway does not answer
|
|
716
|
+
// the full document at all, it refuses it. Either way
|
|
717
|
+
// `catalog.conceptHighlights.filter(...)` on `undefined` would throw inside render,
|
|
718
|
+
// blanking the welcome screen over a decorative strip.
|
|
719
|
+
conceptHighlights: raw.conceptHighlights?.length ? raw.conceptHighlights : fallback.conceptHighlights
|
|
686
720
|
};
|
|
687
721
|
}
|
|
722
|
+
var CATALOG_DOCUMENT_REJECTED_CODES = /* @__PURE__ */ new Set([
|
|
723
|
+
"GRAPHQL_VALIDATION_FAILED",
|
|
724
|
+
"HTTP_400"
|
|
725
|
+
]);
|
|
726
|
+
function catalogRejectedTheHighlightField(res) {
|
|
727
|
+
if (res.data?.exploreCatalog) return false;
|
|
728
|
+
return (res.errors ?? []).some((error) => {
|
|
729
|
+
const code = error.extensions?.code;
|
|
730
|
+
return typeof code === "string" && CATALOG_DOCUMENT_REJECTED_CODES.has(code) || error.message.includes("conceptHighlights");
|
|
731
|
+
});
|
|
732
|
+
}
|
|
688
733
|
var RATE_LIMIT_CODES = /* @__PURE__ */ new Set([
|
|
689
734
|
"RATE_LIMITED",
|
|
690
735
|
"HTTP_429",
|
|
@@ -1024,11 +1069,19 @@ function useExploreChat(options = {}) {
|
|
|
1024
1069
|
useEffect(() => {
|
|
1025
1070
|
let cancelled = false;
|
|
1026
1071
|
const load = async () => {
|
|
1027
|
-
const
|
|
1072
|
+
const variables = { productSlug: productSlug ?? null };
|
|
1073
|
+
let res = await client.query(
|
|
1028
1074
|
EXPLORE_CATALOG_QUERY,
|
|
1029
|
-
|
|
1075
|
+
variables
|
|
1030
1076
|
);
|
|
1031
1077
|
if (cancelled || !mountedRef.current) return;
|
|
1078
|
+
if (catalogRejectedTheHighlightField(res)) {
|
|
1079
|
+
res = await client.query(
|
|
1080
|
+
EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,
|
|
1081
|
+
variables
|
|
1082
|
+
);
|
|
1083
|
+
if (cancelled || !mountedRef.current) return;
|
|
1084
|
+
}
|
|
1032
1085
|
const raw = res.data?.exploreCatalog;
|
|
1033
1086
|
if (res.errors?.length || !raw) {
|
|
1034
1087
|
console.warn(
|
|
@@ -1692,7 +1745,360 @@ function useSpeechInput({
|
|
|
1692
1745
|
);
|
|
1693
1746
|
return { supported, listening, interim, error, start, stop, toggle };
|
|
1694
1747
|
}
|
|
1748
|
+
var SILENT_FAILURE_MS = 1500;
|
|
1749
|
+
function speechPlatform() {
|
|
1750
|
+
if (typeof window === "undefined") return null;
|
|
1751
|
+
const scope = window;
|
|
1752
|
+
const synth = scope.speechSynthesis;
|
|
1753
|
+
const Utterance = scope.SpeechSynthesisUtterance;
|
|
1754
|
+
if (!synth || typeof synth.speak !== "function") return null;
|
|
1755
|
+
if (typeof Utterance !== "function") return null;
|
|
1756
|
+
return { synth, Utterance };
|
|
1757
|
+
}
|
|
1758
|
+
function normaliseTag(tag) {
|
|
1759
|
+
return tag.trim().toLowerCase().replace(/_/g, "-");
|
|
1760
|
+
}
|
|
1761
|
+
function primaryOf(tag) {
|
|
1762
|
+
return normaliseTag(tag).split("-")[0] ?? "";
|
|
1763
|
+
}
|
|
1764
|
+
function pickVoice(voices, lang) {
|
|
1765
|
+
const wanted = normaliseTag(lang);
|
|
1766
|
+
if (wanted === "") return null;
|
|
1767
|
+
const primary = primaryOf(wanted);
|
|
1768
|
+
const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);
|
|
1769
|
+
const related = voices.filter((voice) => primaryOf(voice.lang) === primary);
|
|
1770
|
+
return exact.find((voice) => voice.default) ?? exact[0] ?? related.find((voice) => voice.default) ?? related[0] ?? null;
|
|
1771
|
+
}
|
|
1772
|
+
var activeOwner = null;
|
|
1773
|
+
var activeStop = null;
|
|
1774
|
+
function stopSpeaking() {
|
|
1775
|
+
activeStop?.();
|
|
1776
|
+
}
|
|
1777
|
+
function useSpeechOutput({
|
|
1778
|
+
lang
|
|
1779
|
+
} = {}) {
|
|
1780
|
+
const [available, setAvailable] = useState(() => speechPlatform() !== null);
|
|
1781
|
+
const [speaking, setSpeaking] = useState(false);
|
|
1782
|
+
const utteranceRef = useRef(null);
|
|
1783
|
+
const watchdogRef = useRef(null);
|
|
1784
|
+
const ownerRef = useRef({});
|
|
1785
|
+
const stopRef = useRef(() => void 0);
|
|
1786
|
+
const langRef = useRef(lang);
|
|
1787
|
+
langRef.current = lang;
|
|
1788
|
+
const clearWatchdog = useCallback(() => {
|
|
1789
|
+
if (watchdogRef.current === null) return;
|
|
1790
|
+
clearTimeout(watchdogRef.current);
|
|
1791
|
+
watchdogRef.current = null;
|
|
1792
|
+
}, []);
|
|
1793
|
+
const settle = useCallback(() => {
|
|
1794
|
+
clearWatchdog();
|
|
1795
|
+
utteranceRef.current = null;
|
|
1796
|
+
if (activeOwner === ownerRef.current) {
|
|
1797
|
+
activeOwner = null;
|
|
1798
|
+
activeStop = null;
|
|
1799
|
+
}
|
|
1800
|
+
setSpeaking(false);
|
|
1801
|
+
}, [clearWatchdog]);
|
|
1802
|
+
const stop = useCallback(() => {
|
|
1803
|
+
const owned = utteranceRef.current !== null;
|
|
1804
|
+
settle();
|
|
1805
|
+
if (owned) speechPlatform()?.synth.cancel();
|
|
1806
|
+
}, [settle]);
|
|
1807
|
+
stopRef.current = stop;
|
|
1808
|
+
const speak = useCallback(
|
|
1809
|
+
(text) => {
|
|
1810
|
+
const platform = speechPlatform();
|
|
1811
|
+
if (!platform) {
|
|
1812
|
+
setAvailable(false);
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
const spoken = text.trim();
|
|
1816
|
+
if (spoken === "") return;
|
|
1817
|
+
if (activeStop && activeOwner !== ownerRef.current) activeStop();
|
|
1818
|
+
platform.synth.cancel();
|
|
1819
|
+
const utterance = new platform.Utterance(spoken);
|
|
1820
|
+
const wanted = langRef.current;
|
|
1821
|
+
if (wanted !== void 0 && wanted !== "") {
|
|
1822
|
+
const voice = pickVoice(platform.synth.getVoices(), wanted);
|
|
1823
|
+
utterance.lang = voice?.lang ?? wanted;
|
|
1824
|
+
if (voice) utterance.voice = voice;
|
|
1825
|
+
}
|
|
1826
|
+
const mine = () => utteranceRef.current === utterance;
|
|
1827
|
+
utterance.onstart = () => {
|
|
1828
|
+
if (!mine()) return;
|
|
1829
|
+
clearWatchdog();
|
|
1830
|
+
setSpeaking(true);
|
|
1831
|
+
};
|
|
1832
|
+
utterance.onend = () => {
|
|
1833
|
+
if (!mine()) return;
|
|
1834
|
+
settle();
|
|
1835
|
+
};
|
|
1836
|
+
utterance.onerror = (event) => {
|
|
1837
|
+
if (!mine()) return;
|
|
1838
|
+
const code = typeof event?.error === "string" ? event.error : "";
|
|
1839
|
+
settle();
|
|
1840
|
+
if (code !== "interrupted" && code !== "canceled") setAvailable(false);
|
|
1841
|
+
};
|
|
1842
|
+
utteranceRef.current = utterance;
|
|
1843
|
+
activeOwner = ownerRef.current;
|
|
1844
|
+
activeStop = () => {
|
|
1845
|
+
stopRef.current();
|
|
1846
|
+
};
|
|
1847
|
+
setSpeaking(true);
|
|
1848
|
+
platform.synth.speak(utterance);
|
|
1849
|
+
clearWatchdog();
|
|
1850
|
+
watchdogRef.current = setTimeout(() => {
|
|
1851
|
+
watchdogRef.current = null;
|
|
1852
|
+
if (!mine()) return;
|
|
1853
|
+
if (platform.synth.speaking) return;
|
|
1854
|
+
settle();
|
|
1855
|
+
setAvailable(false);
|
|
1856
|
+
}, SILENT_FAILURE_MS);
|
|
1857
|
+
},
|
|
1858
|
+
[clearWatchdog, settle]
|
|
1859
|
+
);
|
|
1860
|
+
const toggle = useCallback(
|
|
1861
|
+
(text) => {
|
|
1862
|
+
if (speaking) stop();
|
|
1863
|
+
else speak(text);
|
|
1864
|
+
},
|
|
1865
|
+
[speak, speaking, stop]
|
|
1866
|
+
);
|
|
1867
|
+
useEffect(() => {
|
|
1868
|
+
if (!speaking || typeof window === "undefined") return;
|
|
1869
|
+
const onHide = () => {
|
|
1870
|
+
stopRef.current();
|
|
1871
|
+
};
|
|
1872
|
+
window.addEventListener("pagehide", onHide);
|
|
1873
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
1874
|
+
}, [speaking]);
|
|
1875
|
+
useEffect(
|
|
1876
|
+
() => () => {
|
|
1877
|
+
stopRef.current();
|
|
1878
|
+
},
|
|
1879
|
+
[]
|
|
1880
|
+
);
|
|
1881
|
+
return { supported: available, speaking, speak, stop, toggle };
|
|
1882
|
+
}
|
|
1883
|
+
var DRAIN_TARGET_MS = 1200;
|
|
1884
|
+
var MIN_CHARS_PER_SECOND = 90;
|
|
1885
|
+
var MAX_CHARS_PER_SECOND = 2200;
|
|
1886
|
+
var MAX_STEP_MS = 250;
|
|
1887
|
+
var COMMIT_INTERVAL_MS = 45;
|
|
1888
|
+
var FALLBACK_STEP_MS = 50;
|
|
1889
|
+
var FENCE = /^ {0,3}(?:```|~~~)/;
|
|
1890
|
+
var TABLE_ROW = /^ {0,3}\|/;
|
|
1891
|
+
var EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\d{1,9}[.)])\s*$/;
|
|
1892
|
+
function isWordCharacter(char) {
|
|
1893
|
+
return char !== void 0 && /[\p{L}\p{N}]/u.test(char);
|
|
1894
|
+
}
|
|
1895
|
+
function clamp(value, min, max) {
|
|
1896
|
+
return Math.min(Math.max(value, min), max);
|
|
1897
|
+
}
|
|
1898
|
+
function now() {
|
|
1899
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
1900
|
+
}
|
|
1901
|
+
function isHighSurrogate(code) {
|
|
1902
|
+
return code >= 55296 && code <= 56319;
|
|
1903
|
+
}
|
|
1904
|
+
function safeInlineEnd(line) {
|
|
1905
|
+
let codeSpan = null;
|
|
1906
|
+
let link = null;
|
|
1907
|
+
const emphasis = [];
|
|
1908
|
+
let at = 0;
|
|
1909
|
+
while (at < line.length) {
|
|
1910
|
+
const char = line[at] ?? "";
|
|
1911
|
+
if (char === "\\") {
|
|
1912
|
+
at += 2;
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
if (char === "`") {
|
|
1916
|
+
let run = 1;
|
|
1917
|
+
while (line[at + run] === "`") run += 1;
|
|
1918
|
+
codeSpan = codeSpan === null ? at : null;
|
|
1919
|
+
at += run;
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
if (codeSpan !== null) {
|
|
1923
|
+
at += 1;
|
|
1924
|
+
continue;
|
|
1925
|
+
}
|
|
1926
|
+
if (char === "[") {
|
|
1927
|
+
if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
|
|
1928
|
+
at += 1;
|
|
1929
|
+
continue;
|
|
1930
|
+
}
|
|
1931
|
+
if (char === "]") {
|
|
1932
|
+
if (link !== null && line[at + 1] !== "(") link = null;
|
|
1933
|
+
at += 1;
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
if (char === ")") {
|
|
1937
|
+
link = null;
|
|
1938
|
+
at += 1;
|
|
1939
|
+
continue;
|
|
1940
|
+
}
|
|
1941
|
+
if (char === "*" || char === "_" || char === "~") {
|
|
1942
|
+
let run = 1;
|
|
1943
|
+
while (line[at + run] === char) run += 1;
|
|
1944
|
+
const start = at;
|
|
1945
|
+
at += run;
|
|
1946
|
+
const before = line[start - 1];
|
|
1947
|
+
const after = line[start + run];
|
|
1948
|
+
if (run < 2) continue;
|
|
1949
|
+
if (char === "_" && isWordCharacter(before) && isWordCharacter(after)) {
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
const marker = char.repeat(run);
|
|
1953
|
+
const open2 = emphasis.findIndex((entry) => entry.marker === marker);
|
|
1954
|
+
if (open2 !== -1) {
|
|
1955
|
+
emphasis.splice(open2, 1);
|
|
1956
|
+
continue;
|
|
1957
|
+
}
|
|
1958
|
+
if (after !== void 0 && /\s/.test(after)) continue;
|
|
1959
|
+
emphasis.push({ marker, start });
|
|
1960
|
+
continue;
|
|
1961
|
+
}
|
|
1962
|
+
at += 1;
|
|
1963
|
+
}
|
|
1964
|
+
const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
|
|
1965
|
+
(value) => value !== null
|
|
1966
|
+
);
|
|
1967
|
+
return open.length === 0 ? line.length : Math.min(...open);
|
|
1968
|
+
}
|
|
1969
|
+
function revealBoundary(text, limit) {
|
|
1970
|
+
if (limit <= 0) return 0;
|
|
1971
|
+
const caughtUp = limit >= text.length;
|
|
1972
|
+
const head = caughtUp ? text : text.slice(0, limit);
|
|
1973
|
+
const lineStart = head.lastIndexOf("\n") + 1;
|
|
1974
|
+
const line = head.slice(lineStart);
|
|
1975
|
+
let fenceStart = null;
|
|
1976
|
+
let tableStart = null;
|
|
1977
|
+
let tableRows = 0;
|
|
1978
|
+
let at = 0;
|
|
1979
|
+
while (at < lineStart) {
|
|
1980
|
+
const breakAt = head.indexOf("\n", at);
|
|
1981
|
+
const end = breakAt === -1 ? lineStart : breakAt;
|
|
1982
|
+
const row = head.slice(at, end);
|
|
1983
|
+
if (fenceStart !== null) {
|
|
1984
|
+
if (FENCE.test(row)) fenceStart = null;
|
|
1985
|
+
} else if (FENCE.test(row)) {
|
|
1986
|
+
fenceStart = at;
|
|
1987
|
+
} else if (TABLE_ROW.test(row)) {
|
|
1988
|
+
if (tableStart === null) {
|
|
1989
|
+
tableStart = at;
|
|
1990
|
+
tableRows = 0;
|
|
1991
|
+
}
|
|
1992
|
+
tableRows += 1;
|
|
1993
|
+
} else if (tableStart !== null) {
|
|
1994
|
+
tableStart = null;
|
|
1995
|
+
tableRows = 0;
|
|
1996
|
+
}
|
|
1997
|
+
at = end + 1;
|
|
1998
|
+
}
|
|
1999
|
+
if (fenceStart === null) {
|
|
2000
|
+
const lastIsRow = TABLE_ROW.test(line);
|
|
2001
|
+
if (lastIsRow && tableStart === null) {
|
|
2002
|
+
tableStart = lineStart;
|
|
2003
|
+
tableRows = 0;
|
|
2004
|
+
} else if (!lastIsRow && line !== "") {
|
|
2005
|
+
tableStart = null;
|
|
2006
|
+
tableRows = 0;
|
|
2007
|
+
}
|
|
2008
|
+
if (tableStart !== null && tableRows < 2) return tableStart;
|
|
2009
|
+
}
|
|
2010
|
+
if (caughtUp) return text.length;
|
|
2011
|
+
if (fenceStart !== null) return fenceStart;
|
|
2012
|
+
if (TABLE_ROW.test(line)) return lineStart;
|
|
2013
|
+
if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;
|
|
2014
|
+
const cut = lineStart + safeInlineEnd(line);
|
|
2015
|
+
return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;
|
|
2016
|
+
}
|
|
2017
|
+
function prefersReducedMotion() {
|
|
2018
|
+
if (typeof window === "undefined") return true;
|
|
2019
|
+
try {
|
|
2020
|
+
return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
2021
|
+
} catch {
|
|
2022
|
+
return false;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
function useProgressiveReveal(text, settled) {
|
|
2026
|
+
const [, setFrame] = useState(0);
|
|
2027
|
+
const [reduced] = useState(prefersReducedMotion);
|
|
2028
|
+
const immediate = settled || reduced;
|
|
2029
|
+
const textRef = useRef(text);
|
|
2030
|
+
const cursorRef = useRef(immediate ? text.length : 0);
|
|
2031
|
+
const cutRef = useRef(immediate ? text.length : 0);
|
|
2032
|
+
const committedRef = useRef(0);
|
|
2033
|
+
if (text !== textRef.current) {
|
|
2034
|
+
const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));
|
|
2035
|
+
if (revealed !== "" && !text.startsWith(revealed)) {
|
|
2036
|
+
cursorRef.current = text.length;
|
|
2037
|
+
}
|
|
2038
|
+
cursorRef.current = Math.min(cursorRef.current, text.length);
|
|
2039
|
+
cutRef.current = Math.min(cutRef.current, text.length);
|
|
2040
|
+
textRef.current = text;
|
|
2041
|
+
}
|
|
2042
|
+
if (immediate) cursorRef.current = text.length;
|
|
2043
|
+
const cut = immediate ? text.length : Math.max(
|
|
2044
|
+
cutRef.current,
|
|
2045
|
+
revealBoundary(text, Math.floor(cursorRef.current))
|
|
2046
|
+
);
|
|
2047
|
+
cutRef.current = cut;
|
|
2048
|
+
useEffect(() => {
|
|
2049
|
+
if (immediate) return;
|
|
2050
|
+
if (cursorRef.current >= text.length) return;
|
|
2051
|
+
let cancelled = false;
|
|
2052
|
+
let cancelStep = null;
|
|
2053
|
+
let previous = now();
|
|
2054
|
+
const commit = (at, force) => {
|
|
2055
|
+
if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;
|
|
2056
|
+
committedRef.current = at;
|
|
2057
|
+
setFrame((value) => value + 1);
|
|
2058
|
+
};
|
|
2059
|
+
const step = () => {
|
|
2060
|
+
cancelStep = null;
|
|
2061
|
+
if (cancelled) return;
|
|
2062
|
+
const at = now();
|
|
2063
|
+
const elapsed = clamp(at - previous, 0, MAX_STEP_MS);
|
|
2064
|
+
previous = at;
|
|
2065
|
+
const length = textRef.current.length;
|
|
2066
|
+
const pending = length - cursorRef.current;
|
|
2067
|
+
if (pending <= 0) {
|
|
2068
|
+
commit(at, true);
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
const perSecond = clamp(
|
|
2072
|
+
pending / (DRAIN_TARGET_MS / 1e3),
|
|
2073
|
+
MIN_CHARS_PER_SECOND,
|
|
2074
|
+
MAX_CHARS_PER_SECOND
|
|
2075
|
+
);
|
|
2076
|
+
cursorRef.current = Math.min(
|
|
2077
|
+
length,
|
|
2078
|
+
cursorRef.current + perSecond * elapsed / 1e3
|
|
2079
|
+
);
|
|
2080
|
+
const done = cursorRef.current >= length;
|
|
2081
|
+
commit(at, done);
|
|
2082
|
+
if (!done) schedule();
|
|
2083
|
+
};
|
|
2084
|
+
const schedule = () => {
|
|
2085
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
2086
|
+
const id2 = window.requestAnimationFrame(step);
|
|
2087
|
+
cancelStep = () => window.cancelAnimationFrame(id2);
|
|
2088
|
+
return;
|
|
2089
|
+
}
|
|
2090
|
+
const id = setTimeout(step, FALLBACK_STEP_MS);
|
|
2091
|
+
cancelStep = () => clearTimeout(id);
|
|
2092
|
+
};
|
|
2093
|
+
schedule();
|
|
2094
|
+
return () => {
|
|
2095
|
+
cancelled = true;
|
|
2096
|
+
cancelStep?.();
|
|
2097
|
+
};
|
|
2098
|
+
}, [text, immediate]);
|
|
2099
|
+
return text.slice(0, cut);
|
|
2100
|
+
}
|
|
1695
2101
|
|
|
1696
|
-
export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS, FALLBACK_EXPLORE_LIMITS, classifyExploreError, useExploreChat, useSpeechInput };
|
|
2102
|
+
export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS, FALLBACK_EXPLORE_LIMITS, classifyExploreError, stopSpeaking, useExploreChat, useProgressiveReveal, useSpeechInput, useSpeechOutput };
|
|
1697
2103
|
//# sourceMappingURL=index.mjs.map
|
|
1698
2104
|
//# sourceMappingURL=index.mjs.map
|