@burdenoff/website-sdk 2026.828.7 → 2026.829.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/explore.js +567 -200
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +569 -202
- package/dist/components/explore.mjs.map +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +139 -69
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +139 -69
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +567 -200
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +567 -200
- package/dist/index.mjs.map +1 -1
- package/dist/{use-explore-chat-DhvZhH8f.d.mts → use-explore-chat-8eL7WdLc.d.mts} +16 -2
- package/dist/{use-explore-chat-DhvZhH8f.d.ts → use-explore-chat-8eL7WdLc.d.ts} +16 -2
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
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';
|
|
3
|
+
import { Sparkles, RotateCcw, History, Trash2, Mic, Square, ArrowUp, BookOpenText, Layers, Link2, TriangleAlert, ChevronLeft, ChevronRight, 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';
|
|
7
|
-
import { jsx, jsxs
|
|
7
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
8
8
|
import { Helmet } from 'react-helmet-async';
|
|
9
9
|
import { Slot } from '@radix-ui/react-slot';
|
|
10
10
|
import { cva } from 'class-variance-authority';
|
|
@@ -463,6 +463,7 @@ var EXPLORE_MESSAGE_FIELDS = (
|
|
|
463
463
|
description
|
|
464
464
|
product
|
|
465
465
|
imageUrl
|
|
466
|
+
index
|
|
466
467
|
}
|
|
467
468
|
ctas {
|
|
468
469
|
kind
|
|
@@ -470,6 +471,8 @@ var EXPLORE_MESSAGE_FIELDS = (
|
|
|
470
471
|
url
|
|
471
472
|
product
|
|
472
473
|
}
|
|
474
|
+
followUps
|
|
475
|
+
answered
|
|
473
476
|
errorCode
|
|
474
477
|
createdAt
|
|
475
478
|
completedAt
|
|
@@ -535,6 +538,16 @@ var SEND_EXPLORE_MESSAGE_MUTATION = (
|
|
|
535
538
|
}
|
|
536
539
|
`
|
|
537
540
|
);
|
|
541
|
+
var RECORD_EXPLORE_CLICK_MUTATION = (
|
|
542
|
+
/* GraphQL */
|
|
543
|
+
`
|
|
544
|
+
mutation RecordExploreClick($input: RecordExploreClickInput!) {
|
|
545
|
+
recordExploreClick(input: $input) {
|
|
546
|
+
recorded
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
`
|
|
550
|
+
);
|
|
538
551
|
|
|
539
552
|
// src/data/products.ts
|
|
540
553
|
var ECOSYSTEM_PRODUCTS = [
|
|
@@ -1467,6 +1480,20 @@ function useExploreChat(options = {}) {
|
|
|
1467
1480
|
return null;
|
|
1468
1481
|
}, [messages]);
|
|
1469
1482
|
const canSend = (phase === "ready" || phase === "error") && catalog.enabled && turnCount < catalog.limits.maxTurnsPerConversation;
|
|
1483
|
+
const recordClick = useCallback(
|
|
1484
|
+
(messageId, kind, url) => {
|
|
1485
|
+
const token = conversationTokenRef.current;
|
|
1486
|
+
if (!token) return;
|
|
1487
|
+
try {
|
|
1488
|
+
void client.mutate(RECORD_EXPLORE_CLICK_MUTATION, {
|
|
1489
|
+
input: { conversationToken: token, messageId, kind, url }
|
|
1490
|
+
}).catch(() => {
|
|
1491
|
+
});
|
|
1492
|
+
} catch {
|
|
1493
|
+
}
|
|
1494
|
+
},
|
|
1495
|
+
[client]
|
|
1496
|
+
);
|
|
1470
1497
|
return {
|
|
1471
1498
|
phase,
|
|
1472
1499
|
catalog,
|
|
@@ -1484,7 +1511,8 @@ function useExploreChat(options = {}) {
|
|
|
1484
1511
|
history: archive,
|
|
1485
1512
|
openConversation,
|
|
1486
1513
|
deleteConversation,
|
|
1487
|
-
clearHistory
|
|
1514
|
+
clearHistory,
|
|
1515
|
+
recordClick
|
|
1488
1516
|
};
|
|
1489
1517
|
}
|
|
1490
1518
|
var optimisticCounter = 0;
|
|
@@ -1497,21 +1525,37 @@ function getRecognitionCtor() {
|
|
|
1497
1525
|
const w = window;
|
|
1498
1526
|
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
1499
1527
|
}
|
|
1528
|
+
function isTouchDevice() {
|
|
1529
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
1530
|
+
try {
|
|
1531
|
+
return window.matchMedia("(pointer: coarse)").matches;
|
|
1532
|
+
} catch {
|
|
1533
|
+
return false;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1500
1536
|
var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
1537
|
+
var MIC_UNAVAILABLE_MESSAGE = "Dictation isn't available in this browser. You can type instead.";
|
|
1538
|
+
var RELEASE_SETTLE_MS = 250;
|
|
1539
|
+
var RETRY_DELAY_MS = 350;
|
|
1540
|
+
var sleep = (ms) => new Promise((resolve) => {
|
|
1541
|
+
setTimeout(resolve, ms);
|
|
1542
|
+
});
|
|
1501
1543
|
async function ensureMicrophoneAccess() {
|
|
1502
1544
|
const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
|
|
1503
|
-
if (!media?.getUserMedia)
|
|
1545
|
+
if (!media?.getUserMedia)
|
|
1546
|
+
return { ok: true, confirmed: false, primed: false };
|
|
1504
1547
|
try {
|
|
1505
1548
|
const status = await navigator.permissions?.query({
|
|
1506
1549
|
name: "microphone"
|
|
1507
1550
|
});
|
|
1508
|
-
if (status?.state === "granted")
|
|
1551
|
+
if (status?.state === "granted")
|
|
1552
|
+
return { ok: true, confirmed: true, primed: false };
|
|
1509
1553
|
} catch {
|
|
1510
1554
|
}
|
|
1511
1555
|
try {
|
|
1512
1556
|
const stream = await media.getUserMedia({ audio: true });
|
|
1513
1557
|
for (const track of stream.getTracks()) track.stop();
|
|
1514
|
-
return { ok: true, confirmed: true };
|
|
1558
|
+
return { ok: true, confirmed: true, primed: true };
|
|
1515
1559
|
} catch (error) {
|
|
1516
1560
|
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
1517
1561
|
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
@@ -1555,19 +1599,17 @@ function useSpeechInput({
|
|
|
1555
1599
|
const recognitionRef = useRef(null);
|
|
1556
1600
|
const startTokenRef = useRef(0);
|
|
1557
1601
|
const micConfirmedRef = useRef(false);
|
|
1558
|
-
const
|
|
1559
|
-
const
|
|
1560
|
-
(Ctor, token) => {
|
|
1561
|
-
beginRecognitionRef.current?.(Ctor, token);
|
|
1562
|
-
},
|
|
1563
|
-
[]
|
|
1564
|
-
);
|
|
1602
|
+
const wantsListeningRef = useRef(false);
|
|
1603
|
+
const retriedRef = useRef(false);
|
|
1565
1604
|
const finalRef = useRef(onFinalTranscript);
|
|
1566
1605
|
const errorRef = useRef(onError);
|
|
1567
1606
|
finalRef.current = onFinalTranscript;
|
|
1568
1607
|
errorRef.current = onError;
|
|
1608
|
+
const langRef = useRef(lang);
|
|
1609
|
+
langRef.current = lang;
|
|
1569
1610
|
const stop = useCallback(() => {
|
|
1570
1611
|
startTokenRef.current += 1;
|
|
1612
|
+
wantsListeningRef.current = false;
|
|
1571
1613
|
setListening(false);
|
|
1572
1614
|
setInterim("");
|
|
1573
1615
|
const recognition = recognitionRef.current;
|
|
@@ -1576,83 +1618,111 @@ function useSpeechInput({
|
|
|
1576
1618
|
recognition.stop();
|
|
1577
1619
|
} catch {
|
|
1578
1620
|
}
|
|
1579
|
-
setListening(false);
|
|
1580
|
-
setInterim("");
|
|
1581
1621
|
}, []);
|
|
1582
|
-
const
|
|
1622
|
+
const openSession = useCallback((token) => {
|
|
1583
1623
|
const Ctor = getRecognitionCtor();
|
|
1584
|
-
if (!Ctor) return;
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1624
|
+
if (!Ctor || token !== startTokenRef.current) return;
|
|
1625
|
+
const recognition = new Ctor();
|
|
1626
|
+
recognition.lang = langRef.current ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
1627
|
+
recognition.continuous = !isTouchDevice();
|
|
1628
|
+
recognition.interimResults = true;
|
|
1629
|
+
recognition.maxAlternatives = 1;
|
|
1630
|
+
recognition.onstart = () => {
|
|
1631
|
+
if (token !== startTokenRef.current) return;
|
|
1632
|
+
setError(null);
|
|
1633
|
+
setListening(true);
|
|
1634
|
+
};
|
|
1635
|
+
recognition.onresult = (event) => {
|
|
1636
|
+
if (token !== startTokenRef.current) return;
|
|
1637
|
+
let settled = "";
|
|
1638
|
+
let pending = "";
|
|
1639
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
1640
|
+
const result = event.results[i];
|
|
1641
|
+
if (!result) continue;
|
|
1642
|
+
const text = result[0]?.transcript ?? "";
|
|
1643
|
+
if (result.isFinal) settled += text;
|
|
1644
|
+
else pending += text;
|
|
1645
|
+
}
|
|
1646
|
+
setInterim(pending);
|
|
1647
|
+
if (settled.trim() !== "") finalRef.current(settled);
|
|
1648
|
+
};
|
|
1649
|
+
recognition.onerror = (event) => {
|
|
1650
|
+
if (token !== startTokenRef.current) return;
|
|
1651
|
+
if ((event.error === "not-allowed" || event.error === "service-not-allowed") && micConfirmedRef.current && !retriedRef.current) {
|
|
1652
|
+
retriedRef.current = true;
|
|
1653
|
+
void sleep(RETRY_DELAY_MS).then(() => {
|
|
1654
|
+
if (token !== startTokenRef.current || !wantsListeningRef.current)
|
|
1655
|
+
return;
|
|
1656
|
+
openSessionRef.current?.(token);
|
|
1657
|
+
});
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
const message = micConfirmedRef.current && (event.error === "not-allowed" || event.error === "service-not-allowed") ? MIC_UNAVAILABLE_MESSAGE : describeError(event.error);
|
|
1661
|
+
wantsListeningRef.current = false;
|
|
1662
|
+
setListening(false);
|
|
1663
|
+
setInterim("");
|
|
1664
|
+
if (message !== "") {
|
|
1665
|
+
setError(message);
|
|
1666
|
+
errorRef.current?.(message);
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
recognition.onend = () => {
|
|
1670
|
+
if (token !== startTokenRef.current) return;
|
|
1671
|
+
setInterim("");
|
|
1672
|
+
if (wantsListeningRef.current && isTouchDevice()) {
|
|
1673
|
+
void sleep(120).then(() => {
|
|
1674
|
+
if (token !== startTokenRef.current || !wantsListeningRef.current)
|
|
1675
|
+
return;
|
|
1676
|
+
openSessionRef.current?.(token);
|
|
1677
|
+
});
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
setListening(false);
|
|
1681
|
+
};
|
|
1682
|
+
recognitionRef.current = recognition;
|
|
1683
|
+
try {
|
|
1684
|
+
recognition.start();
|
|
1685
|
+
} catch {
|
|
1686
|
+
wantsListeningRef.current = false;
|
|
1687
|
+
setListening(false);
|
|
1688
|
+
}
|
|
1689
|
+
}, []);
|
|
1690
|
+
const openSessionRef = useRef(null);
|
|
1691
|
+
openSessionRef.current = openSession;
|
|
1692
|
+
const start = useCallback(() => {
|
|
1693
|
+
if (!getRecognitionCtor()) return;
|
|
1588
1694
|
startTokenRef.current += 1;
|
|
1589
1695
|
const token = startTokenRef.current;
|
|
1590
|
-
|
|
1696
|
+
wantsListeningRef.current = true;
|
|
1697
|
+
retriedRef.current = false;
|
|
1698
|
+
micConfirmedRef.current = false;
|
|
1699
|
+
setError(null);
|
|
1700
|
+
setListening(true);
|
|
1701
|
+
void ensureMicrophoneAccess().then(async (access) => {
|
|
1591
1702
|
if (token !== startTokenRef.current) return;
|
|
1592
|
-
micConfirmedRef.current = access.ok && access.confirmed;
|
|
1593
1703
|
if (!access.ok) {
|
|
1704
|
+
wantsListeningRef.current = false;
|
|
1594
1705
|
setListening(false);
|
|
1595
1706
|
setError(access.message);
|
|
1596
1707
|
errorRef.current?.(access.message);
|
|
1597
1708
|
return;
|
|
1598
1709
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
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 = () => {
|
|
1710
|
+
micConfirmedRef.current = access.confirmed;
|
|
1711
|
+
if (access.primed) {
|
|
1712
|
+
await sleep(RELEASE_SETTLE_MS);
|
|
1610
1713
|
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 = micConfirmedRef.current && (event.error === "not-allowed" || event.error === "service-not-allowed") ? "Dictation isn't available in this browser. You can type instead." : 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
1714
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
);
|
|
1649
|
-
beginRecognitionRef.current = beginRecognitionImpl;
|
|
1715
|
+
openSessionRef.current?.(token);
|
|
1716
|
+
});
|
|
1717
|
+
}, []);
|
|
1650
1718
|
const toggle = useCallback(() => {
|
|
1651
1719
|
if (listening) stop();
|
|
1652
1720
|
else start();
|
|
1653
1721
|
}, [listening, start, stop]);
|
|
1654
1722
|
useEffect(
|
|
1655
1723
|
() => () => {
|
|
1724
|
+
startTokenRef.current += 1;
|
|
1725
|
+
wantsListeningRef.current = false;
|
|
1656
1726
|
const recognition = recognitionRef.current;
|
|
1657
1727
|
if (!recognition) return;
|
|
1658
1728
|
recognition.onresult = null;
|
|
@@ -1836,6 +1906,50 @@ var EXPLORE_CSS = `
|
|
|
1836
1906
|
border-radius: 9999px;
|
|
1837
1907
|
background-clip: content-box;
|
|
1838
1908
|
}
|
|
1909
|
+
|
|
1910
|
+
/* The product strip scrolls inside itself; a visible scrollbar would read as a broken
|
|
1911
|
+
layout rather than an affordance, and reserving its gutter shifts the chips. */
|
|
1912
|
+
.boff-explore-strip {
|
|
1913
|
+
scrollbar-width: none;
|
|
1914
|
+
-ms-overflow-style: none;
|
|
1915
|
+
}
|
|
1916
|
+
.boff-explore-strip::-webkit-scrollbar { display: none; }
|
|
1917
|
+
|
|
1918
|
+
@keyframes boff-explore-fade-in {
|
|
1919
|
+
from { opacity: 0; transform: translateY(2px); }
|
|
1920
|
+
to { opacity: 1; transform: none; }
|
|
1921
|
+
}
|
|
1922
|
+
.boff-explore-fade { animation: boff-explore-fade-in 220ms ease-out; }
|
|
1923
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1924
|
+
.boff-explore-fade { animation: none; }
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
/* Nothing inside an answer may widen the page. Model output carries long URLs, code and
|
|
1928
|
+
tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
|
|
1929
|
+
WHOLE document horizontally scrollable, not just the bubble. */
|
|
1930
|
+
.boff-explore-body,
|
|
1931
|
+
.boff-explore-body p,
|
|
1932
|
+
.boff-explore-body li,
|
|
1933
|
+
.boff-explore-body a,
|
|
1934
|
+
.boff-explore-body h1,
|
|
1935
|
+
.boff-explore-body h2,
|
|
1936
|
+
.boff-explore-body h3 {
|
|
1937
|
+
overflow-wrap: anywhere;
|
|
1938
|
+
word-break: break-word;
|
|
1939
|
+
}
|
|
1940
|
+
.boff-explore-body pre {
|
|
1941
|
+
overflow-x: auto;
|
|
1942
|
+
max-width: 100%;
|
|
1943
|
+
}
|
|
1944
|
+
.boff-explore-body table {
|
|
1945
|
+
display: block;
|
|
1946
|
+
overflow-x: auto;
|
|
1947
|
+
max-width: 100%;
|
|
1948
|
+
}
|
|
1949
|
+
.boff-explore-body img {
|
|
1950
|
+
max-width: 100%;
|
|
1951
|
+
height: auto;
|
|
1952
|
+
}
|
|
1839
1953
|
`;
|
|
1840
1954
|
function ExploreStyles() {
|
|
1841
1955
|
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
|
|
@@ -1857,7 +1971,6 @@ var CAPABILITIES = [
|
|
|
1857
1971
|
body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
|
|
1858
1972
|
}
|
|
1859
1973
|
];
|
|
1860
|
-
var PRODUCT_CHIP_PREVIEW = 12;
|
|
1861
1974
|
var CTA_VARIANTS = {
|
|
1862
1975
|
CONTACT: "default",
|
|
1863
1976
|
WAITLIST: "default",
|
|
@@ -1896,7 +2009,10 @@ function logoUrlOf(url) {
|
|
|
1896
2009
|
return null;
|
|
1897
2010
|
}
|
|
1898
2011
|
}
|
|
1899
|
-
function ReferenceCard({
|
|
2012
|
+
function ReferenceCard({
|
|
2013
|
+
reference,
|
|
2014
|
+
onFollow
|
|
2015
|
+
}) {
|
|
1900
2016
|
const [imageFailed, setImageFailed] = useState(false);
|
|
1901
2017
|
const [logoFailed, setLogoFailed] = useState(false);
|
|
1902
2018
|
const host = hostnameOf(reference.url);
|
|
@@ -1918,9 +2034,11 @@ function ReferenceCard({ reference }) {
|
|
|
1918
2034
|
"a",
|
|
1919
2035
|
{
|
|
1920
2036
|
"data-boff-explore": "reference",
|
|
2037
|
+
"data-index": reference.index ?? void 0,
|
|
1921
2038
|
href: reference.url,
|
|
1922
2039
|
target: "_blank",
|
|
1923
2040
|
rel: "noopener noreferrer",
|
|
2041
|
+
onClick: () => onFollow?.(reference.url),
|
|
1924
2042
|
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",
|
|
1925
2043
|
children: [
|
|
1926
2044
|
/* @__PURE__ */ jsx("div", { className: "relative h-11 w-11 shrink-0 overflow-hidden rounded-lg bg-muted sm:hidden", children: logoImg }),
|
|
@@ -1944,7 +2062,10 @@ function ReferenceCard({ reference }) {
|
|
|
1944
2062
|
}
|
|
1945
2063
|
) }) : letterPlate }),
|
|
1946
2064
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 sm:gap-1.5 sm:p-3", children: [
|
|
1947
|
-
/* @__PURE__ */
|
|
2065
|
+
/* @__PURE__ */ jsxs("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: [
|
|
2066
|
+
reference.index ? /* @__PURE__ */ jsx("span", { className: "mr-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded bg-primary/10 px-1 text-[10px] font-bold text-primary", children: reference.index }) : null,
|
|
2067
|
+
reference.title || host || reference.url
|
|
2068
|
+
] }),
|
|
1948
2069
|
reference.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground max-sm:hidden", children: reference.description }) : null,
|
|
1949
2070
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 sm:mt-auto sm:pt-2", children: [
|
|
1950
2071
|
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,
|
|
@@ -1960,7 +2081,8 @@ function ReferenceCard({ reference }) {
|
|
|
1960
2081
|
}
|
|
1961
2082
|
function CtaButton({
|
|
1962
2083
|
cta,
|
|
1963
|
-
onNavigate
|
|
2084
|
+
onNavigate,
|
|
2085
|
+
onFollow
|
|
1964
2086
|
}) {
|
|
1965
2087
|
const variant = CTA_VARIANTS[cta.kind] ?? "outline";
|
|
1966
2088
|
const internal = isInternalPath(cta.url);
|
|
@@ -1973,7 +2095,10 @@ function CtaButton({
|
|
|
1973
2095
|
type: "button",
|
|
1974
2096
|
size: "sm",
|
|
1975
2097
|
variant,
|
|
1976
|
-
onClick: () =>
|
|
2098
|
+
onClick: () => {
|
|
2099
|
+
onFollow?.(cta.url);
|
|
2100
|
+
onNavigate(cta.url);
|
|
2101
|
+
},
|
|
1977
2102
|
children: cta.label
|
|
1978
2103
|
}
|
|
1979
2104
|
);
|
|
@@ -1984,6 +2109,7 @@ function CtaButton({
|
|
|
1984
2109
|
"data-boff-explore": "cta",
|
|
1985
2110
|
"data-kind": cta.kind,
|
|
1986
2111
|
href: cta.url,
|
|
2112
|
+
onClick: () => onFollow?.(cta.url),
|
|
1987
2113
|
...internal ? {} : { target: "_blank", rel: "noopener noreferrer" },
|
|
1988
2114
|
children: [
|
|
1989
2115
|
cta.label,
|
|
@@ -2035,7 +2161,9 @@ function AssistantBubble({
|
|
|
2035
2161
|
message,
|
|
2036
2162
|
activity,
|
|
2037
2163
|
onNavigate,
|
|
2038
|
-
anchorRef
|
|
2164
|
+
anchorRef,
|
|
2165
|
+
onFollow,
|
|
2166
|
+
onFollowUp
|
|
2039
2167
|
}) {
|
|
2040
2168
|
const streaming = message.status === "PENDING" || message.status === "RUNNING";
|
|
2041
2169
|
const body = message.content || message.partialContent || "";
|
|
@@ -2057,24 +2185,77 @@ function AssistantBubble({
|
|
|
2057
2185
|
}
|
|
2058
2186
|
),
|
|
2059
2187
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
|
|
2188
|
+
message.status === "COMPLETED" && message.answered === false ? /* @__PURE__ */ jsxs(
|
|
2189
|
+
"div",
|
|
2190
|
+
{
|
|
2191
|
+
"data-boff-explore": "no-answer",
|
|
2192
|
+
className: "flex items-start gap-2 rounded-xl border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs leading-relaxed text-foreground",
|
|
2193
|
+
children: [
|
|
2194
|
+
/* @__PURE__ */ jsx(TriangleAlert, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600 dark:text-amber-400" }),
|
|
2195
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
2196
|
+
"I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
|
|
2197
|
+
" ",
|
|
2198
|
+
/* @__PURE__ */ jsx(
|
|
2199
|
+
"a",
|
|
2200
|
+
{
|
|
2201
|
+
href: "/contact",
|
|
2202
|
+
className: "underline underline-offset-2 hover:text-primary",
|
|
2203
|
+
onClick: (event) => {
|
|
2204
|
+
if (!onNavigate) return;
|
|
2205
|
+
event.preventDefault();
|
|
2206
|
+
onNavigate("/contact");
|
|
2207
|
+
},
|
|
2208
|
+
children: "ask a human"
|
|
2209
|
+
}
|
|
2210
|
+
),
|
|
2211
|
+
"."
|
|
2212
|
+
] })
|
|
2213
|
+
]
|
|
2214
|
+
}
|
|
2215
|
+
) : null,
|
|
2060
2216
|
body ? /* @__PURE__ */ jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
|
|
2061
|
-
/* @__PURE__ */ jsx("div", { className: "boff-prose boff-prose-sm text-card-foreground", children: /* @__PURE__ */ jsx(Markdown, { children: body }) }),
|
|
2217
|
+
/* @__PURE__ */ jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsx(Markdown, { children: body }) }),
|
|
2062
2218
|
streaming ? /* @__PURE__ */ jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
|
|
2063
2219
|
] }) : null,
|
|
2064
2220
|
streaming ? /* @__PURE__ */ jsx(ThinkingIndicator, { activity }) : null,
|
|
2065
2221
|
failed && !body ? /* @__PURE__ */ jsx("div", { className: "rounded-2xl rounded-tl-md border border-border bg-muted px-4 py-3 text-sm text-muted-foreground", children: "That answer didn't come through. Try asking again." }) : null,
|
|
2066
2222
|
message.references.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
|
|
2067
2223
|
/* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Sources" }),
|
|
2068
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: message.references.map((reference) => /* @__PURE__ */ jsx(
|
|
2224
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: message.references.map((reference) => /* @__PURE__ */ jsx(
|
|
2225
|
+
ReferenceCard,
|
|
2226
|
+
{
|
|
2227
|
+
reference,
|
|
2228
|
+
onFollow: (url) => onFollow?.("REFERENCE", url)
|
|
2229
|
+
},
|
|
2230
|
+
reference.url
|
|
2231
|
+
)) })
|
|
2069
2232
|
] }) : null,
|
|
2070
2233
|
message.ctas.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 pt-0.5", children: message.ctas.map((cta) => /* @__PURE__ */ jsx(
|
|
2071
2234
|
CtaButton,
|
|
2072
2235
|
{
|
|
2073
2236
|
cta,
|
|
2074
|
-
onNavigate
|
|
2237
|
+
onNavigate,
|
|
2238
|
+
onFollow: (url) => onFollow?.("CTA", url)
|
|
2075
2239
|
},
|
|
2076
2240
|
`${cta.kind}-${cta.url}`
|
|
2077
|
-
)) }) : null
|
|
2241
|
+
)) }) : null,
|
|
2242
|
+
message.status === "COMPLETED" && (message.followUps?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 pt-0.5", children: [
|
|
2243
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Next you could ask" }),
|
|
2244
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: (message.followUps ?? []).map((question) => /* @__PURE__ */ jsxs(
|
|
2245
|
+
"button",
|
|
2246
|
+
{
|
|
2247
|
+
"data-boff-explore": "follow-up",
|
|
2248
|
+
type: "button",
|
|
2249
|
+
onClick: () => onFollowUp?.(question),
|
|
2250
|
+
className: "group inline-flex max-w-full items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1 text-left text-xs text-card-foreground transition-colors hover:border-primary/40 hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2251
|
+
children: [
|
|
2252
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: question }),
|
|
2253
|
+
/* @__PURE__ */ jsx(ArrowUp, { className: "h-3 w-3 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
|
|
2254
|
+
]
|
|
2255
|
+
},
|
|
2256
|
+
question
|
|
2257
|
+
)) })
|
|
2258
|
+
] }) : null
|
|
2078
2259
|
] })
|
|
2079
2260
|
]
|
|
2080
2261
|
}
|
|
@@ -2120,6 +2301,256 @@ function ErrorBanner({
|
|
|
2120
2301
|
}
|
|
2121
2302
|
);
|
|
2122
2303
|
}
|
|
2304
|
+
function PromptCarousel({
|
|
2305
|
+
prompts,
|
|
2306
|
+
onPrompt,
|
|
2307
|
+
disabled
|
|
2308
|
+
}) {
|
|
2309
|
+
const [index, setIndex] = useState(0);
|
|
2310
|
+
const [paused, setPaused] = useState(false);
|
|
2311
|
+
const count = prompts.length;
|
|
2312
|
+
const current = prompts[index % count] ?? prompts[0] ?? "";
|
|
2313
|
+
const go = useCallback(
|
|
2314
|
+
(delta) => {
|
|
2315
|
+
setIndex((i) => (i + delta + count) % count);
|
|
2316
|
+
},
|
|
2317
|
+
[count]
|
|
2318
|
+
);
|
|
2319
|
+
useEffect(() => {
|
|
2320
|
+
if (paused || disabled || count < 2) return;
|
|
2321
|
+
if (typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
const timer = setInterval(() => setIndex((i) => (i + 1) % count), 4500);
|
|
2325
|
+
return () => clearInterval(timer);
|
|
2326
|
+
}, [paused, disabled, count]);
|
|
2327
|
+
if (count === 0) return null;
|
|
2328
|
+
return /* @__PURE__ */ jsxs(
|
|
2329
|
+
"div",
|
|
2330
|
+
{
|
|
2331
|
+
className: "flex min-w-0 items-center gap-1.5",
|
|
2332
|
+
role: "group",
|
|
2333
|
+
"aria-roledescription": "carousel",
|
|
2334
|
+
"aria-label": "Example questions",
|
|
2335
|
+
onMouseEnter: () => setPaused(true),
|
|
2336
|
+
onMouseLeave: () => setPaused(false),
|
|
2337
|
+
onFocusCapture: () => setPaused(true),
|
|
2338
|
+
onBlurCapture: () => setPaused(false),
|
|
2339
|
+
children: [
|
|
2340
|
+
count > 1 ? /* @__PURE__ */ jsx(
|
|
2341
|
+
"button",
|
|
2342
|
+
{
|
|
2343
|
+
type: "button",
|
|
2344
|
+
"aria-label": "Previous suggestion",
|
|
2345
|
+
onClick: () => go(-1),
|
|
2346
|
+
className: "hidden h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:inline-flex",
|
|
2347
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
2348
|
+
}
|
|
2349
|
+
) : null,
|
|
2350
|
+
/* @__PURE__ */ jsxs(
|
|
2351
|
+
"button",
|
|
2352
|
+
{
|
|
2353
|
+
"data-boff-explore": "chip",
|
|
2354
|
+
"data-chip-kind": "prompt",
|
|
2355
|
+
type: "button",
|
|
2356
|
+
disabled,
|
|
2357
|
+
"aria-label": `Ask: ${current}`,
|
|
2358
|
+
"aria-live": paused ? "polite" : "off",
|
|
2359
|
+
onClick: () => onPrompt(current),
|
|
2360
|
+
className: "boff-explore-fade group flex min-w-0 flex-1 items-center gap-2 rounded-full border border-border bg-card px-4 py-2 text-left text-sm text-card-foreground shadow-sm transition-colors hover:border-primary/50 hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2361
|
+
children: [
|
|
2362
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 shrink-0 text-primary" }),
|
|
2363
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: current }),
|
|
2364
|
+
/* @__PURE__ */ jsx(ArrowUp, { className: "ml-auto h-3.5 w-3.5 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
|
|
2365
|
+
]
|
|
2366
|
+
},
|
|
2367
|
+
current
|
|
2368
|
+
),
|
|
2369
|
+
count > 1 ? /* @__PURE__ */ jsx(
|
|
2370
|
+
"button",
|
|
2371
|
+
{
|
|
2372
|
+
type: "button",
|
|
2373
|
+
"aria-label": "Next suggestion",
|
|
2374
|
+
onClick: () => go(1),
|
|
2375
|
+
className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2376
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
2377
|
+
}
|
|
2378
|
+
) : null
|
|
2379
|
+
]
|
|
2380
|
+
}
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
function ProductStrip({
|
|
2384
|
+
products,
|
|
2385
|
+
focusProduct,
|
|
2386
|
+
onFocus
|
|
2387
|
+
}) {
|
|
2388
|
+
const [expanded, setExpanded] = useState(false);
|
|
2389
|
+
const chipClass = (active) => cn(
|
|
2390
|
+
"shrink-0 rounded-full border px-3 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2391
|
+
active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
2392
|
+
);
|
|
2393
|
+
return /* @__PURE__ */ jsxs(
|
|
2394
|
+
"div",
|
|
2395
|
+
{
|
|
2396
|
+
className: "flex min-w-0 items-center gap-2",
|
|
2397
|
+
role: "group",
|
|
2398
|
+
"aria-label": "Focus the assistant on one product",
|
|
2399
|
+
children: [
|
|
2400
|
+
/* @__PURE__ */ jsxs(
|
|
2401
|
+
"div",
|
|
2402
|
+
{
|
|
2403
|
+
className: cn(
|
|
2404
|
+
"boff-explore-strip flex min-w-0 flex-1 gap-2",
|
|
2405
|
+
expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
|
|
2406
|
+
),
|
|
2407
|
+
children: [
|
|
2408
|
+
/* @__PURE__ */ jsx(
|
|
2409
|
+
"button",
|
|
2410
|
+
{
|
|
2411
|
+
"data-boff-explore": "chip",
|
|
2412
|
+
"data-chip-kind": "product",
|
|
2413
|
+
"data-product": "all",
|
|
2414
|
+
type: "button",
|
|
2415
|
+
"aria-pressed": focusProduct === null,
|
|
2416
|
+
onClick: () => onFocus(null),
|
|
2417
|
+
className: chipClass(focusProduct === null),
|
|
2418
|
+
children: "All products"
|
|
2419
|
+
}
|
|
2420
|
+
),
|
|
2421
|
+
products.map((product) => /* @__PURE__ */ jsx(
|
|
2422
|
+
"button",
|
|
2423
|
+
{
|
|
2424
|
+
"data-boff-explore": "chip",
|
|
2425
|
+
"data-chip-kind": "product",
|
|
2426
|
+
"data-product": product.slug,
|
|
2427
|
+
type: "button",
|
|
2428
|
+
"aria-pressed": focusProduct === product.slug,
|
|
2429
|
+
title: product.tagline ?? product.name,
|
|
2430
|
+
onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
|
|
2431
|
+
className: chipClass(focusProduct === product.slug),
|
|
2432
|
+
children: product.name
|
|
2433
|
+
},
|
|
2434
|
+
product.slug
|
|
2435
|
+
))
|
|
2436
|
+
]
|
|
2437
|
+
}
|
|
2438
|
+
),
|
|
2439
|
+
products.length > 0 ? /* @__PURE__ */ jsx(
|
|
2440
|
+
"button",
|
|
2441
|
+
{
|
|
2442
|
+
type: "button",
|
|
2443
|
+
"data-boff-explore": "products-toggle",
|
|
2444
|
+
"aria-expanded": expanded,
|
|
2445
|
+
onClick: () => setExpanded((v) => !v),
|
|
2446
|
+
className: "shrink-0 whitespace-nowrap rounded-full px-2 py-1 text-xs font-medium text-primary underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2447
|
+
children: expanded ? "Show less" : `All ${products.length}`
|
|
2448
|
+
}
|
|
2449
|
+
) : null
|
|
2450
|
+
]
|
|
2451
|
+
}
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
function DisclaimerDialog({
|
|
2455
|
+
open,
|
|
2456
|
+
onClose,
|
|
2457
|
+
captchaOn
|
|
2458
|
+
}) {
|
|
2459
|
+
const panelRef = useRef(null);
|
|
2460
|
+
const closeRef = useRef(null);
|
|
2461
|
+
const titleId = "boff-explore-disclaimer-title";
|
|
2462
|
+
useEffect(() => {
|
|
2463
|
+
if (!open) return;
|
|
2464
|
+
const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
2465
|
+
const focusable = () => Array.from(
|
|
2466
|
+
panelRef.current?.querySelectorAll(
|
|
2467
|
+
'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
|
|
2468
|
+
) ?? []
|
|
2469
|
+
);
|
|
2470
|
+
const onKey = (event) => {
|
|
2471
|
+
if (event.key === "Escape") {
|
|
2472
|
+
onClose();
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
if (event.key !== "Tab") return;
|
|
2476
|
+
const items = focusable();
|
|
2477
|
+
if (items.length === 0) return;
|
|
2478
|
+
const first = items[0];
|
|
2479
|
+
const last = items[items.length - 1];
|
|
2480
|
+
const active = document.activeElement;
|
|
2481
|
+
if (event.shiftKey && (active === first || !panelRef.current?.contains(active))) {
|
|
2482
|
+
event.preventDefault();
|
|
2483
|
+
last?.focus();
|
|
2484
|
+
} else if (!event.shiftKey && active === last) {
|
|
2485
|
+
event.preventDefault();
|
|
2486
|
+
first?.focus();
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2489
|
+
document.addEventListener("keydown", onKey);
|
|
2490
|
+
closeRef.current?.focus();
|
|
2491
|
+
return () => {
|
|
2492
|
+
document.removeEventListener("keydown", onKey);
|
|
2493
|
+
opener?.focus();
|
|
2494
|
+
};
|
|
2495
|
+
}, [open, onClose]);
|
|
2496
|
+
if (!open) return null;
|
|
2497
|
+
return /* @__PURE__ */ jsx(
|
|
2498
|
+
"div",
|
|
2499
|
+
{
|
|
2500
|
+
"data-boff-explore": "disclaimer-dialog",
|
|
2501
|
+
role: "dialog",
|
|
2502
|
+
"aria-modal": "true",
|
|
2503
|
+
"aria-labelledby": titleId,
|
|
2504
|
+
className: "fixed inset-0 z-50 flex items-end justify-center bg-foreground/40 p-4 backdrop-blur-sm sm:items-center",
|
|
2505
|
+
onClick: onClose,
|
|
2506
|
+
children: /* @__PURE__ */ jsxs(
|
|
2507
|
+
"div",
|
|
2508
|
+
{
|
|
2509
|
+
ref: panelRef,
|
|
2510
|
+
className: "w-full max-w-md rounded-2xl border border-border bg-card p-5 shadow-xl",
|
|
2511
|
+
onClick: (event) => event.stopPropagation(),
|
|
2512
|
+
children: [
|
|
2513
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
2514
|
+
/* @__PURE__ */ jsx(
|
|
2515
|
+
"span",
|
|
2516
|
+
{
|
|
2517
|
+
"aria-hidden": "true",
|
|
2518
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary",
|
|
2519
|
+
children: /* @__PURE__ */ jsx(TriangleAlert, { className: "h-4 w-4" })
|
|
2520
|
+
}
|
|
2521
|
+
),
|
|
2522
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2523
|
+
/* @__PURE__ */ jsx(
|
|
2524
|
+
"p",
|
|
2525
|
+
{
|
|
2526
|
+
id: titleId,
|
|
2527
|
+
className: "text-sm font-semibold text-card-foreground",
|
|
2528
|
+
children: "How this assistant works"
|
|
2529
|
+
}
|
|
2530
|
+
),
|
|
2531
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2 text-xs leading-relaxed text-muted-foreground", children: [
|
|
2532
|
+
/* @__PURE__ */ jsx("p", { children: "Answers are generated from our public documentation and can be imperfect \u2014 check anything important against the linked sources." }),
|
|
2533
|
+
/* @__PURE__ */ jsx("p", { children: "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." }),
|
|
2534
|
+
captchaOn ? /* @__PURE__ */ jsx("p", { children: "Protected by reCAPTCHA \u2014 the Google Privacy Policy and Terms of Service apply." }) : null
|
|
2535
|
+
] })
|
|
2536
|
+
] })
|
|
2537
|
+
] }),
|
|
2538
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-end", children: /* @__PURE__ */ jsx(
|
|
2539
|
+
Button,
|
|
2540
|
+
{
|
|
2541
|
+
ref: closeRef,
|
|
2542
|
+
size: "sm",
|
|
2543
|
+
variant: "secondary",
|
|
2544
|
+
onClick: onClose,
|
|
2545
|
+
children: "Got it"
|
|
2546
|
+
}
|
|
2547
|
+
) })
|
|
2548
|
+
]
|
|
2549
|
+
}
|
|
2550
|
+
)
|
|
2551
|
+
}
|
|
2552
|
+
);
|
|
2553
|
+
}
|
|
2123
2554
|
function WelcomeState({
|
|
2124
2555
|
title,
|
|
2125
2556
|
body,
|
|
@@ -2130,21 +2561,19 @@ function WelcomeState({
|
|
|
2130
2561
|
onFocus,
|
|
2131
2562
|
disabled
|
|
2132
2563
|
}) {
|
|
2133
|
-
|
|
2134
|
-
const visibleProducts = showAllProducts ? products : products.slice(0, PRODUCT_CHIP_PREVIEW);
|
|
2135
|
-
return /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl px-4 py-10 sm:py-14", children: [
|
|
2564
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl px-4 py-8 sm:py-12", children: [
|
|
2136
2565
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
2137
2566
|
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted px-3 py-1 text-xs font-medium text-muted-foreground", children: [
|
|
2138
2567
|
/* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5" }),
|
|
2139
2568
|
"AI answers, grounded in our documentation"
|
|
2140
2569
|
] }),
|
|
2141
|
-
/* @__PURE__ */ jsx("h1", { className: "mt-
|
|
2142
|
-
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-
|
|
2570
|
+
/* @__PURE__ */ jsx("h1", { className: "mt-4 text-balance break-words text-2xl font-bold tracking-tight sm:text-4xl", children: title }),
|
|
2571
|
+
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-2.5 max-w-2xl text-pretty text-sm leading-relaxed text-muted-foreground sm:text-lg", children: body })
|
|
2143
2572
|
] }),
|
|
2144
|
-
/* @__PURE__ */ jsx("ul", { className: "mt-
|
|
2573
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-7 grid gap-2.5 sm:grid-cols-3", children: CAPABILITIES.map(({ icon: Icon, title: capTitle, body: capBody }) => /* @__PURE__ */ jsxs(
|
|
2145
2574
|
"li",
|
|
2146
2575
|
{
|
|
2147
|
-
className: "rounded-xl border border-border bg-card p-
|
|
2576
|
+
className: "rounded-xl border border-border/70 bg-card/60 p-3.5 text-left transition-colors hover:border-border",
|
|
2148
2577
|
children: [
|
|
2149
2578
|
/* @__PURE__ */ jsx("span", { className: "mb-2.5 flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }) }),
|
|
2150
2579
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-card-foreground", children: capTitle }),
|
|
@@ -2153,72 +2582,27 @@ function WelcomeState({
|
|
|
2153
2582
|
},
|
|
2154
2583
|
capTitle
|
|
2155
2584
|
)) }),
|
|
2156
|
-
prompts.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-
|
|
2157
|
-
/* @__PURE__ */ jsx("p", { className: "mb-2
|
|
2158
|
-
/* @__PURE__ */ jsx(
|
|
2159
|
-
|
|
2585
|
+
prompts.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-8", children: [
|
|
2586
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Try asking" }),
|
|
2587
|
+
/* @__PURE__ */ jsx(
|
|
2588
|
+
PromptCarousel,
|
|
2160
2589
|
{
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
className: "group inline-flex max-w-full items-center gap-1.5 rounded-full border border-border bg-card px-3.5 py-1.5 text-left text-sm text-card-foreground transition-colors hover:border-primary/40 hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2167
|
-
children: [
|
|
2168
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: prompt }),
|
|
2169
|
-
/* @__PURE__ */ jsx(ArrowUp, { className: "h-3 w-3 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
|
|
2170
|
-
]
|
|
2171
|
-
},
|
|
2172
|
-
prompt
|
|
2173
|
-
)) })
|
|
2590
|
+
prompts,
|
|
2591
|
+
onPrompt,
|
|
2592
|
+
disabled
|
|
2593
|
+
}
|
|
2594
|
+
)
|
|
2174
2595
|
] }) : null,
|
|
2175
|
-
products.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-
|
|
2176
|
-
/* @__PURE__ */ jsx("p", { className: "mb-2
|
|
2177
|
-
/* @__PURE__ */
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
"aria-pressed": focusProduct === null,
|
|
2186
|
-
onClick: () => onFocus(null),
|
|
2187
|
-
className: cn(
|
|
2188
|
-
"rounded-full border px-3 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2189
|
-
focusProduct === null ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
2190
|
-
),
|
|
2191
|
-
children: "All products"
|
|
2192
|
-
}
|
|
2193
|
-
),
|
|
2194
|
-
visibleProducts.map((product) => /* @__PURE__ */ jsx(
|
|
2195
|
-
"button",
|
|
2196
|
-
{
|
|
2197
|
-
"data-boff-explore": "chip",
|
|
2198
|
-
"data-chip-kind": "product",
|
|
2199
|
-
"data-product": product.slug,
|
|
2200
|
-
type: "button",
|
|
2201
|
-
"aria-pressed": focusProduct === product.slug,
|
|
2202
|
-
title: product.tagline ?? product.name,
|
|
2203
|
-
onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
|
|
2204
|
-
className: cn(
|
|
2205
|
-
"rounded-full border px-3 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2206
|
-
focusProduct === product.slug ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
2207
|
-
),
|
|
2208
|
-
children: product.name
|
|
2209
|
-
},
|
|
2210
|
-
product.slug
|
|
2211
|
-
)),
|
|
2212
|
-
products.length > PRODUCT_CHIP_PREVIEW ? /* @__PURE__ */ jsx(
|
|
2213
|
-
"button",
|
|
2214
|
-
{
|
|
2215
|
-
type: "button",
|
|
2216
|
-
onClick: () => setShowAllProducts((v) => !v),
|
|
2217
|
-
className: "rounded-full px-3 py-1 text-xs font-medium text-primary underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2218
|
-
children: showAllProducts ? "Show fewer" : `Show all ${products.length}`
|
|
2219
|
-
}
|
|
2220
|
-
) : null
|
|
2221
|
-
] })
|
|
2596
|
+
products.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
2597
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Focus on a product" }),
|
|
2598
|
+
/* @__PURE__ */ jsx(
|
|
2599
|
+
ProductStrip,
|
|
2600
|
+
{
|
|
2601
|
+
products,
|
|
2602
|
+
focusProduct,
|
|
2603
|
+
onFocus
|
|
2604
|
+
}
|
|
2605
|
+
)
|
|
2222
2606
|
] }) : null
|
|
2223
2607
|
] });
|
|
2224
2608
|
}
|
|
@@ -2255,7 +2639,8 @@ function ExplorePage({
|
|
|
2255
2639
|
history,
|
|
2256
2640
|
openConversation,
|
|
2257
2641
|
deleteConversation,
|
|
2258
|
-
clearHistory
|
|
2642
|
+
clearHistory,
|
|
2643
|
+
recordClick
|
|
2259
2644
|
} = useExploreChat({ productSlug, pageUrl, onSend, examplePrompts });
|
|
2260
2645
|
const [draft, setDraft] = useState("");
|
|
2261
2646
|
const textareaRef = useRef(null);
|
|
@@ -2274,6 +2659,7 @@ function ExplorePage({
|
|
|
2274
2659
|
}
|
|
2275
2660
|
});
|
|
2276
2661
|
speechStopRef.current = speech.stop;
|
|
2662
|
+
const [disclaimerOpen, setDisclaimerOpen] = useState(false);
|
|
2277
2663
|
const scrollRef = useRef(null);
|
|
2278
2664
|
const latestAssistantRef = useRef(null);
|
|
2279
2665
|
const alignedForRef = useRef(null);
|
|
@@ -2366,7 +2752,7 @@ function ExplorePage({
|
|
|
2366
2752
|
"data-boff-explore": "page",
|
|
2367
2753
|
"data-phase": phase,
|
|
2368
2754
|
className: cn(
|
|
2369
|
-
"flex w-full flex-col bg-background text-foreground",
|
|
2755
|
+
"flex w-full min-w-0 flex-col overflow-x-hidden bg-background text-foreground",
|
|
2370
2756
|
heightMode === "auto" && "min-h-[70vh]",
|
|
2371
2757
|
className
|
|
2372
2758
|
),
|
|
@@ -2508,7 +2894,7 @@ function ExplorePage({
|
|
|
2508
2894
|
role: "log",
|
|
2509
2895
|
"aria-live": "polite",
|
|
2510
2896
|
"aria-label": "Explore conversation",
|
|
2511
|
-
className: "boff-explore-scroll min-h-0 flex-1 overflow-y-auto",
|
|
2897
|
+
className: "boff-explore-scroll min-h-0 min-w-0 flex-1 overflow-y-auto overflow-x-hidden",
|
|
2512
2898
|
children: showWelcome ? /* @__PURE__ */ jsx(
|
|
2513
2899
|
WelcomeState,
|
|
2514
2900
|
{
|
|
@@ -2521,21 +2907,31 @@ function ExplorePage({
|
|
|
2521
2907
|
onFocus: setFocusProduct,
|
|
2522
2908
|
disabled: !canSend || busy
|
|
2523
2909
|
}
|
|
2524
|
-
) : /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-3xl space-y-6 px-4 py-6", children: messages.map(
|
|
2910
|
+
) : /* @__PURE__ */ jsx("div", { className: "mx-auto w-full min-w-0 max-w-3xl space-y-6 px-4 py-6", children: messages.map(
|
|
2525
2911
|
(message) => message.role === "USER" ? /* @__PURE__ */ jsx(UserBubble, { message }, message.id) : /* @__PURE__ */ jsx(
|
|
2526
2912
|
AssistantBubble,
|
|
2527
2913
|
{
|
|
2528
2914
|
message,
|
|
2529
2915
|
activity,
|
|
2530
2916
|
onNavigate,
|
|
2531
|
-
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0
|
|
2917
|
+
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
|
|
2918
|
+
onFollow: (kind, url) => recordClick(message.id, kind, url),
|
|
2919
|
+
onFollowUp: sendPrompt
|
|
2532
2920
|
},
|
|
2533
2921
|
message.id
|
|
2534
2922
|
)
|
|
2535
2923
|
) })
|
|
2536
2924
|
}
|
|
2537
2925
|
),
|
|
2538
|
-
/* @__PURE__ */ jsx(
|
|
2926
|
+
/* @__PURE__ */ jsx(
|
|
2927
|
+
DisclaimerDialog,
|
|
2928
|
+
{
|
|
2929
|
+
open: disclaimerOpen,
|
|
2930
|
+
onClose: () => setDisclaimerOpen(false),
|
|
2931
|
+
captchaOn
|
|
2932
|
+
}
|
|
2933
|
+
),
|
|
2934
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-border bg-background/85 px-4 pb-[max(1rem,env(safe-area-inset-bottom))] pt-3 backdrop-blur-md", children: /* @__PURE__ */ jsxs(
|
|
2539
2935
|
"form",
|
|
2540
2936
|
{
|
|
2541
2937
|
"data-boff-explore": "composer",
|
|
@@ -2550,7 +2946,7 @@ function ExplorePage({
|
|
|
2550
2946
|
"div",
|
|
2551
2947
|
{
|
|
2552
2948
|
className: cn(
|
|
2553
|
-
"flex items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-
|
|
2949
|
+
"flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
|
|
2554
2950
|
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
|
|
2555
2951
|
),
|
|
2556
2952
|
children: [
|
|
@@ -2571,7 +2967,7 @@ function ExplorePage({
|
|
|
2571
2967
|
},
|
|
2572
2968
|
"aria-label": "Ask a question",
|
|
2573
2969
|
placeholder: composerDisabled ? phase === "limited" ? "Message limit reached \u2014 please try again later" : "Explore is unavailable right now" : focusedProductName ? `Ask about ${focusedProductName}\u2026` : "Ask anything about our products\u2026",
|
|
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"
|
|
2970
|
+
className: "max-h-[200px] min-h-[44px] w-full min-w-0 flex-1 resize-none border-0 bg-transparent px-2 py-2.5 text-sm shadow-none focus-visible:ring-0 md:text-sm"
|
|
2575
2971
|
}
|
|
2576
2972
|
),
|
|
2577
2973
|
speech.supported ? /* @__PURE__ */ jsx(
|
|
@@ -2662,48 +3058,19 @@ function ExplorePage({
|
|
|
2662
3058
|
}
|
|
2663
3059
|
)
|
|
2664
3060
|
] }),
|
|
2665
|
-
/* @__PURE__ */ jsxs(
|
|
2666
|
-
"
|
|
2667
|
-
|
|
2668
|
-
"
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
"
|
|
2672
|
-
|
|
2673
|
-
"
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
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
|
-
)
|
|
3061
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
3062
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: "Grounded in our docs \u2014 check anything important." }),
|
|
3063
|
+
/* @__PURE__ */ jsx(
|
|
3064
|
+
"button",
|
|
3065
|
+
{
|
|
3066
|
+
type: "button",
|
|
3067
|
+
"data-boff-explore": "disclaimer",
|
|
3068
|
+
onClick: () => setDisclaimerOpen(true),
|
|
3069
|
+
className: "shrink-0 underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
3070
|
+
children: "Disclaimer"
|
|
3071
|
+
}
|
|
3072
|
+
)
|
|
3073
|
+
] })
|
|
2707
3074
|
]
|
|
2708
3075
|
}
|
|
2709
3076
|
) })
|