@burdenoff/website-sdk 2026.828.4 → 2026.828.6
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.d.mts +11 -1
- package/dist/components/explore.d.ts +11 -1
- package/dist/components/explore.js +561 -57
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +563 -60
- 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 +298 -5
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +298 -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 +569 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +570 -65
- 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,182 @@ 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
|
+
var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
1501
|
+
async function ensureMicrophoneAccess() {
|
|
1502
|
+
const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
|
|
1503
|
+
if (!media?.getUserMedia) return { ok: true };
|
|
1504
|
+
try {
|
|
1505
|
+
const status = await navigator.permissions?.query({
|
|
1506
|
+
name: "microphone"
|
|
1507
|
+
});
|
|
1508
|
+
if (status?.state === "granted") return { ok: true };
|
|
1509
|
+
if (status?.state === "denied")
|
|
1510
|
+
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
1511
|
+
} catch {
|
|
1512
|
+
}
|
|
1513
|
+
try {
|
|
1514
|
+
const stream = await media.getUserMedia({ audio: true });
|
|
1515
|
+
for (const track of stream.getTracks()) track.stop();
|
|
1516
|
+
return { ok: true };
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
1519
|
+
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
1520
|
+
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
1521
|
+
}
|
|
1522
|
+
if (name === "NotFoundError" || name === "DevicesNotFoundError") {
|
|
1523
|
+
return { ok: false, message: "No microphone was found." };
|
|
1524
|
+
}
|
|
1525
|
+
return {
|
|
1526
|
+
ok: false,
|
|
1527
|
+
message: "Dictation could not start. You can type instead."
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
function describeError(code) {
|
|
1532
|
+
switch (code) {
|
|
1533
|
+
case "not-allowed":
|
|
1534
|
+
case "service-not-allowed":
|
|
1535
|
+
return MIC_DENIED_MESSAGE;
|
|
1536
|
+
case "no-speech":
|
|
1537
|
+
return "I didn't catch anything \u2014 try again a little closer to the mic.";
|
|
1538
|
+
case "audio-capture":
|
|
1539
|
+
return "No microphone was found.";
|
|
1540
|
+
case "network":
|
|
1541
|
+
return "Speech recognition needs a network connection.";
|
|
1542
|
+
case "aborted":
|
|
1543
|
+
return "";
|
|
1544
|
+
default:
|
|
1545
|
+
return "Dictation stopped unexpectedly. You can type instead.";
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
function useSpeechInput({
|
|
1549
|
+
onFinalTranscript,
|
|
1550
|
+
lang,
|
|
1551
|
+
onError
|
|
1552
|
+
}) {
|
|
1553
|
+
const [supported] = useState(() => getRecognitionCtor() !== null);
|
|
1554
|
+
const [listening, setListening] = useState(false);
|
|
1555
|
+
const [interim, setInterim] = useState("");
|
|
1556
|
+
const [error, setError] = useState(null);
|
|
1557
|
+
const recognitionRef = useRef(null);
|
|
1558
|
+
const startTokenRef = useRef(0);
|
|
1559
|
+
const beginRecognitionRef = useRef(null);
|
|
1560
|
+
const beginRecognition = useCallback(
|
|
1561
|
+
(Ctor, token) => {
|
|
1562
|
+
beginRecognitionRef.current?.(Ctor, token);
|
|
1563
|
+
},
|
|
1564
|
+
[]
|
|
1565
|
+
);
|
|
1566
|
+
const finalRef = useRef(onFinalTranscript);
|
|
1567
|
+
const errorRef = useRef(onError);
|
|
1568
|
+
finalRef.current = onFinalTranscript;
|
|
1569
|
+
errorRef.current = onError;
|
|
1570
|
+
const stop = useCallback(() => {
|
|
1571
|
+
startTokenRef.current += 1;
|
|
1572
|
+
setListening(false);
|
|
1573
|
+
setInterim("");
|
|
1574
|
+
const recognition = recognitionRef.current;
|
|
1575
|
+
if (!recognition) return;
|
|
1576
|
+
try {
|
|
1577
|
+
recognition.stop();
|
|
1578
|
+
} catch {
|
|
1579
|
+
}
|
|
1580
|
+
setListening(false);
|
|
1581
|
+
setInterim("");
|
|
1582
|
+
}, []);
|
|
1583
|
+
const start = useCallback(() => {
|
|
1584
|
+
const Ctor = getRecognitionCtor();
|
|
1585
|
+
if (!Ctor) return;
|
|
1586
|
+
if (recognitionRef.current) stop();
|
|
1587
|
+
setError(null);
|
|
1588
|
+
setListening(true);
|
|
1589
|
+
startTokenRef.current += 1;
|
|
1590
|
+
const token = startTokenRef.current;
|
|
1591
|
+
void ensureMicrophoneAccess().then((access) => {
|
|
1592
|
+
if (token !== startTokenRef.current) return;
|
|
1593
|
+
if (!access.ok) {
|
|
1594
|
+
setListening(false);
|
|
1595
|
+
setError(access.message);
|
|
1596
|
+
errorRef.current?.(access.message);
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
beginRecognition(Ctor, token);
|
|
1600
|
+
});
|
|
1601
|
+
}, [beginRecognition, stop]);
|
|
1602
|
+
const beginRecognitionImpl = useCallback(
|
|
1603
|
+
(Ctor, token) => {
|
|
1604
|
+
const recognition = new Ctor();
|
|
1605
|
+
recognition.lang = lang ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
1606
|
+
recognition.continuous = true;
|
|
1607
|
+
recognition.interimResults = true;
|
|
1608
|
+
recognition.maxAlternatives = 1;
|
|
1609
|
+
recognition.onstart = () => {
|
|
1610
|
+
if (token !== startTokenRef.current) return;
|
|
1611
|
+
setError(null);
|
|
1612
|
+
setListening(true);
|
|
1613
|
+
};
|
|
1614
|
+
recognition.onresult = (event) => {
|
|
1615
|
+
let settled = "";
|
|
1616
|
+
let pending = "";
|
|
1617
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
1618
|
+
const result = event.results[i];
|
|
1619
|
+
if (!result) continue;
|
|
1620
|
+
const text = result[0]?.transcript ?? "";
|
|
1621
|
+
if (result.isFinal) settled += text;
|
|
1622
|
+
else pending += text;
|
|
1623
|
+
}
|
|
1624
|
+
setInterim(pending);
|
|
1625
|
+
if (settled.trim() !== "") finalRef.current(settled);
|
|
1626
|
+
};
|
|
1627
|
+
recognition.onerror = (event) => {
|
|
1628
|
+
const message = describeError(event.error);
|
|
1629
|
+
setListening(false);
|
|
1630
|
+
setInterim("");
|
|
1631
|
+
if (message !== "") {
|
|
1632
|
+
setError(message);
|
|
1633
|
+
errorRef.current?.(message);
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
recognition.onend = () => {
|
|
1637
|
+
setListening(false);
|
|
1638
|
+
setInterim("");
|
|
1639
|
+
};
|
|
1640
|
+
recognitionRef.current = recognition;
|
|
1641
|
+
try {
|
|
1642
|
+
recognition.start();
|
|
1643
|
+
} catch {
|
|
1644
|
+
setListening(false);
|
|
1645
|
+
}
|
|
1646
|
+
},
|
|
1647
|
+
[lang]
|
|
1648
|
+
);
|
|
1649
|
+
beginRecognitionRef.current = beginRecognitionImpl;
|
|
1650
|
+
const toggle = useCallback(() => {
|
|
1651
|
+
if (listening) stop();
|
|
1652
|
+
else start();
|
|
1653
|
+
}, [listening, start, stop]);
|
|
1654
|
+
useEffect(
|
|
1655
|
+
() => () => {
|
|
1656
|
+
const recognition = recognitionRef.current;
|
|
1657
|
+
if (!recognition) return;
|
|
1658
|
+
recognition.onresult = null;
|
|
1659
|
+
recognition.onerror = null;
|
|
1660
|
+
recognition.onend = null;
|
|
1661
|
+
recognition.onstart = null;
|
|
1662
|
+
try {
|
|
1663
|
+
recognition.abort();
|
|
1664
|
+
} catch {
|
|
1665
|
+
}
|
|
1666
|
+
},
|
|
1667
|
+
[]
|
|
1668
|
+
);
|
|
1669
|
+
return { supported, listening, interim, error, start, stop, toggle };
|
|
1670
|
+
}
|
|
1379
1671
|
function canonicalFromLocation() {
|
|
1380
1672
|
if (typeof window === "undefined" || !window.location) return void 0;
|
|
1381
1673
|
const { origin, pathname } = window.location;
|
|
@@ -1595,10 +1887,33 @@ function initialOf(value, fallback) {
|
|
|
1595
1887
|
const source = (value || fallback).trim();
|
|
1596
1888
|
return source ? source.slice(0, 1).toUpperCase() : "?";
|
|
1597
1889
|
}
|
|
1890
|
+
function logoUrlOf(url) {
|
|
1891
|
+
try {
|
|
1892
|
+
const { origin, protocol } = new URL(url);
|
|
1893
|
+
if (protocol !== "https:" && protocol !== "http:") return null;
|
|
1894
|
+
return `${origin}/favicon.svg`;
|
|
1895
|
+
} catch {
|
|
1896
|
+
return null;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1598
1899
|
function ReferenceCard({ reference }) {
|
|
1599
1900
|
const [imageFailed, setImageFailed] = useState(false);
|
|
1901
|
+
const [logoFailed, setLogoFailed] = useState(false);
|
|
1600
1902
|
const host = hostnameOf(reference.url);
|
|
1903
|
+
const logoUrl = logoUrlOf(reference.url);
|
|
1601
1904
|
const showImage = Boolean(reference.imageUrl) && !imageFailed;
|
|
1905
|
+
const showLogo = Boolean(logoUrl) && !logoFailed;
|
|
1906
|
+
const letterPlate = /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center bg-gradient-to-br from-primary/25 via-primary/5 to-secondary", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-primary/70 sm:text-2xl", children: initialOf(reference.product, reference.title || host) }) });
|
|
1907
|
+
const logoImg = showLogo ? /* @__PURE__ */ jsx(
|
|
1908
|
+
"img",
|
|
1909
|
+
{
|
|
1910
|
+
src: logoUrl ?? "",
|
|
1911
|
+
alt: "",
|
|
1912
|
+
loading: "lazy",
|
|
1913
|
+
onError: () => setLogoFailed(true),
|
|
1914
|
+
className: "h-full w-full object-contain p-1.5 sm:p-0"
|
|
1915
|
+
}
|
|
1916
|
+
) : letterPlate;
|
|
1602
1917
|
return /* @__PURE__ */ jsxs(
|
|
1603
1918
|
"a",
|
|
1604
1919
|
{
|
|
@@ -1606,9 +1921,10 @@ function ReferenceCard({ reference }) {
|
|
|
1606
1921
|
href: reference.url,
|
|
1607
1922
|
target: "_blank",
|
|
1608
1923
|
rel: "noopener noreferrer",
|
|
1609
|
-
className: "group flex flex-
|
|
1924
|
+
className: "group flex flex-row items-center gap-3 overflow-hidden rounded-xl border border-border bg-card p-2 transition-colors hover:border-primary/40 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:flex-col sm:items-stretch sm:gap-0 sm:p-0",
|
|
1610
1925
|
children: [
|
|
1611
|
-
/* @__PURE__ */ jsx("div", { className: "relative
|
|
1926
|
+
/* @__PURE__ */ jsx("div", { className: "relative h-11 w-11 shrink-0 overflow-hidden rounded-lg bg-muted sm:hidden", children: logoImg }),
|
|
1927
|
+
/* @__PURE__ */ jsx("div", { className: "relative hidden aspect-[16/9] w-full overflow-hidden bg-muted sm:block", children: showImage ? /* @__PURE__ */ jsx(
|
|
1612
1928
|
"img",
|
|
1613
1929
|
{
|
|
1614
1930
|
src: reference.imageUrl ?? "",
|
|
@@ -1617,17 +1933,22 @@ function ReferenceCard({ reference }) {
|
|
|
1617
1933
|
onError: () => setImageFailed(true),
|
|
1618
1934
|
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
1619
1935
|
}
|
|
1620
|
-
) : (
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1936
|
+
) : showLogo ? /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center bg-gradient-to-br from-primary/15 via-transparent to-secondary/60 p-6", children: /* @__PURE__ */ jsx(
|
|
1937
|
+
"img",
|
|
1938
|
+
{
|
|
1939
|
+
src: logoUrl ?? "",
|
|
1940
|
+
alt: "",
|
|
1941
|
+
loading: "lazy",
|
|
1942
|
+
onError: () => setLogoFailed(true),
|
|
1943
|
+
className: "max-h-full max-w-full object-contain"
|
|
1944
|
+
}
|
|
1945
|
+
) }) : letterPlate }),
|
|
1946
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 sm:gap-1.5 sm:p-3", children: [
|
|
1626
1947
|
/* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: reference.title || host || reference.url }),
|
|
1627
|
-
reference.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground", children: reference.description }) : null,
|
|
1628
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1629
|
-
reference.product ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-secondary-foreground", children: reference.product }) : null,
|
|
1630
|
-
/* @__PURE__ */ jsxs("span", { className: "
|
|
1948
|
+
reference.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground max-sm:hidden", children: reference.description }) : null,
|
|
1949
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 sm:mt-auto sm:pt-2", children: [
|
|
1950
|
+
reference.product ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-secondary-foreground max-sm:hidden", children: reference.product }) : null,
|
|
1951
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground sm:ml-auto", children: [
|
|
1631
1952
|
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3 shrink-0" }),
|
|
1632
1953
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: host })
|
|
1633
1954
|
] })
|
|
@@ -1713,7 +2034,8 @@ function UserBubble({ message }) {
|
|
|
1713
2034
|
function AssistantBubble({
|
|
1714
2035
|
message,
|
|
1715
2036
|
activity,
|
|
1716
|
-
onNavigate
|
|
2037
|
+
onNavigate,
|
|
2038
|
+
anchorRef
|
|
1717
2039
|
}) {
|
|
1718
2040
|
const streaming = message.status === "PENDING" || message.status === "RUNNING";
|
|
1719
2041
|
const body = message.content || message.partialContent || "";
|
|
@@ -1721,9 +2043,10 @@ function AssistantBubble({
|
|
|
1721
2043
|
return /* @__PURE__ */ jsxs(
|
|
1722
2044
|
"div",
|
|
1723
2045
|
{
|
|
2046
|
+
ref: anchorRef,
|
|
1724
2047
|
"data-boff-explore": "assistant",
|
|
1725
2048
|
"data-status": message.status,
|
|
1726
|
-
className: "flex gap-3",
|
|
2049
|
+
className: "flex scroll-mt-4 gap-3",
|
|
1727
2050
|
children: [
|
|
1728
2051
|
/* @__PURE__ */ jsx(
|
|
1729
2052
|
"span",
|
|
@@ -1928,11 +2251,32 @@ function ExplorePage({
|
|
|
1928
2251
|
stop,
|
|
1929
2252
|
retry,
|
|
1930
2253
|
reset,
|
|
1931
|
-
canSend
|
|
2254
|
+
canSend,
|
|
2255
|
+
history,
|
|
2256
|
+
openConversation,
|
|
2257
|
+
deleteConversation,
|
|
2258
|
+
clearHistory
|
|
1932
2259
|
} = useExploreChat({ productSlug, pageUrl, onSend, examplePrompts });
|
|
1933
2260
|
const [draft, setDraft] = useState("");
|
|
1934
2261
|
const textareaRef = useRef(null);
|
|
2262
|
+
const [historyOpen, setHistoryOpen] = useState(false);
|
|
2263
|
+
const speechStopRef = useRef(() => void 0);
|
|
2264
|
+
const stopDictation = useCallback(() => {
|
|
2265
|
+
speechStopRef.current();
|
|
2266
|
+
}, []);
|
|
2267
|
+
const speech = useSpeechInput({
|
|
2268
|
+
onFinalTranscript: (text) => {
|
|
2269
|
+
setDraft((current) => {
|
|
2270
|
+
const joined = current.trim() === "" ? text.trimStart() : `${current.trimEnd()} ${text.trim()}`;
|
|
2271
|
+
return joined;
|
|
2272
|
+
});
|
|
2273
|
+
textareaRef.current?.focus();
|
|
2274
|
+
}
|
|
2275
|
+
});
|
|
2276
|
+
speechStopRef.current = speech.stop;
|
|
1935
2277
|
const scrollRef = useRef(null);
|
|
2278
|
+
const latestAssistantRef = useRef(null);
|
|
2279
|
+
const alignedForRef = useRef(null);
|
|
1936
2280
|
const stickToBottomRef = useRef(true);
|
|
1937
2281
|
const composingRef = useRef(false);
|
|
1938
2282
|
const busy = phase === "sending" || phase === "streaming";
|
|
@@ -1958,20 +2302,41 @@ function ExplorePage({
|
|
|
1958
2302
|
if (!el) return;
|
|
1959
2303
|
stickToBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
|
|
1960
2304
|
}, []);
|
|
2305
|
+
const latestAssistantId = useMemo(() => {
|
|
2306
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
2307
|
+
const message = messages[i];
|
|
2308
|
+
if (message && message.role === "ASSISTANT") return message.id;
|
|
2309
|
+
}
|
|
2310
|
+
return null;
|
|
2311
|
+
}, [messages]);
|
|
2312
|
+
useEffect(() => {
|
|
2313
|
+
const el = scrollRef.current;
|
|
2314
|
+
const anchor = latestAssistantRef.current;
|
|
2315
|
+
if (!el || !anchor || !latestAssistantId) return;
|
|
2316
|
+
if (alignedForRef.current === latestAssistantId) return;
|
|
2317
|
+
if (!stickToBottomRef.current) return;
|
|
2318
|
+
if (anchor.offsetHeight === 0) return;
|
|
2319
|
+
alignedForRef.current = latestAssistantId;
|
|
2320
|
+
const delta = anchor.getBoundingClientRect().top - el.getBoundingClientRect().top;
|
|
2321
|
+
el.scrollTop = Math.max(0, el.scrollTop + delta - 12);
|
|
2322
|
+
}, [latestAssistantId, messages]);
|
|
1961
2323
|
useEffect(() => {
|
|
1962
2324
|
if (!stickToBottomRef.current) return;
|
|
2325
|
+
if (latestAssistantId && alignedForRef.current === latestAssistantId)
|
|
2326
|
+
return;
|
|
1963
2327
|
const el = scrollRef.current;
|
|
1964
2328
|
if (!el) return;
|
|
1965
2329
|
el.scrollTop = el.scrollHeight;
|
|
1966
|
-
}, [
|
|
2330
|
+
}, [activity, latestAssistantId, messages]);
|
|
1967
2331
|
const submitDraft = useCallback(() => {
|
|
1968
2332
|
const text = draft.trim();
|
|
1969
2333
|
if (!text || overLimit || busy || !canSend) return;
|
|
2334
|
+
stopDictation();
|
|
1970
2335
|
setDraft("");
|
|
1971
2336
|
stickToBottomRef.current = true;
|
|
1972
2337
|
void send(text);
|
|
1973
2338
|
textareaRef.current?.focus();
|
|
1974
|
-
}, [busy, canSend, draft, overLimit, send]);
|
|
2339
|
+
}, [busy, canSend, draft, overLimit, send, stopDictation]);
|
|
1975
2340
|
const sendPrompt = useCallback(
|
|
1976
2341
|
(prompt) => {
|
|
1977
2342
|
if (!canSend || busy) return;
|
|
@@ -2044,8 +2409,96 @@ function ExplorePage({
|
|
|
2044
2409
|
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "New chat" })
|
|
2045
2410
|
]
|
|
2046
2411
|
}
|
|
2047
|
-
)
|
|
2412
|
+
),
|
|
2413
|
+
history.length > 0 ? /* @__PURE__ */ jsxs(
|
|
2414
|
+
Button,
|
|
2415
|
+
{
|
|
2416
|
+
"data-boff-explore": "history-toggle",
|
|
2417
|
+
type: "button",
|
|
2418
|
+
size: "sm",
|
|
2419
|
+
variant: "ghost",
|
|
2420
|
+
className: "shrink-0 text-muted-foreground",
|
|
2421
|
+
"aria-expanded": historyOpen,
|
|
2422
|
+
onClick: () => {
|
|
2423
|
+
setHistoryOpen((open) => !open);
|
|
2424
|
+
},
|
|
2425
|
+
children: [
|
|
2426
|
+
/* @__PURE__ */ jsx(History, { className: "h-3.5 w-3.5" }),
|
|
2427
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden sm:inline", children: [
|
|
2428
|
+
"History (",
|
|
2429
|
+
history.length,
|
|
2430
|
+
")"
|
|
2431
|
+
] })
|
|
2432
|
+
]
|
|
2433
|
+
}
|
|
2434
|
+
) : null
|
|
2048
2435
|
] }) : null,
|
|
2436
|
+
historyOpen && history.length > 0 ? /* @__PURE__ */ jsx(
|
|
2437
|
+
"div",
|
|
2438
|
+
{
|
|
2439
|
+
"data-boff-explore": "history-panel",
|
|
2440
|
+
className: "border-b border-border bg-muted/30 px-4 py-3",
|
|
2441
|
+
children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl", children: [
|
|
2442
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
2443
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Your recent conversations" }),
|
|
2444
|
+
/* @__PURE__ */ jsxs(
|
|
2445
|
+
Button,
|
|
2446
|
+
{
|
|
2447
|
+
"data-boff-explore": "history-clear",
|
|
2448
|
+
type: "button",
|
|
2449
|
+
size: "sm",
|
|
2450
|
+
variant: "ghost",
|
|
2451
|
+
className: "h-7 shrink-0 text-xs text-muted-foreground hover:text-destructive",
|
|
2452
|
+
onClick: () => {
|
|
2453
|
+
clearHistory();
|
|
2454
|
+
setHistoryOpen(false);
|
|
2455
|
+
},
|
|
2456
|
+
children: [
|
|
2457
|
+
/* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }),
|
|
2458
|
+
"Clear history"
|
|
2459
|
+
]
|
|
2460
|
+
}
|
|
2461
|
+
)
|
|
2462
|
+
] }),
|
|
2463
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1", children: history.map((entry) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1", children: [
|
|
2464
|
+
/* @__PURE__ */ jsxs(
|
|
2465
|
+
"button",
|
|
2466
|
+
{
|
|
2467
|
+
"data-boff-explore": "history-item",
|
|
2468
|
+
type: "button",
|
|
2469
|
+
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",
|
|
2470
|
+
onClick: () => {
|
|
2471
|
+
openConversation(entry.token);
|
|
2472
|
+
setHistoryOpen(false);
|
|
2473
|
+
},
|
|
2474
|
+
children: [
|
|
2475
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: entry.title }),
|
|
2476
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: [
|
|
2477
|
+
entry.messageCount,
|
|
2478
|
+
" message",
|
|
2479
|
+
entry.messageCount === 1 ? "" : "s"
|
|
2480
|
+
] })
|
|
2481
|
+
]
|
|
2482
|
+
}
|
|
2483
|
+
),
|
|
2484
|
+
/* @__PURE__ */ jsx(
|
|
2485
|
+
Button,
|
|
2486
|
+
{
|
|
2487
|
+
type: "button",
|
|
2488
|
+
size: "icon",
|
|
2489
|
+
variant: "ghost",
|
|
2490
|
+
className: "h-7 w-7 shrink-0 text-muted-foreground hover:text-destructive",
|
|
2491
|
+
"aria-label": `Delete conversation: ${entry.title}`,
|
|
2492
|
+
onClick: () => {
|
|
2493
|
+
deleteConversation(entry.token);
|
|
2494
|
+
},
|
|
2495
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
2496
|
+
}
|
|
2497
|
+
)
|
|
2498
|
+
] }, entry.token)) })
|
|
2499
|
+
] })
|
|
2500
|
+
}
|
|
2501
|
+
) : null,
|
|
2049
2502
|
/* @__PURE__ */ jsx(
|
|
2050
2503
|
"div",
|
|
2051
2504
|
{
|
|
@@ -2074,7 +2527,8 @@ function ExplorePage({
|
|
|
2074
2527
|
{
|
|
2075
2528
|
message,
|
|
2076
2529
|
activity,
|
|
2077
|
-
onNavigate
|
|
2530
|
+
onNavigate,
|
|
2531
|
+
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0
|
|
2078
2532
|
},
|
|
2079
2533
|
message.id
|
|
2080
2534
|
)
|
|
@@ -2120,6 +2574,23 @@ function ExplorePage({
|
|
|
2120
2574
|
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
2575
|
}
|
|
2122
2576
|
),
|
|
2577
|
+
speech.supported ? /* @__PURE__ */ jsx(
|
|
2578
|
+
Button,
|
|
2579
|
+
{
|
|
2580
|
+
"data-boff-explore": "mic",
|
|
2581
|
+
"data-listening": speech.listening ? "true" : "false",
|
|
2582
|
+
type: "button",
|
|
2583
|
+
size: "icon",
|
|
2584
|
+
variant: speech.listening ? "default" : "ghost",
|
|
2585
|
+
disabled: composerDisabled,
|
|
2586
|
+
"aria-label": speech.listening ? "Stop dictating" : "Dictate your question",
|
|
2587
|
+
"aria-pressed": speech.listening,
|
|
2588
|
+
title: speech.listening ? "Stop dictating" : "Dictate your question",
|
|
2589
|
+
onClick: speech.toggle,
|
|
2590
|
+
className: cn(speech.listening && "animate-pulse"),
|
|
2591
|
+
children: /* @__PURE__ */ jsx(Mic, { className: "h-4 w-4" })
|
|
2592
|
+
}
|
|
2593
|
+
) : null,
|
|
2123
2594
|
busy ? /* @__PURE__ */ jsx(
|
|
2124
2595
|
Button,
|
|
2125
2596
|
{
|
|
@@ -2145,6 +2616,29 @@ function ExplorePage({
|
|
|
2145
2616
|
]
|
|
2146
2617
|
}
|
|
2147
2618
|
),
|
|
2619
|
+
speech.listening || speech.interim !== "" ? /* @__PURE__ */ jsxs(
|
|
2620
|
+
"p",
|
|
2621
|
+
{
|
|
2622
|
+
"data-boff-explore": "dictation",
|
|
2623
|
+
className: "mt-2 flex items-center gap-2 text-xs text-muted-foreground",
|
|
2624
|
+
"aria-live": "polite",
|
|
2625
|
+
children: [
|
|
2626
|
+
/* @__PURE__ */ jsxs("span", { className: "relative flex h-2 w-2 shrink-0", children: [
|
|
2627
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" }),
|
|
2628
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
|
|
2629
|
+
] }),
|
|
2630
|
+
/* @__PURE__ */ jsx("span", { className: "italic", children: speech.interim !== "" ? speech.interim : "Listening\u2026" })
|
|
2631
|
+
]
|
|
2632
|
+
}
|
|
2633
|
+
) : null,
|
|
2634
|
+
speech.error !== null ? /* @__PURE__ */ jsx(
|
|
2635
|
+
"p",
|
|
2636
|
+
{
|
|
2637
|
+
"data-boff-explore": "dictation-error",
|
|
2638
|
+
className: "mt-2 text-xs text-destructive",
|
|
2639
|
+
children: speech.error
|
|
2640
|
+
}
|
|
2641
|
+
) : null,
|
|
2148
2642
|
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
2149
2643
|
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Enter to send \xB7 Shift + Enter for a new line" }),
|
|
2150
2644
|
remainingToday !== null ? /* @__PURE__ */ jsxs("span", { "data-boff-explore": "remaining", children: [
|
|
@@ -2168,39 +2662,48 @@ function ExplorePage({
|
|
|
2168
2662
|
}
|
|
2169
2663
|
)
|
|
2170
2664
|
] }),
|
|
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
|
-
|
|
2665
|
+
/* @__PURE__ */ jsxs(
|
|
2666
|
+
"p",
|
|
2667
|
+
{
|
|
2668
|
+
"data-boff-explore": "disclaimer",
|
|
2669
|
+
className: "mt-2 text-xs leading-relaxed text-muted-foreground",
|
|
2670
|
+
children: [
|
|
2671
|
+
"Answers are generated from our public documentation and can be imperfect \u2014 check anything important against the linked sources.",
|
|
2672
|
+
" ",
|
|
2673
|
+
"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.",
|
|
2674
|
+
captchaOn ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2675
|
+
" ",
|
|
2676
|
+
"This site is protected by reCAPTCHA; the Google",
|
|
2677
|
+
" ",
|
|
2678
|
+
/* @__PURE__ */ jsx(
|
|
2679
|
+
"a",
|
|
2680
|
+
{
|
|
2681
|
+
href: "https://policies.google.com/privacy",
|
|
2682
|
+
target: "_blank",
|
|
2683
|
+
rel: "noopener noreferrer",
|
|
2684
|
+
className: "underline underline-offset-2 hover:text-foreground",
|
|
2685
|
+
children: "Privacy Policy"
|
|
2686
|
+
}
|
|
2687
|
+
),
|
|
2688
|
+
" ",
|
|
2689
|
+
"and",
|
|
2690
|
+
" ",
|
|
2691
|
+
/* @__PURE__ */ jsx(
|
|
2692
|
+
"a",
|
|
2693
|
+
{
|
|
2694
|
+
href: "https://policies.google.com/terms",
|
|
2695
|
+
target: "_blank",
|
|
2696
|
+
rel: "noopener noreferrer",
|
|
2697
|
+
className: "underline underline-offset-2 hover:text-foreground",
|
|
2698
|
+
children: "Terms of Service"
|
|
2699
|
+
}
|
|
2700
|
+
),
|
|
2701
|
+
" ",
|
|
2702
|
+
"apply."
|
|
2703
|
+
] }) : null
|
|
2704
|
+
]
|
|
2705
|
+
}
|
|
2706
|
+
)
|
|
2204
2707
|
]
|
|
2205
2708
|
}
|
|
2206
2709
|
) })
|
|
@@ -2209,6 +2712,6 @@ function ExplorePage({
|
|
|
2209
2712
|
);
|
|
2210
2713
|
}
|
|
2211
2714
|
|
|
2212
|
-
export { ExplorePage };
|
|
2715
|
+
export { ExplorePage, logoUrlOf };
|
|
2213
2716
|
//# sourceMappingURL=explore.mjs.map
|
|
2214
2717
|
//# sourceMappingURL=explore.mjs.map
|