@burdenoff/website-sdk 2026.828.4 → 2026.828.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-cta.js +9 -7
- package/dist/components/explore-cta.js.map +1 -1
- package/dist/components/explore-cta.mjs +9 -7
- package/dist/components/explore-cta.mjs.map +1 -1
- package/dist/components/explore.js +427 -41
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +429 -43
- package/dist/components/explore.mjs.map +1 -1
- package/dist/hooks/index.d.mts +24 -1
- package/dist/hooks/index.d.ts +24 -1
- package/dist/hooks/index.js +235 -5
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +235 -6
- 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 +436 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +437 -49
- package/dist/index.mjs.map +1 -1
- package/dist/{index-CUJTTlYx.d.mts → use-explore-chat-DhvZhH8f.d.mts} +21 -2
- package/dist/{index-CUJTTlYx.d.ts → use-explore-chat-DhvZhH8f.d.ts} +21 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { createContext, useMemo, useState, useRef,
|
|
3
|
-
import { Sparkles, RotateCcw, Square, ArrowUp, BookOpenText, Layers, Link2, TriangleAlert, ExternalLink } from 'lucide-react';
|
|
2
|
+
import { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext } from 'react';
|
|
3
|
+
import { Sparkles, RotateCcw, History, Trash2, Mic, Square, ArrowUp, BookOpenText, Layers, Link2, TriangleAlert, ExternalLink } from 'lucide-react';
|
|
4
4
|
import ReactMarkdown from 'react-markdown';
|
|
5
5
|
import rehypeSanitize from 'rehype-sanitize';
|
|
6
6
|
import remarkGfm from 'remark-gfm';
|
|
@@ -780,6 +780,8 @@ async function getRecaptchaV3Token(siteKey, action = EXPLORE_RECAPTCHA_ACTION) {
|
|
|
780
780
|
|
|
781
781
|
// src/hooks/use-explore-chat.ts
|
|
782
782
|
var DEFAULT_STORAGE_KEY = "boff.explore.v1";
|
|
783
|
+
var HISTORY_SUFFIX = ".history";
|
|
784
|
+
var MAX_ARCHIVED_CONVERSATIONS = 15;
|
|
783
785
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
784
786
|
var DEFAULT_POLL_TIMEOUT_MS = 9e4;
|
|
785
787
|
var POLL_REQUEST_TIMEOUT_MS = 1e4;
|
|
@@ -918,10 +920,58 @@ function classifyExploreError(code, serverMessage) {
|
|
|
918
920
|
retryable: true
|
|
919
921
|
};
|
|
920
922
|
}
|
|
923
|
+
function historyKey(key) {
|
|
924
|
+
return `${key}${HISTORY_SUFFIX}`;
|
|
925
|
+
}
|
|
926
|
+
function readArchive(key) {
|
|
927
|
+
if (typeof window === "undefined") return [];
|
|
928
|
+
try {
|
|
929
|
+
const raw = window.localStorage.getItem(historyKey(key));
|
|
930
|
+
if (!raw) return [];
|
|
931
|
+
const parsed = JSON.parse(raw);
|
|
932
|
+
if (!Array.isArray(parsed)) return [];
|
|
933
|
+
return parsed.filter(
|
|
934
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.token === "string" && Array.isArray(entry.messages)
|
|
935
|
+
);
|
|
936
|
+
} catch {
|
|
937
|
+
return [];
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
function writeArchive(key, entries) {
|
|
941
|
+
if (typeof window === "undefined") return;
|
|
942
|
+
try {
|
|
943
|
+
if (entries.length === 0) {
|
|
944
|
+
window.localStorage.removeItem(historyKey(key));
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
window.localStorage.setItem(historyKey(key), JSON.stringify(entries));
|
|
948
|
+
} catch {
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
function upsertArchive(key, thread) {
|
|
952
|
+
if (!thread.token) return readArchive(key);
|
|
953
|
+
const real = thread.messages.filter(
|
|
954
|
+
(m) => m.content.trim() !== "" || m.role === "USER"
|
|
955
|
+
);
|
|
956
|
+
if (real.length === 0) return readArchive(key);
|
|
957
|
+
const firstUser = real.find((m) => m.role === "USER");
|
|
958
|
+
const entry = {
|
|
959
|
+
token: thread.token,
|
|
960
|
+
title: (firstUser?.content ?? "Conversation").trim().slice(0, 80),
|
|
961
|
+
updatedAt: real[real.length - 1]?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
962
|
+
messageCount: real.length,
|
|
963
|
+
focusProduct: thread.focusProduct,
|
|
964
|
+
messages: real.slice(-20)
|
|
965
|
+
};
|
|
966
|
+
const rest = readArchive(key).filter((e) => e.token !== entry.token);
|
|
967
|
+
const next = [entry, ...rest].slice(0, MAX_ARCHIVED_CONVERSATIONS);
|
|
968
|
+
writeArchive(key, next);
|
|
969
|
+
return next;
|
|
970
|
+
}
|
|
921
971
|
function readPersisted(key) {
|
|
922
972
|
if (typeof window === "undefined") return null;
|
|
923
973
|
try {
|
|
924
|
-
const raw = window.
|
|
974
|
+
const raw = window.localStorage.getItem(key);
|
|
925
975
|
if (!raw) return null;
|
|
926
976
|
const parsed = JSON.parse(raw);
|
|
927
977
|
if (!parsed || typeof parsed !== "object") return null;
|
|
@@ -966,6 +1016,8 @@ function useExploreChat(options = {}) {
|
|
|
966
1016
|
const [remainingToday, setRemainingToday] = useState(null);
|
|
967
1017
|
const [focusProduct, setFocusProductState] = useState(null);
|
|
968
1018
|
const [turnCount, setTurnCount] = useState(0);
|
|
1019
|
+
const messagesRef = useRef([]);
|
|
1020
|
+
const [archive, setArchive] = useState([]);
|
|
969
1021
|
const [hydrated, setHydrated] = useState(false);
|
|
970
1022
|
const mountedRef = useRef(true);
|
|
971
1023
|
const pollGenerationRef = useRef(0);
|
|
@@ -1003,6 +1055,7 @@ function useExploreChat(options = {}) {
|
|
|
1003
1055
|
setHydrated(true);
|
|
1004
1056
|
return;
|
|
1005
1057
|
}
|
|
1058
|
+
setArchive(readArchive(storageKey));
|
|
1006
1059
|
const stored = readPersisted(storageKey);
|
|
1007
1060
|
if (stored) {
|
|
1008
1061
|
conversationTokenRef.current = stored.conversationToken;
|
|
@@ -1031,13 +1084,28 @@ function useExploreChat(options = {}) {
|
|
|
1031
1084
|
focusProduct
|
|
1032
1085
|
};
|
|
1033
1086
|
if (!payload.conversationToken && payload.messages.length === 0 && !payload.focusProduct) {
|
|
1034
|
-
window.
|
|
1087
|
+
window.localStorage.removeItem(storageKey);
|
|
1035
1088
|
return;
|
|
1036
1089
|
}
|
|
1037
|
-
window.
|
|
1090
|
+
window.localStorage.setItem(storageKey, JSON.stringify(payload));
|
|
1038
1091
|
} catch {
|
|
1039
1092
|
}
|
|
1040
1093
|
}, [persist, hydrated, storageKey, messages, focusProduct]);
|
|
1094
|
+
useEffect(() => {
|
|
1095
|
+
messagesRef.current = messages;
|
|
1096
|
+
if (!persist || !hydrated) return;
|
|
1097
|
+
const settled = messages.some(
|
|
1098
|
+
(m) => m.role === "ASSISTANT" && (m.status === "COMPLETED" || m.status === "FAILED")
|
|
1099
|
+
);
|
|
1100
|
+
if (!settled) return;
|
|
1101
|
+
setArchive(
|
|
1102
|
+
upsertArchive(storageKey, {
|
|
1103
|
+
token: conversationTokenRef.current,
|
|
1104
|
+
messages,
|
|
1105
|
+
focusProduct: focusProductRef.current
|
|
1106
|
+
})
|
|
1107
|
+
);
|
|
1108
|
+
}, [messages, persist, hydrated, storageKey]);
|
|
1041
1109
|
useEffect(() => {
|
|
1042
1110
|
let cancelled = false;
|
|
1043
1111
|
const load = async () => {
|
|
@@ -1323,6 +1391,13 @@ function useExploreChat(options = {}) {
|
|
|
1323
1391
|
void send(text);
|
|
1324
1392
|
}, [cancelPolling, send]);
|
|
1325
1393
|
const reset = useCallback(() => {
|
|
1394
|
+
setArchive(
|
|
1395
|
+
upsertArchive(storageKey, {
|
|
1396
|
+
token: conversationTokenRef.current,
|
|
1397
|
+
messages: messagesRef.current,
|
|
1398
|
+
focusProduct: focusProductRef.current
|
|
1399
|
+
})
|
|
1400
|
+
);
|
|
1326
1401
|
cancelPolling();
|
|
1327
1402
|
inFlightRef.current = false;
|
|
1328
1403
|
conversationTokenRef.current = null;
|
|
@@ -1335,11 +1410,48 @@ function useExploreChat(options = {}) {
|
|
|
1335
1410
|
setPhase(catalogRef.current.enabled ? "ready" : "disabled");
|
|
1336
1411
|
if (typeof window !== "undefined") {
|
|
1337
1412
|
try {
|
|
1338
|
-
window.
|
|
1413
|
+
window.localStorage.removeItem(storageKey);
|
|
1339
1414
|
} catch {
|
|
1340
1415
|
}
|
|
1341
1416
|
}
|
|
1342
1417
|
}, [cancelPolling, storageKey]);
|
|
1418
|
+
const openConversation = useCallback(
|
|
1419
|
+
(token) => {
|
|
1420
|
+
const entry = readArchive(storageKey).find((e) => e.token === token);
|
|
1421
|
+
if (!entry) return;
|
|
1422
|
+
upsertArchive(storageKey, {
|
|
1423
|
+
token: conversationTokenRef.current,
|
|
1424
|
+
messages: messagesRef.current,
|
|
1425
|
+
focusProduct: focusProductRef.current
|
|
1426
|
+
});
|
|
1427
|
+
cancelPolling();
|
|
1428
|
+
inFlightRef.current = false;
|
|
1429
|
+
conversationTokenRef.current = entry.token;
|
|
1430
|
+
turnCountRef.current = entry.messages.filter(
|
|
1431
|
+
(m) => m.role === "USER"
|
|
1432
|
+
).length;
|
|
1433
|
+
setTurnCount(turnCountRef.current);
|
|
1434
|
+
setMessages(entry.messages);
|
|
1435
|
+
focusProductRef.current = entry.focusProduct;
|
|
1436
|
+
setFocusProductState(entry.focusProduct);
|
|
1437
|
+
setError(null);
|
|
1438
|
+
setPhase(catalogRef.current.enabled ? "ready" : "disabled");
|
|
1439
|
+
setArchive(readArchive(storageKey));
|
|
1440
|
+
},
|
|
1441
|
+
[cancelPolling, storageKey]
|
|
1442
|
+
);
|
|
1443
|
+
const deleteConversation = useCallback(
|
|
1444
|
+
(token) => {
|
|
1445
|
+
const next = readArchive(storageKey).filter((e) => e.token !== token);
|
|
1446
|
+
writeArchive(storageKey, next);
|
|
1447
|
+
setArchive(next);
|
|
1448
|
+
},
|
|
1449
|
+
[storageKey]
|
|
1450
|
+
);
|
|
1451
|
+
const clearHistory = useCallback(() => {
|
|
1452
|
+
writeArchive(storageKey, []);
|
|
1453
|
+
setArchive([]);
|
|
1454
|
+
}, [storageKey]);
|
|
1343
1455
|
const setFocusProduct = useCallback((slug) => {
|
|
1344
1456
|
focusProductRef.current = slug;
|
|
1345
1457
|
setFocusProductState(slug);
|
|
@@ -1368,7 +1480,11 @@ function useExploreChat(options = {}) {
|
|
|
1368
1480
|
stop,
|
|
1369
1481
|
retry,
|
|
1370
1482
|
reset,
|
|
1371
|
-
canSend
|
|
1483
|
+
canSend,
|
|
1484
|
+
history: archive,
|
|
1485
|
+
openConversation,
|
|
1486
|
+
deleteConversation,
|
|
1487
|
+
clearHistory
|
|
1372
1488
|
};
|
|
1373
1489
|
}
|
|
1374
1490
|
var optimisticCounter = 0;
|
|
@@ -1376,6 +1492,119 @@ function makeOptimisticId() {
|
|
|
1376
1492
|
optimisticCounter += 1;
|
|
1377
1493
|
return `boff-explore-local-${Date.now()}-${optimisticCounter}`;
|
|
1378
1494
|
}
|
|
1495
|
+
function getRecognitionCtor() {
|
|
1496
|
+
if (typeof window === "undefined") return null;
|
|
1497
|
+
const w = window;
|
|
1498
|
+
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
1499
|
+
}
|
|
1500
|
+
function describeError(code) {
|
|
1501
|
+
switch (code) {
|
|
1502
|
+
case "not-allowed":
|
|
1503
|
+
case "service-not-allowed":
|
|
1504
|
+
return "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
1505
|
+
case "no-speech":
|
|
1506
|
+
return "I didn't catch anything \u2014 try again a little closer to the mic.";
|
|
1507
|
+
case "audio-capture":
|
|
1508
|
+
return "No microphone was found.";
|
|
1509
|
+
case "network":
|
|
1510
|
+
return "Speech recognition needs a network connection.";
|
|
1511
|
+
case "aborted":
|
|
1512
|
+
return "";
|
|
1513
|
+
default:
|
|
1514
|
+
return "Dictation stopped unexpectedly. You can type instead.";
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function useSpeechInput({
|
|
1518
|
+
onFinalTranscript,
|
|
1519
|
+
lang,
|
|
1520
|
+
onError
|
|
1521
|
+
}) {
|
|
1522
|
+
const [supported] = useState(() => getRecognitionCtor() !== null);
|
|
1523
|
+
const [listening, setListening] = useState(false);
|
|
1524
|
+
const [interim, setInterim] = useState("");
|
|
1525
|
+
const [error, setError] = useState(null);
|
|
1526
|
+
const recognitionRef = useRef(null);
|
|
1527
|
+
const finalRef = useRef(onFinalTranscript);
|
|
1528
|
+
const errorRef = useRef(onError);
|
|
1529
|
+
finalRef.current = onFinalTranscript;
|
|
1530
|
+
errorRef.current = onError;
|
|
1531
|
+
const stop = useCallback(() => {
|
|
1532
|
+
const recognition = recognitionRef.current;
|
|
1533
|
+
if (!recognition) return;
|
|
1534
|
+
try {
|
|
1535
|
+
recognition.stop();
|
|
1536
|
+
} catch {
|
|
1537
|
+
}
|
|
1538
|
+
setListening(false);
|
|
1539
|
+
setInterim("");
|
|
1540
|
+
}, []);
|
|
1541
|
+
const start = useCallback(() => {
|
|
1542
|
+
const Ctor = getRecognitionCtor();
|
|
1543
|
+
if (!Ctor) return;
|
|
1544
|
+
if (recognitionRef.current) stop();
|
|
1545
|
+
const recognition = new Ctor();
|
|
1546
|
+
recognition.lang = lang ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
1547
|
+
recognition.continuous = true;
|
|
1548
|
+
recognition.interimResults = true;
|
|
1549
|
+
recognition.maxAlternatives = 1;
|
|
1550
|
+
recognition.onstart = () => {
|
|
1551
|
+
setError(null);
|
|
1552
|
+
setListening(true);
|
|
1553
|
+
};
|
|
1554
|
+
recognition.onresult = (event) => {
|
|
1555
|
+
let settled = "";
|
|
1556
|
+
let pending = "";
|
|
1557
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
1558
|
+
const result = event.results[i];
|
|
1559
|
+
if (!result) continue;
|
|
1560
|
+
const text = result[0]?.transcript ?? "";
|
|
1561
|
+
if (result.isFinal) settled += text;
|
|
1562
|
+
else pending += text;
|
|
1563
|
+
}
|
|
1564
|
+
setInterim(pending);
|
|
1565
|
+
if (settled.trim() !== "") finalRef.current(settled);
|
|
1566
|
+
};
|
|
1567
|
+
recognition.onerror = (event) => {
|
|
1568
|
+
const message = describeError(event.error);
|
|
1569
|
+
setListening(false);
|
|
1570
|
+
setInterim("");
|
|
1571
|
+
if (message !== "") {
|
|
1572
|
+
setError(message);
|
|
1573
|
+
errorRef.current?.(message);
|
|
1574
|
+
}
|
|
1575
|
+
};
|
|
1576
|
+
recognition.onend = () => {
|
|
1577
|
+
setListening(false);
|
|
1578
|
+
setInterim("");
|
|
1579
|
+
};
|
|
1580
|
+
recognitionRef.current = recognition;
|
|
1581
|
+
try {
|
|
1582
|
+
recognition.start();
|
|
1583
|
+
} catch {
|
|
1584
|
+
setListening(false);
|
|
1585
|
+
}
|
|
1586
|
+
}, [lang, stop]);
|
|
1587
|
+
const toggle = useCallback(() => {
|
|
1588
|
+
if (listening) stop();
|
|
1589
|
+
else start();
|
|
1590
|
+
}, [listening, start, stop]);
|
|
1591
|
+
useEffect(
|
|
1592
|
+
() => () => {
|
|
1593
|
+
const recognition = recognitionRef.current;
|
|
1594
|
+
if (!recognition) return;
|
|
1595
|
+
recognition.onresult = null;
|
|
1596
|
+
recognition.onerror = null;
|
|
1597
|
+
recognition.onend = null;
|
|
1598
|
+
recognition.onstart = null;
|
|
1599
|
+
try {
|
|
1600
|
+
recognition.abort();
|
|
1601
|
+
} catch {
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
[]
|
|
1605
|
+
);
|
|
1606
|
+
return { supported, listening, interim, error, start, stop, toggle };
|
|
1607
|
+
}
|
|
1379
1608
|
function canonicalFromLocation() {
|
|
1380
1609
|
if (typeof window === "undefined" || !window.location) return void 0;
|
|
1381
1610
|
const { origin, pathname } = window.location;
|
|
@@ -1928,10 +2157,29 @@ function ExplorePage({
|
|
|
1928
2157
|
stop,
|
|
1929
2158
|
retry,
|
|
1930
2159
|
reset,
|
|
1931
|
-
canSend
|
|
2160
|
+
canSend,
|
|
2161
|
+
history,
|
|
2162
|
+
openConversation,
|
|
2163
|
+
deleteConversation,
|
|
2164
|
+
clearHistory
|
|
1932
2165
|
} = useExploreChat({ productSlug, pageUrl, onSend, examplePrompts });
|
|
1933
2166
|
const [draft, setDraft] = useState("");
|
|
1934
2167
|
const textareaRef = useRef(null);
|
|
2168
|
+
const [historyOpen, setHistoryOpen] = useState(false);
|
|
2169
|
+
const speechStopRef = useRef(() => void 0);
|
|
2170
|
+
const stopDictation = useCallback(() => {
|
|
2171
|
+
speechStopRef.current();
|
|
2172
|
+
}, []);
|
|
2173
|
+
const speech = useSpeechInput({
|
|
2174
|
+
onFinalTranscript: (text) => {
|
|
2175
|
+
setDraft((current) => {
|
|
2176
|
+
const joined = current.trim() === "" ? text.trimStart() : `${current.trimEnd()} ${text.trim()}`;
|
|
2177
|
+
return joined;
|
|
2178
|
+
});
|
|
2179
|
+
textareaRef.current?.focus();
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
2182
|
+
speechStopRef.current = speech.stop;
|
|
1935
2183
|
const scrollRef = useRef(null);
|
|
1936
2184
|
const stickToBottomRef = useRef(true);
|
|
1937
2185
|
const composingRef = useRef(false);
|
|
@@ -1967,11 +2215,12 @@ function ExplorePage({
|
|
|
1967
2215
|
const submitDraft = useCallback(() => {
|
|
1968
2216
|
const text = draft.trim();
|
|
1969
2217
|
if (!text || overLimit || busy || !canSend) return;
|
|
2218
|
+
stopDictation();
|
|
1970
2219
|
setDraft("");
|
|
1971
2220
|
stickToBottomRef.current = true;
|
|
1972
2221
|
void send(text);
|
|
1973
2222
|
textareaRef.current?.focus();
|
|
1974
|
-
}, [busy, canSend, draft, overLimit, send]);
|
|
2223
|
+
}, [busy, canSend, draft, overLimit, send, stopDictation]);
|
|
1975
2224
|
const sendPrompt = useCallback(
|
|
1976
2225
|
(prompt) => {
|
|
1977
2226
|
if (!canSend || busy) return;
|
|
@@ -2044,8 +2293,96 @@ function ExplorePage({
|
|
|
2044
2293
|
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "New chat" })
|
|
2045
2294
|
]
|
|
2046
2295
|
}
|
|
2047
|
-
)
|
|
2296
|
+
),
|
|
2297
|
+
history.length > 0 ? /* @__PURE__ */ jsxs(
|
|
2298
|
+
Button,
|
|
2299
|
+
{
|
|
2300
|
+
"data-boff-explore": "history-toggle",
|
|
2301
|
+
type: "button",
|
|
2302
|
+
size: "sm",
|
|
2303
|
+
variant: "ghost",
|
|
2304
|
+
className: "shrink-0 text-muted-foreground",
|
|
2305
|
+
"aria-expanded": historyOpen,
|
|
2306
|
+
onClick: () => {
|
|
2307
|
+
setHistoryOpen((open) => !open);
|
|
2308
|
+
},
|
|
2309
|
+
children: [
|
|
2310
|
+
/* @__PURE__ */ jsx(History, { className: "h-3.5 w-3.5" }),
|
|
2311
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden sm:inline", children: [
|
|
2312
|
+
"History (",
|
|
2313
|
+
history.length,
|
|
2314
|
+
")"
|
|
2315
|
+
] })
|
|
2316
|
+
]
|
|
2317
|
+
}
|
|
2318
|
+
) : null
|
|
2048
2319
|
] }) : null,
|
|
2320
|
+
historyOpen && history.length > 0 ? /* @__PURE__ */ jsx(
|
|
2321
|
+
"div",
|
|
2322
|
+
{
|
|
2323
|
+
"data-boff-explore": "history-panel",
|
|
2324
|
+
className: "border-b border-border bg-muted/30 px-4 py-3",
|
|
2325
|
+
children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl", children: [
|
|
2326
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
2327
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Your recent conversations" }),
|
|
2328
|
+
/* @__PURE__ */ jsxs(
|
|
2329
|
+
Button,
|
|
2330
|
+
{
|
|
2331
|
+
"data-boff-explore": "history-clear",
|
|
2332
|
+
type: "button",
|
|
2333
|
+
size: "sm",
|
|
2334
|
+
variant: "ghost",
|
|
2335
|
+
className: "h-7 shrink-0 text-xs text-muted-foreground hover:text-destructive",
|
|
2336
|
+
onClick: () => {
|
|
2337
|
+
clearHistory();
|
|
2338
|
+
setHistoryOpen(false);
|
|
2339
|
+
},
|
|
2340
|
+
children: [
|
|
2341
|
+
/* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }),
|
|
2342
|
+
"Clear history"
|
|
2343
|
+
]
|
|
2344
|
+
}
|
|
2345
|
+
)
|
|
2346
|
+
] }),
|
|
2347
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1", children: history.map((entry) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1", children: [
|
|
2348
|
+
/* @__PURE__ */ jsxs(
|
|
2349
|
+
"button",
|
|
2350
|
+
{
|
|
2351
|
+
"data-boff-explore": "history-item",
|
|
2352
|
+
type: "button",
|
|
2353
|
+
className: "flex-1 truncate rounded-md px-2 py-1.5 text-left text-sm text-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
2354
|
+
onClick: () => {
|
|
2355
|
+
openConversation(entry.token);
|
|
2356
|
+
setHistoryOpen(false);
|
|
2357
|
+
},
|
|
2358
|
+
children: [
|
|
2359
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: entry.title }),
|
|
2360
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: [
|
|
2361
|
+
entry.messageCount,
|
|
2362
|
+
" message",
|
|
2363
|
+
entry.messageCount === 1 ? "" : "s"
|
|
2364
|
+
] })
|
|
2365
|
+
]
|
|
2366
|
+
}
|
|
2367
|
+
),
|
|
2368
|
+
/* @__PURE__ */ jsx(
|
|
2369
|
+
Button,
|
|
2370
|
+
{
|
|
2371
|
+
type: "button",
|
|
2372
|
+
size: "icon",
|
|
2373
|
+
variant: "ghost",
|
|
2374
|
+
className: "h-7 w-7 shrink-0 text-muted-foreground hover:text-destructive",
|
|
2375
|
+
"aria-label": `Delete conversation: ${entry.title}`,
|
|
2376
|
+
onClick: () => {
|
|
2377
|
+
deleteConversation(entry.token);
|
|
2378
|
+
},
|
|
2379
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
2380
|
+
}
|
|
2381
|
+
)
|
|
2382
|
+
] }, entry.token)) })
|
|
2383
|
+
] })
|
|
2384
|
+
}
|
|
2385
|
+
) : null,
|
|
2049
2386
|
/* @__PURE__ */ jsx(
|
|
2050
2387
|
"div",
|
|
2051
2388
|
{
|
|
@@ -2120,6 +2457,23 @@ function ExplorePage({
|
|
|
2120
2457
|
className: "max-h-[200px] min-h-[44px] flex-1 resize-none border-0 bg-transparent px-2 py-2.5 text-sm shadow-none focus-visible:ring-0 md:text-sm"
|
|
2121
2458
|
}
|
|
2122
2459
|
),
|
|
2460
|
+
speech.supported ? /* @__PURE__ */ jsx(
|
|
2461
|
+
Button,
|
|
2462
|
+
{
|
|
2463
|
+
"data-boff-explore": "mic",
|
|
2464
|
+
"data-listening": speech.listening ? "true" : "false",
|
|
2465
|
+
type: "button",
|
|
2466
|
+
size: "icon",
|
|
2467
|
+
variant: speech.listening ? "default" : "ghost",
|
|
2468
|
+
disabled: composerDisabled,
|
|
2469
|
+
"aria-label": speech.listening ? "Stop dictating" : "Dictate your question",
|
|
2470
|
+
"aria-pressed": speech.listening,
|
|
2471
|
+
title: speech.listening ? "Stop dictating" : "Dictate your question",
|
|
2472
|
+
onClick: speech.toggle,
|
|
2473
|
+
className: cn(speech.listening && "animate-pulse"),
|
|
2474
|
+
children: /* @__PURE__ */ jsx(Mic, { className: "h-4 w-4" })
|
|
2475
|
+
}
|
|
2476
|
+
) : null,
|
|
2123
2477
|
busy ? /* @__PURE__ */ jsx(
|
|
2124
2478
|
Button,
|
|
2125
2479
|
{
|
|
@@ -2145,6 +2499,29 @@ function ExplorePage({
|
|
|
2145
2499
|
]
|
|
2146
2500
|
}
|
|
2147
2501
|
),
|
|
2502
|
+
speech.listening || speech.interim !== "" ? /* @__PURE__ */ jsxs(
|
|
2503
|
+
"p",
|
|
2504
|
+
{
|
|
2505
|
+
"data-boff-explore": "dictation",
|
|
2506
|
+
className: "mt-2 flex items-center gap-2 text-xs text-muted-foreground",
|
|
2507
|
+
"aria-live": "polite",
|
|
2508
|
+
children: [
|
|
2509
|
+
/* @__PURE__ */ jsxs("span", { className: "relative flex h-2 w-2 shrink-0", children: [
|
|
2510
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" }),
|
|
2511
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
|
|
2512
|
+
] }),
|
|
2513
|
+
/* @__PURE__ */ jsx("span", { className: "italic", children: speech.interim !== "" ? speech.interim : "Listening\u2026" })
|
|
2514
|
+
]
|
|
2515
|
+
}
|
|
2516
|
+
) : null,
|
|
2517
|
+
speech.error !== null ? /* @__PURE__ */ jsx(
|
|
2518
|
+
"p",
|
|
2519
|
+
{
|
|
2520
|
+
"data-boff-explore": "dictation-error",
|
|
2521
|
+
className: "mt-2 text-xs text-destructive",
|
|
2522
|
+
children: speech.error
|
|
2523
|
+
}
|
|
2524
|
+
) : null,
|
|
2148
2525
|
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
2149
2526
|
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Enter to send \xB7 Shift + Enter for a new line" }),
|
|
2150
2527
|
remainingToday !== null ? /* @__PURE__ */ jsxs("span", { "data-boff-explore": "remaining", children: [
|
|
@@ -2168,39 +2545,48 @@ function ExplorePage({
|
|
|
2168
2545
|
}
|
|
2169
2546
|
)
|
|
2170
2547
|
] }),
|
|
2171
|
-
/* @__PURE__ */ jsxs(
|
|
2172
|
-
"
|
|
2173
|
-
|
|
2174
|
-
" ",
|
|
2175
|
-
"
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
"
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2548
|
+
/* @__PURE__ */ jsxs(
|
|
2549
|
+
"p",
|
|
2550
|
+
{
|
|
2551
|
+
"data-boff-explore": "disclaimer",
|
|
2552
|
+
className: "mt-2 text-xs leading-relaxed text-muted-foreground",
|
|
2553
|
+
children: [
|
|
2554
|
+
"Answers are generated from our public documentation and can be imperfect \u2014 check anything important against the linked sources.",
|
|
2555
|
+
" ",
|
|
2556
|
+
"Conversations are saved in this browser so you can come back to them, and are also stored on our servers to help us improve these answers. Please don't share personal or confidential information.",
|
|
2557
|
+
captchaOn ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2558
|
+
" ",
|
|
2559
|
+
"This site is protected by reCAPTCHA; the Google",
|
|
2560
|
+
" ",
|
|
2561
|
+
/* @__PURE__ */ jsx(
|
|
2562
|
+
"a",
|
|
2563
|
+
{
|
|
2564
|
+
href: "https://policies.google.com/privacy",
|
|
2565
|
+
target: "_blank",
|
|
2566
|
+
rel: "noopener noreferrer",
|
|
2567
|
+
className: "underline underline-offset-2 hover:text-foreground",
|
|
2568
|
+
children: "Privacy Policy"
|
|
2569
|
+
}
|
|
2570
|
+
),
|
|
2571
|
+
" ",
|
|
2572
|
+
"and",
|
|
2573
|
+
" ",
|
|
2574
|
+
/* @__PURE__ */ jsx(
|
|
2575
|
+
"a",
|
|
2576
|
+
{
|
|
2577
|
+
href: "https://policies.google.com/terms",
|
|
2578
|
+
target: "_blank",
|
|
2579
|
+
rel: "noopener noreferrer",
|
|
2580
|
+
className: "underline underline-offset-2 hover:text-foreground",
|
|
2581
|
+
children: "Terms of Service"
|
|
2582
|
+
}
|
|
2583
|
+
),
|
|
2584
|
+
" ",
|
|
2585
|
+
"apply."
|
|
2586
|
+
] }) : null
|
|
2587
|
+
]
|
|
2588
|
+
}
|
|
2589
|
+
)
|
|
2204
2590
|
]
|
|
2205
2591
|
}
|
|
2206
2592
|
) })
|