@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
package/dist/index.js
CHANGED
|
@@ -4959,6 +4959,7 @@ var EXPLORE_MESSAGE_FIELDS = (
|
|
|
4959
4959
|
description
|
|
4960
4960
|
product
|
|
4961
4961
|
imageUrl
|
|
4962
|
+
index
|
|
4962
4963
|
}
|
|
4963
4964
|
ctas {
|
|
4964
4965
|
kind
|
|
@@ -4966,6 +4967,8 @@ var EXPLORE_MESSAGE_FIELDS = (
|
|
|
4966
4967
|
url
|
|
4967
4968
|
product
|
|
4968
4969
|
}
|
|
4970
|
+
followUps
|
|
4971
|
+
answered
|
|
4969
4972
|
errorCode
|
|
4970
4973
|
createdAt
|
|
4971
4974
|
completedAt
|
|
@@ -5031,6 +5034,16 @@ var SEND_EXPLORE_MESSAGE_MUTATION = (
|
|
|
5031
5034
|
}
|
|
5032
5035
|
`
|
|
5033
5036
|
);
|
|
5037
|
+
var RECORD_EXPLORE_CLICK_MUTATION = (
|
|
5038
|
+
/* GraphQL */
|
|
5039
|
+
`
|
|
5040
|
+
mutation RecordExploreClick($input: RecordExploreClickInput!) {
|
|
5041
|
+
recordExploreClick(input: $input) {
|
|
5042
|
+
recorded
|
|
5043
|
+
}
|
|
5044
|
+
}
|
|
5045
|
+
`
|
|
5046
|
+
);
|
|
5034
5047
|
|
|
5035
5048
|
// src/data/products.ts
|
|
5036
5049
|
var ECOSYSTEM_PRODUCTS = [
|
|
@@ -5963,6 +5976,20 @@ function useExploreChat(options = {}) {
|
|
|
5963
5976
|
return null;
|
|
5964
5977
|
}, [messages]);
|
|
5965
5978
|
const canSend = (phase === "ready" || phase === "error") && catalog.enabled && turnCount < catalog.limits.maxTurnsPerConversation;
|
|
5979
|
+
const recordClick = React.useCallback(
|
|
5980
|
+
(messageId, kind, url) => {
|
|
5981
|
+
const token = conversationTokenRef.current;
|
|
5982
|
+
if (!token) return;
|
|
5983
|
+
try {
|
|
5984
|
+
void client.mutate(RECORD_EXPLORE_CLICK_MUTATION, {
|
|
5985
|
+
input: { conversationToken: token, messageId, kind, url }
|
|
5986
|
+
}).catch(() => {
|
|
5987
|
+
});
|
|
5988
|
+
} catch {
|
|
5989
|
+
}
|
|
5990
|
+
},
|
|
5991
|
+
[client]
|
|
5992
|
+
);
|
|
5966
5993
|
return {
|
|
5967
5994
|
phase,
|
|
5968
5995
|
catalog,
|
|
@@ -5980,7 +6007,8 @@ function useExploreChat(options = {}) {
|
|
|
5980
6007
|
history: archive,
|
|
5981
6008
|
openConversation,
|
|
5982
6009
|
deleteConversation,
|
|
5983
|
-
clearHistory
|
|
6010
|
+
clearHistory,
|
|
6011
|
+
recordClick
|
|
5984
6012
|
};
|
|
5985
6013
|
}
|
|
5986
6014
|
var optimisticCounter = 0;
|
|
@@ -5993,21 +6021,37 @@ function getRecognitionCtor() {
|
|
|
5993
6021
|
const w = window;
|
|
5994
6022
|
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
5995
6023
|
}
|
|
6024
|
+
function isTouchDevice() {
|
|
6025
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
6026
|
+
try {
|
|
6027
|
+
return window.matchMedia("(pointer: coarse)").matches;
|
|
6028
|
+
} catch {
|
|
6029
|
+
return false;
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
5996
6032
|
var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
6033
|
+
var MIC_UNAVAILABLE_MESSAGE = "Dictation isn't available in this browser. You can type instead.";
|
|
6034
|
+
var RELEASE_SETTLE_MS = 250;
|
|
6035
|
+
var RETRY_DELAY_MS = 350;
|
|
6036
|
+
var sleep = (ms) => new Promise((resolve) => {
|
|
6037
|
+
setTimeout(resolve, ms);
|
|
6038
|
+
});
|
|
5997
6039
|
async function ensureMicrophoneAccess() {
|
|
5998
6040
|
const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
|
|
5999
|
-
if (!media?.getUserMedia)
|
|
6041
|
+
if (!media?.getUserMedia)
|
|
6042
|
+
return { ok: true, confirmed: false, primed: false };
|
|
6000
6043
|
try {
|
|
6001
6044
|
const status = await navigator.permissions?.query({
|
|
6002
6045
|
name: "microphone"
|
|
6003
6046
|
});
|
|
6004
|
-
if (status?.state === "granted")
|
|
6047
|
+
if (status?.state === "granted")
|
|
6048
|
+
return { ok: true, confirmed: true, primed: false };
|
|
6005
6049
|
} catch {
|
|
6006
6050
|
}
|
|
6007
6051
|
try {
|
|
6008
6052
|
const stream = await media.getUserMedia({ audio: true });
|
|
6009
6053
|
for (const track of stream.getTracks()) track.stop();
|
|
6010
|
-
return { ok: true, confirmed: true };
|
|
6054
|
+
return { ok: true, confirmed: true, primed: true };
|
|
6011
6055
|
} catch (error) {
|
|
6012
6056
|
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
6013
6057
|
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
@@ -6051,19 +6095,17 @@ function useSpeechInput({
|
|
|
6051
6095
|
const recognitionRef = React.useRef(null);
|
|
6052
6096
|
const startTokenRef = React.useRef(0);
|
|
6053
6097
|
const micConfirmedRef = React.useRef(false);
|
|
6054
|
-
const
|
|
6055
|
-
const
|
|
6056
|
-
(Ctor, token) => {
|
|
6057
|
-
beginRecognitionRef.current?.(Ctor, token);
|
|
6058
|
-
},
|
|
6059
|
-
[]
|
|
6060
|
-
);
|
|
6098
|
+
const wantsListeningRef = React.useRef(false);
|
|
6099
|
+
const retriedRef = React.useRef(false);
|
|
6061
6100
|
const finalRef = React.useRef(onFinalTranscript);
|
|
6062
6101
|
const errorRef = React.useRef(onError);
|
|
6063
6102
|
finalRef.current = onFinalTranscript;
|
|
6064
6103
|
errorRef.current = onError;
|
|
6104
|
+
const langRef = React.useRef(lang);
|
|
6105
|
+
langRef.current = lang;
|
|
6065
6106
|
const stop = React.useCallback(() => {
|
|
6066
6107
|
startTokenRef.current += 1;
|
|
6108
|
+
wantsListeningRef.current = false;
|
|
6067
6109
|
setListening(false);
|
|
6068
6110
|
setInterim("");
|
|
6069
6111
|
const recognition = recognitionRef.current;
|
|
@@ -6072,83 +6114,111 @@ function useSpeechInput({
|
|
|
6072
6114
|
recognition.stop();
|
|
6073
6115
|
} catch {
|
|
6074
6116
|
}
|
|
6075
|
-
setListening(false);
|
|
6076
|
-
setInterim("");
|
|
6077
6117
|
}, []);
|
|
6078
|
-
const
|
|
6118
|
+
const openSession = React.useCallback((token) => {
|
|
6079
6119
|
const Ctor = getRecognitionCtor();
|
|
6080
|
-
if (!Ctor) return;
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6120
|
+
if (!Ctor || token !== startTokenRef.current) return;
|
|
6121
|
+
const recognition = new Ctor();
|
|
6122
|
+
recognition.lang = langRef.current ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
6123
|
+
recognition.continuous = !isTouchDevice();
|
|
6124
|
+
recognition.interimResults = true;
|
|
6125
|
+
recognition.maxAlternatives = 1;
|
|
6126
|
+
recognition.onstart = () => {
|
|
6127
|
+
if (token !== startTokenRef.current) return;
|
|
6128
|
+
setError(null);
|
|
6129
|
+
setListening(true);
|
|
6130
|
+
};
|
|
6131
|
+
recognition.onresult = (event) => {
|
|
6132
|
+
if (token !== startTokenRef.current) return;
|
|
6133
|
+
let settled = "";
|
|
6134
|
+
let pending = "";
|
|
6135
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
6136
|
+
const result = event.results[i];
|
|
6137
|
+
if (!result) continue;
|
|
6138
|
+
const text = result[0]?.transcript ?? "";
|
|
6139
|
+
if (result.isFinal) settled += text;
|
|
6140
|
+
else pending += text;
|
|
6141
|
+
}
|
|
6142
|
+
setInterim(pending);
|
|
6143
|
+
if (settled.trim() !== "") finalRef.current(settled);
|
|
6144
|
+
};
|
|
6145
|
+
recognition.onerror = (event) => {
|
|
6146
|
+
if (token !== startTokenRef.current) return;
|
|
6147
|
+
if ((event.error === "not-allowed" || event.error === "service-not-allowed") && micConfirmedRef.current && !retriedRef.current) {
|
|
6148
|
+
retriedRef.current = true;
|
|
6149
|
+
void sleep(RETRY_DELAY_MS).then(() => {
|
|
6150
|
+
if (token !== startTokenRef.current || !wantsListeningRef.current)
|
|
6151
|
+
return;
|
|
6152
|
+
openSessionRef.current?.(token);
|
|
6153
|
+
});
|
|
6154
|
+
return;
|
|
6155
|
+
}
|
|
6156
|
+
const message = micConfirmedRef.current && (event.error === "not-allowed" || event.error === "service-not-allowed") ? MIC_UNAVAILABLE_MESSAGE : describeError(event.error);
|
|
6157
|
+
wantsListeningRef.current = false;
|
|
6158
|
+
setListening(false);
|
|
6159
|
+
setInterim("");
|
|
6160
|
+
if (message !== "") {
|
|
6161
|
+
setError(message);
|
|
6162
|
+
errorRef.current?.(message);
|
|
6163
|
+
}
|
|
6164
|
+
};
|
|
6165
|
+
recognition.onend = () => {
|
|
6166
|
+
if (token !== startTokenRef.current) return;
|
|
6167
|
+
setInterim("");
|
|
6168
|
+
if (wantsListeningRef.current && isTouchDevice()) {
|
|
6169
|
+
void sleep(120).then(() => {
|
|
6170
|
+
if (token !== startTokenRef.current || !wantsListeningRef.current)
|
|
6171
|
+
return;
|
|
6172
|
+
openSessionRef.current?.(token);
|
|
6173
|
+
});
|
|
6174
|
+
return;
|
|
6175
|
+
}
|
|
6176
|
+
setListening(false);
|
|
6177
|
+
};
|
|
6178
|
+
recognitionRef.current = recognition;
|
|
6179
|
+
try {
|
|
6180
|
+
recognition.start();
|
|
6181
|
+
} catch {
|
|
6182
|
+
wantsListeningRef.current = false;
|
|
6183
|
+
setListening(false);
|
|
6184
|
+
}
|
|
6185
|
+
}, []);
|
|
6186
|
+
const openSessionRef = React.useRef(null);
|
|
6187
|
+
openSessionRef.current = openSession;
|
|
6188
|
+
const start = React.useCallback(() => {
|
|
6189
|
+
if (!getRecognitionCtor()) return;
|
|
6084
6190
|
startTokenRef.current += 1;
|
|
6085
6191
|
const token = startTokenRef.current;
|
|
6086
|
-
|
|
6192
|
+
wantsListeningRef.current = true;
|
|
6193
|
+
retriedRef.current = false;
|
|
6194
|
+
micConfirmedRef.current = false;
|
|
6195
|
+
setError(null);
|
|
6196
|
+
setListening(true);
|
|
6197
|
+
void ensureMicrophoneAccess().then(async (access) => {
|
|
6087
6198
|
if (token !== startTokenRef.current) return;
|
|
6088
|
-
micConfirmedRef.current = access.ok && access.confirmed;
|
|
6089
6199
|
if (!access.ok) {
|
|
6200
|
+
wantsListeningRef.current = false;
|
|
6090
6201
|
setListening(false);
|
|
6091
6202
|
setError(access.message);
|
|
6092
6203
|
errorRef.current?.(access.message);
|
|
6093
6204
|
return;
|
|
6094
6205
|
}
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
const beginRecognitionImpl = React.useCallback(
|
|
6099
|
-
(Ctor, token) => {
|
|
6100
|
-
const recognition = new Ctor();
|
|
6101
|
-
recognition.lang = lang ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
6102
|
-
recognition.continuous = true;
|
|
6103
|
-
recognition.interimResults = true;
|
|
6104
|
-
recognition.maxAlternatives = 1;
|
|
6105
|
-
recognition.onstart = () => {
|
|
6206
|
+
micConfirmedRef.current = access.confirmed;
|
|
6207
|
+
if (access.primed) {
|
|
6208
|
+
await sleep(RELEASE_SETTLE_MS);
|
|
6106
6209
|
if (token !== startTokenRef.current) return;
|
|
6107
|
-
setError(null);
|
|
6108
|
-
setListening(true);
|
|
6109
|
-
};
|
|
6110
|
-
recognition.onresult = (event) => {
|
|
6111
|
-
let settled = "";
|
|
6112
|
-
let pending = "";
|
|
6113
|
-
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
6114
|
-
const result = event.results[i];
|
|
6115
|
-
if (!result) continue;
|
|
6116
|
-
const text = result[0]?.transcript ?? "";
|
|
6117
|
-
if (result.isFinal) settled += text;
|
|
6118
|
-
else pending += text;
|
|
6119
|
-
}
|
|
6120
|
-
setInterim(pending);
|
|
6121
|
-
if (settled.trim() !== "") finalRef.current(settled);
|
|
6122
|
-
};
|
|
6123
|
-
recognition.onerror = (event) => {
|
|
6124
|
-
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);
|
|
6125
|
-
setListening(false);
|
|
6126
|
-
setInterim("");
|
|
6127
|
-
if (message !== "") {
|
|
6128
|
-
setError(message);
|
|
6129
|
-
errorRef.current?.(message);
|
|
6130
|
-
}
|
|
6131
|
-
};
|
|
6132
|
-
recognition.onend = () => {
|
|
6133
|
-
setListening(false);
|
|
6134
|
-
setInterim("");
|
|
6135
|
-
};
|
|
6136
|
-
recognitionRef.current = recognition;
|
|
6137
|
-
try {
|
|
6138
|
-
recognition.start();
|
|
6139
|
-
} catch {
|
|
6140
|
-
setListening(false);
|
|
6141
6210
|
}
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
);
|
|
6145
|
-
beginRecognitionRef.current = beginRecognitionImpl;
|
|
6211
|
+
openSessionRef.current?.(token);
|
|
6212
|
+
});
|
|
6213
|
+
}, []);
|
|
6146
6214
|
const toggle = React.useCallback(() => {
|
|
6147
6215
|
if (listening) stop();
|
|
6148
6216
|
else start();
|
|
6149
6217
|
}, [listening, start, stop]);
|
|
6150
6218
|
React.useEffect(
|
|
6151
6219
|
() => () => {
|
|
6220
|
+
startTokenRef.current += 1;
|
|
6221
|
+
wantsListeningRef.current = false;
|
|
6152
6222
|
const recognition = recognitionRef.current;
|
|
6153
6223
|
if (!recognition) return;
|
|
6154
6224
|
recognition.onresult = null;
|
|
@@ -6207,6 +6277,50 @@ var EXPLORE_CSS = `
|
|
|
6207
6277
|
border-radius: 9999px;
|
|
6208
6278
|
background-clip: content-box;
|
|
6209
6279
|
}
|
|
6280
|
+
|
|
6281
|
+
/* The product strip scrolls inside itself; a visible scrollbar would read as a broken
|
|
6282
|
+
layout rather than an affordance, and reserving its gutter shifts the chips. */
|
|
6283
|
+
.boff-explore-strip {
|
|
6284
|
+
scrollbar-width: none;
|
|
6285
|
+
-ms-overflow-style: none;
|
|
6286
|
+
}
|
|
6287
|
+
.boff-explore-strip::-webkit-scrollbar { display: none; }
|
|
6288
|
+
|
|
6289
|
+
@keyframes boff-explore-fade-in {
|
|
6290
|
+
from { opacity: 0; transform: translateY(2px); }
|
|
6291
|
+
to { opacity: 1; transform: none; }
|
|
6292
|
+
}
|
|
6293
|
+
.boff-explore-fade { animation: boff-explore-fade-in 220ms ease-out; }
|
|
6294
|
+
@media (prefers-reduced-motion: reduce) {
|
|
6295
|
+
.boff-explore-fade { animation: none; }
|
|
6296
|
+
}
|
|
6297
|
+
|
|
6298
|
+
/* Nothing inside an answer may widen the page. Model output carries long URLs, code and
|
|
6299
|
+
tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
|
|
6300
|
+
WHOLE document horizontally scrollable, not just the bubble. */
|
|
6301
|
+
.boff-explore-body,
|
|
6302
|
+
.boff-explore-body p,
|
|
6303
|
+
.boff-explore-body li,
|
|
6304
|
+
.boff-explore-body a,
|
|
6305
|
+
.boff-explore-body h1,
|
|
6306
|
+
.boff-explore-body h2,
|
|
6307
|
+
.boff-explore-body h3 {
|
|
6308
|
+
overflow-wrap: anywhere;
|
|
6309
|
+
word-break: break-word;
|
|
6310
|
+
}
|
|
6311
|
+
.boff-explore-body pre {
|
|
6312
|
+
overflow-x: auto;
|
|
6313
|
+
max-width: 100%;
|
|
6314
|
+
}
|
|
6315
|
+
.boff-explore-body table {
|
|
6316
|
+
display: block;
|
|
6317
|
+
overflow-x: auto;
|
|
6318
|
+
max-width: 100%;
|
|
6319
|
+
}
|
|
6320
|
+
.boff-explore-body img {
|
|
6321
|
+
max-width: 100%;
|
|
6322
|
+
height: auto;
|
|
6323
|
+
}
|
|
6210
6324
|
`;
|
|
6211
6325
|
function ExploreStyles() {
|
|
6212
6326
|
return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
|
|
@@ -6228,7 +6342,6 @@ var CAPABILITIES = [
|
|
|
6228
6342
|
body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
|
|
6229
6343
|
}
|
|
6230
6344
|
];
|
|
6231
|
-
var PRODUCT_CHIP_PREVIEW = 12;
|
|
6232
6345
|
var CTA_VARIANTS = {
|
|
6233
6346
|
CONTACT: "default",
|
|
6234
6347
|
WAITLIST: "default",
|
|
@@ -6267,7 +6380,10 @@ function logoUrlOf(url) {
|
|
|
6267
6380
|
return null;
|
|
6268
6381
|
}
|
|
6269
6382
|
}
|
|
6270
|
-
function ReferenceCard({
|
|
6383
|
+
function ReferenceCard({
|
|
6384
|
+
reference,
|
|
6385
|
+
onFollow
|
|
6386
|
+
}) {
|
|
6271
6387
|
const [imageFailed, setImageFailed] = React.useState(false);
|
|
6272
6388
|
const [logoFailed, setLogoFailed] = React.useState(false);
|
|
6273
6389
|
const host = hostnameOf(reference.url);
|
|
@@ -6289,9 +6405,11 @@ function ReferenceCard({ reference }) {
|
|
|
6289
6405
|
"a",
|
|
6290
6406
|
{
|
|
6291
6407
|
"data-boff-explore": "reference",
|
|
6408
|
+
"data-index": reference.index ?? void 0,
|
|
6292
6409
|
href: reference.url,
|
|
6293
6410
|
target: "_blank",
|
|
6294
6411
|
rel: "noopener noreferrer",
|
|
6412
|
+
onClick: () => onFollow?.(reference.url),
|
|
6295
6413
|
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",
|
|
6296
6414
|
children: [
|
|
6297
6415
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-11 w-11 shrink-0 overflow-hidden rounded-lg bg-muted sm:hidden", children: logoImg }),
|
|
@@ -6315,7 +6433,10 @@ function ReferenceCard({ reference }) {
|
|
|
6315
6433
|
}
|
|
6316
6434
|
) }) : letterPlate }),
|
|
6317
6435
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 sm:gap-1.5 sm:p-3", children: [
|
|
6318
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6436
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: [
|
|
6437
|
+
reference.index ? /* @__PURE__ */ jsxRuntime.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,
|
|
6438
|
+
reference.title || host || reference.url
|
|
6439
|
+
] }),
|
|
6319
6440
|
reference.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground max-sm:hidden", children: reference.description }) : null,
|
|
6320
6441
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:mt-auto sm:pt-2", children: [
|
|
6321
6442
|
reference.product ? /* @__PURE__ */ jsxRuntime.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,
|
|
@@ -6331,7 +6452,8 @@ function ReferenceCard({ reference }) {
|
|
|
6331
6452
|
}
|
|
6332
6453
|
function CtaButton({
|
|
6333
6454
|
cta,
|
|
6334
|
-
onNavigate
|
|
6455
|
+
onNavigate,
|
|
6456
|
+
onFollow
|
|
6335
6457
|
}) {
|
|
6336
6458
|
const variant = CTA_VARIANTS[cta.kind] ?? "outline";
|
|
6337
6459
|
const internal = isInternalPath(cta.url);
|
|
@@ -6344,7 +6466,10 @@ function CtaButton({
|
|
|
6344
6466
|
type: "button",
|
|
6345
6467
|
size: "sm",
|
|
6346
6468
|
variant,
|
|
6347
|
-
onClick: () =>
|
|
6469
|
+
onClick: () => {
|
|
6470
|
+
onFollow?.(cta.url);
|
|
6471
|
+
onNavigate(cta.url);
|
|
6472
|
+
},
|
|
6348
6473
|
children: cta.label
|
|
6349
6474
|
}
|
|
6350
6475
|
);
|
|
@@ -6355,6 +6480,7 @@ function CtaButton({
|
|
|
6355
6480
|
"data-boff-explore": "cta",
|
|
6356
6481
|
"data-kind": cta.kind,
|
|
6357
6482
|
href: cta.url,
|
|
6483
|
+
onClick: () => onFollow?.(cta.url),
|
|
6358
6484
|
...internal ? {} : { target: "_blank", rel: "noopener noreferrer" },
|
|
6359
6485
|
children: [
|
|
6360
6486
|
cta.label,
|
|
@@ -6406,7 +6532,9 @@ function AssistantBubble({
|
|
|
6406
6532
|
message,
|
|
6407
6533
|
activity,
|
|
6408
6534
|
onNavigate,
|
|
6409
|
-
anchorRef
|
|
6535
|
+
anchorRef,
|
|
6536
|
+
onFollow,
|
|
6537
|
+
onFollowUp
|
|
6410
6538
|
}) {
|
|
6411
6539
|
const streaming = message.status === "PENDING" || message.status === "RUNNING";
|
|
6412
6540
|
const body = message.content || message.partialContent || "";
|
|
@@ -6428,24 +6556,77 @@ function AssistantBubble({
|
|
|
6428
6556
|
}
|
|
6429
6557
|
),
|
|
6430
6558
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
|
|
6559
|
+
message.status === "COMPLETED" && message.answered === false ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6560
|
+
"div",
|
|
6561
|
+
{
|
|
6562
|
+
"data-boff-explore": "no-answer",
|
|
6563
|
+
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",
|
|
6564
|
+
children: [
|
|
6565
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600 dark:text-amber-400" }),
|
|
6566
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
6567
|
+
"I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
|
|
6568
|
+
" ",
|
|
6569
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6570
|
+
"a",
|
|
6571
|
+
{
|
|
6572
|
+
href: "/contact",
|
|
6573
|
+
className: "underline underline-offset-2 hover:text-primary",
|
|
6574
|
+
onClick: (event) => {
|
|
6575
|
+
if (!onNavigate) return;
|
|
6576
|
+
event.preventDefault();
|
|
6577
|
+
onNavigate("/contact");
|
|
6578
|
+
},
|
|
6579
|
+
children: "ask a human"
|
|
6580
|
+
}
|
|
6581
|
+
),
|
|
6582
|
+
"."
|
|
6583
|
+
] })
|
|
6584
|
+
]
|
|
6585
|
+
}
|
|
6586
|
+
) : null,
|
|
6431
6587
|
body ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
|
|
6432
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { children: body }) }),
|
|
6588
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { children: body }) }),
|
|
6433
6589
|
streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
|
|
6434
6590
|
] }) : null,
|
|
6435
6591
|
streaming ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingIndicator, { activity }) : null,
|
|
6436
6592
|
failed && !body ? /* @__PURE__ */ jsxRuntime.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,
|
|
6437
6593
|
message.references.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
6438
6594
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Sources" }),
|
|
6439
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: message.references.map((reference) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6595
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: message.references.map((reference) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6596
|
+
ReferenceCard,
|
|
6597
|
+
{
|
|
6598
|
+
reference,
|
|
6599
|
+
onFollow: (url) => onFollow?.("REFERENCE", url)
|
|
6600
|
+
},
|
|
6601
|
+
reference.url
|
|
6602
|
+
)) })
|
|
6440
6603
|
] }) : null,
|
|
6441
6604
|
message.ctas.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 pt-0.5", children: message.ctas.map((cta) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6442
6605
|
CtaButton,
|
|
6443
6606
|
{
|
|
6444
6607
|
cta,
|
|
6445
|
-
onNavigate
|
|
6608
|
+
onNavigate,
|
|
6609
|
+
onFollow: (url) => onFollow?.("CTA", url)
|
|
6446
6610
|
},
|
|
6447
6611
|
`${cta.kind}-${cta.url}`
|
|
6448
|
-
)) }) : null
|
|
6612
|
+
)) }) : null,
|
|
6613
|
+
message.status === "COMPLETED" && (message.followUps?.length ?? 0) > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 pt-0.5", children: [
|
|
6614
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Next you could ask" }),
|
|
6615
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: (message.followUps ?? []).map((question) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6616
|
+
"button",
|
|
6617
|
+
{
|
|
6618
|
+
"data-boff-explore": "follow-up",
|
|
6619
|
+
type: "button",
|
|
6620
|
+
onClick: () => onFollowUp?.(question),
|
|
6621
|
+
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",
|
|
6622
|
+
children: [
|
|
6623
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: question }),
|
|
6624
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3 w-3 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
|
|
6625
|
+
]
|
|
6626
|
+
},
|
|
6627
|
+
question
|
|
6628
|
+
)) })
|
|
6629
|
+
] }) : null
|
|
6449
6630
|
] })
|
|
6450
6631
|
]
|
|
6451
6632
|
}
|
|
@@ -6491,6 +6672,256 @@ function ErrorBanner({
|
|
|
6491
6672
|
}
|
|
6492
6673
|
);
|
|
6493
6674
|
}
|
|
6675
|
+
function PromptCarousel({
|
|
6676
|
+
prompts,
|
|
6677
|
+
onPrompt,
|
|
6678
|
+
disabled
|
|
6679
|
+
}) {
|
|
6680
|
+
const [index, setIndex] = React.useState(0);
|
|
6681
|
+
const [paused, setPaused] = React.useState(false);
|
|
6682
|
+
const count = prompts.length;
|
|
6683
|
+
const current = prompts[index % count] ?? prompts[0] ?? "";
|
|
6684
|
+
const go = React.useCallback(
|
|
6685
|
+
(delta) => {
|
|
6686
|
+
setIndex((i) => (i + delta + count) % count);
|
|
6687
|
+
},
|
|
6688
|
+
[count]
|
|
6689
|
+
);
|
|
6690
|
+
React.useEffect(() => {
|
|
6691
|
+
if (paused || disabled || count < 2) return;
|
|
6692
|
+
if (typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
|
|
6693
|
+
return;
|
|
6694
|
+
}
|
|
6695
|
+
const timer = setInterval(() => setIndex((i) => (i + 1) % count), 4500);
|
|
6696
|
+
return () => clearInterval(timer);
|
|
6697
|
+
}, [paused, disabled, count]);
|
|
6698
|
+
if (count === 0) return null;
|
|
6699
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6700
|
+
"div",
|
|
6701
|
+
{
|
|
6702
|
+
className: "flex min-w-0 items-center gap-1.5",
|
|
6703
|
+
role: "group",
|
|
6704
|
+
"aria-roledescription": "carousel",
|
|
6705
|
+
"aria-label": "Example questions",
|
|
6706
|
+
onMouseEnter: () => setPaused(true),
|
|
6707
|
+
onMouseLeave: () => setPaused(false),
|
|
6708
|
+
onFocusCapture: () => setPaused(true),
|
|
6709
|
+
onBlurCapture: () => setPaused(false),
|
|
6710
|
+
children: [
|
|
6711
|
+
count > 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6712
|
+
"button",
|
|
6713
|
+
{
|
|
6714
|
+
type: "button",
|
|
6715
|
+
"aria-label": "Previous suggestion",
|
|
6716
|
+
onClick: () => go(-1),
|
|
6717
|
+
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",
|
|
6718
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
|
|
6719
|
+
}
|
|
6720
|
+
) : null,
|
|
6721
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6722
|
+
"button",
|
|
6723
|
+
{
|
|
6724
|
+
"data-boff-explore": "chip",
|
|
6725
|
+
"data-chip-kind": "prompt",
|
|
6726
|
+
type: "button",
|
|
6727
|
+
disabled,
|
|
6728
|
+
"aria-label": `Ask: ${current}`,
|
|
6729
|
+
"aria-live": paused ? "polite" : "off",
|
|
6730
|
+
onClick: () => onPrompt(current),
|
|
6731
|
+
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",
|
|
6732
|
+
children: [
|
|
6733
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "h-3.5 w-3.5 shrink-0 text-primary" }),
|
|
6734
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: current }),
|
|
6735
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "ml-auto h-3.5 w-3.5 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
|
|
6736
|
+
]
|
|
6737
|
+
},
|
|
6738
|
+
current
|
|
6739
|
+
),
|
|
6740
|
+
count > 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6741
|
+
"button",
|
|
6742
|
+
{
|
|
6743
|
+
type: "button",
|
|
6744
|
+
"aria-label": "Next suggestion",
|
|
6745
|
+
onClick: () => go(1),
|
|
6746
|
+
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",
|
|
6747
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
|
|
6748
|
+
}
|
|
6749
|
+
) : null
|
|
6750
|
+
]
|
|
6751
|
+
}
|
|
6752
|
+
);
|
|
6753
|
+
}
|
|
6754
|
+
function ProductStrip({
|
|
6755
|
+
products,
|
|
6756
|
+
focusProduct,
|
|
6757
|
+
onFocus
|
|
6758
|
+
}) {
|
|
6759
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
6760
|
+
const chipClass = (active) => cn(
|
|
6761
|
+
"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",
|
|
6762
|
+
active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
6763
|
+
);
|
|
6764
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6765
|
+
"div",
|
|
6766
|
+
{
|
|
6767
|
+
className: "flex min-w-0 items-center gap-2",
|
|
6768
|
+
role: "group",
|
|
6769
|
+
"aria-label": "Focus the assistant on one product",
|
|
6770
|
+
children: [
|
|
6771
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6772
|
+
"div",
|
|
6773
|
+
{
|
|
6774
|
+
className: cn(
|
|
6775
|
+
"boff-explore-strip flex min-w-0 flex-1 gap-2",
|
|
6776
|
+
expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
|
|
6777
|
+
),
|
|
6778
|
+
children: [
|
|
6779
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6780
|
+
"button",
|
|
6781
|
+
{
|
|
6782
|
+
"data-boff-explore": "chip",
|
|
6783
|
+
"data-chip-kind": "product",
|
|
6784
|
+
"data-product": "all",
|
|
6785
|
+
type: "button",
|
|
6786
|
+
"aria-pressed": focusProduct === null,
|
|
6787
|
+
onClick: () => onFocus(null),
|
|
6788
|
+
className: chipClass(focusProduct === null),
|
|
6789
|
+
children: "All products"
|
|
6790
|
+
}
|
|
6791
|
+
),
|
|
6792
|
+
products.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6793
|
+
"button",
|
|
6794
|
+
{
|
|
6795
|
+
"data-boff-explore": "chip",
|
|
6796
|
+
"data-chip-kind": "product",
|
|
6797
|
+
"data-product": product.slug,
|
|
6798
|
+
type: "button",
|
|
6799
|
+
"aria-pressed": focusProduct === product.slug,
|
|
6800
|
+
title: product.tagline ?? product.name,
|
|
6801
|
+
onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
|
|
6802
|
+
className: chipClass(focusProduct === product.slug),
|
|
6803
|
+
children: product.name
|
|
6804
|
+
},
|
|
6805
|
+
product.slug
|
|
6806
|
+
))
|
|
6807
|
+
]
|
|
6808
|
+
}
|
|
6809
|
+
),
|
|
6810
|
+
products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6811
|
+
"button",
|
|
6812
|
+
{
|
|
6813
|
+
type: "button",
|
|
6814
|
+
"data-boff-explore": "products-toggle",
|
|
6815
|
+
"aria-expanded": expanded,
|
|
6816
|
+
onClick: () => setExpanded((v) => !v),
|
|
6817
|
+
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",
|
|
6818
|
+
children: expanded ? "Show less" : `All ${products.length}`
|
|
6819
|
+
}
|
|
6820
|
+
) : null
|
|
6821
|
+
]
|
|
6822
|
+
}
|
|
6823
|
+
);
|
|
6824
|
+
}
|
|
6825
|
+
function DisclaimerDialog({
|
|
6826
|
+
open,
|
|
6827
|
+
onClose,
|
|
6828
|
+
captchaOn
|
|
6829
|
+
}) {
|
|
6830
|
+
const panelRef = React.useRef(null);
|
|
6831
|
+
const closeRef = React.useRef(null);
|
|
6832
|
+
const titleId = "boff-explore-disclaimer-title";
|
|
6833
|
+
React.useEffect(() => {
|
|
6834
|
+
if (!open) return;
|
|
6835
|
+
const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
6836
|
+
const focusable = () => Array.from(
|
|
6837
|
+
panelRef.current?.querySelectorAll(
|
|
6838
|
+
'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
|
|
6839
|
+
) ?? []
|
|
6840
|
+
);
|
|
6841
|
+
const onKey = (event) => {
|
|
6842
|
+
if (event.key === "Escape") {
|
|
6843
|
+
onClose();
|
|
6844
|
+
return;
|
|
6845
|
+
}
|
|
6846
|
+
if (event.key !== "Tab") return;
|
|
6847
|
+
const items = focusable();
|
|
6848
|
+
if (items.length === 0) return;
|
|
6849
|
+
const first = items[0];
|
|
6850
|
+
const last = items[items.length - 1];
|
|
6851
|
+
const active = document.activeElement;
|
|
6852
|
+
if (event.shiftKey && (active === first || !panelRef.current?.contains(active))) {
|
|
6853
|
+
event.preventDefault();
|
|
6854
|
+
last?.focus();
|
|
6855
|
+
} else if (!event.shiftKey && active === last) {
|
|
6856
|
+
event.preventDefault();
|
|
6857
|
+
first?.focus();
|
|
6858
|
+
}
|
|
6859
|
+
};
|
|
6860
|
+
document.addEventListener("keydown", onKey);
|
|
6861
|
+
closeRef.current?.focus();
|
|
6862
|
+
return () => {
|
|
6863
|
+
document.removeEventListener("keydown", onKey);
|
|
6864
|
+
opener?.focus();
|
|
6865
|
+
};
|
|
6866
|
+
}, [open, onClose]);
|
|
6867
|
+
if (!open) return null;
|
|
6868
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6869
|
+
"div",
|
|
6870
|
+
{
|
|
6871
|
+
"data-boff-explore": "disclaimer-dialog",
|
|
6872
|
+
role: "dialog",
|
|
6873
|
+
"aria-modal": "true",
|
|
6874
|
+
"aria-labelledby": titleId,
|
|
6875
|
+
className: "fixed inset-0 z-50 flex items-end justify-center bg-foreground/40 p-4 backdrop-blur-sm sm:items-center",
|
|
6876
|
+
onClick: onClose,
|
|
6877
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6878
|
+
"div",
|
|
6879
|
+
{
|
|
6880
|
+
ref: panelRef,
|
|
6881
|
+
className: "w-full max-w-md rounded-2xl border border-border bg-card p-5 shadow-xl",
|
|
6882
|
+
onClick: (event) => event.stopPropagation(),
|
|
6883
|
+
children: [
|
|
6884
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
|
|
6885
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6886
|
+
"span",
|
|
6887
|
+
{
|
|
6888
|
+
"aria-hidden": "true",
|
|
6889
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary",
|
|
6890
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "h-4 w-4" })
|
|
6891
|
+
}
|
|
6892
|
+
),
|
|
6893
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
6894
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6895
|
+
"p",
|
|
6896
|
+
{
|
|
6897
|
+
id: titleId,
|
|
6898
|
+
className: "text-sm font-semibold text-card-foreground",
|
|
6899
|
+
children: "How this assistant works"
|
|
6900
|
+
}
|
|
6901
|
+
),
|
|
6902
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 space-y-2 text-xs leading-relaxed text-muted-foreground", children: [
|
|
6903
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "Answers are generated from our public documentation and can be imperfect \u2014 check anything important against the linked sources." }),
|
|
6904
|
+
/* @__PURE__ */ jsxRuntime.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." }),
|
|
6905
|
+
captchaOn ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Protected by reCAPTCHA \u2014 the Google Privacy Policy and Terms of Service apply." }) : null
|
|
6906
|
+
] })
|
|
6907
|
+
] })
|
|
6908
|
+
] }),
|
|
6909
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6910
|
+
Button,
|
|
6911
|
+
{
|
|
6912
|
+
ref: closeRef,
|
|
6913
|
+
size: "sm",
|
|
6914
|
+
variant: "secondary",
|
|
6915
|
+
onClick: onClose,
|
|
6916
|
+
children: "Got it"
|
|
6917
|
+
}
|
|
6918
|
+
) })
|
|
6919
|
+
]
|
|
6920
|
+
}
|
|
6921
|
+
)
|
|
6922
|
+
}
|
|
6923
|
+
);
|
|
6924
|
+
}
|
|
6494
6925
|
function WelcomeState({
|
|
6495
6926
|
title,
|
|
6496
6927
|
body,
|
|
@@ -6501,21 +6932,19 @@ function WelcomeState({
|
|
|
6501
6932
|
onFocus,
|
|
6502
6933
|
disabled
|
|
6503
6934
|
}) {
|
|
6504
|
-
|
|
6505
|
-
const visibleProducts = showAllProducts ? products : products.slice(0, PRODUCT_CHIP_PREVIEW);
|
|
6506
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-3xl px-4 py-10 sm:py-14", children: [
|
|
6935
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-3xl px-4 py-8 sm:py-12", children: [
|
|
6507
6936
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
6508
6937
|
/* @__PURE__ */ jsxRuntime.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: [
|
|
6509
6938
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "h-3.5 w-3.5" }),
|
|
6510
6939
|
"AI answers, grounded in our documentation"
|
|
6511
6940
|
] }),
|
|
6512
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-
|
|
6513
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mx-auto mt-
|
|
6941
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-4 text-balance break-words text-2xl font-bold tracking-tight sm:text-4xl", children: title }),
|
|
6942
|
+
/* @__PURE__ */ jsxRuntime.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 })
|
|
6514
6943
|
] }),
|
|
6515
|
-
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-
|
|
6944
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-7 grid gap-2.5 sm:grid-cols-3", children: CAPABILITIES.map(({ icon: Icon, title: capTitle, body: capBody }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6516
6945
|
"li",
|
|
6517
6946
|
{
|
|
6518
|
-
className: "rounded-xl border border-border bg-card p-
|
|
6947
|
+
className: "rounded-xl border border-border/70 bg-card/60 p-3.5 text-left transition-colors hover:border-border",
|
|
6519
6948
|
children: [
|
|
6520
6949
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-2.5 flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4" }) }),
|
|
6521
6950
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-card-foreground", children: capTitle }),
|
|
@@ -6524,72 +6953,27 @@ function WelcomeState({
|
|
|
6524
6953
|
},
|
|
6525
6954
|
capTitle
|
|
6526
6955
|
)) }),
|
|
6527
|
-
prompts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-
|
|
6528
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2
|
|
6529
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6530
|
-
|
|
6956
|
+
prompts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8", children: [
|
|
6957
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Try asking" }),
|
|
6958
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6959
|
+
PromptCarousel,
|
|
6531
6960
|
{
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
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",
|
|
6538
|
-
children: [
|
|
6539
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: prompt }),
|
|
6540
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3 w-3 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
|
|
6541
|
-
]
|
|
6542
|
-
},
|
|
6543
|
-
prompt
|
|
6544
|
-
)) })
|
|
6961
|
+
prompts,
|
|
6962
|
+
onPrompt,
|
|
6963
|
+
disabled
|
|
6964
|
+
}
|
|
6965
|
+
)
|
|
6545
6966
|
] }) : null,
|
|
6546
|
-
products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-
|
|
6547
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2
|
|
6548
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
"aria-pressed": focusProduct === null,
|
|
6557
|
-
onClick: () => onFocus(null),
|
|
6558
|
-
className: cn(
|
|
6559
|
-
"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",
|
|
6560
|
-
focusProduct === null ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
6561
|
-
),
|
|
6562
|
-
children: "All products"
|
|
6563
|
-
}
|
|
6564
|
-
),
|
|
6565
|
-
visibleProducts.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6566
|
-
"button",
|
|
6567
|
-
{
|
|
6568
|
-
"data-boff-explore": "chip",
|
|
6569
|
-
"data-chip-kind": "product",
|
|
6570
|
-
"data-product": product.slug,
|
|
6571
|
-
type: "button",
|
|
6572
|
-
"aria-pressed": focusProduct === product.slug,
|
|
6573
|
-
title: product.tagline ?? product.name,
|
|
6574
|
-
onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
|
|
6575
|
-
className: cn(
|
|
6576
|
-
"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",
|
|
6577
|
-
focusProduct === product.slug ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
6578
|
-
),
|
|
6579
|
-
children: product.name
|
|
6580
|
-
},
|
|
6581
|
-
product.slug
|
|
6582
|
-
)),
|
|
6583
|
-
products.length > PRODUCT_CHIP_PREVIEW ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6584
|
-
"button",
|
|
6585
|
-
{
|
|
6586
|
-
type: "button",
|
|
6587
|
-
onClick: () => setShowAllProducts((v) => !v),
|
|
6588
|
-
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",
|
|
6589
|
-
children: showAllProducts ? "Show fewer" : `Show all ${products.length}`
|
|
6590
|
-
}
|
|
6591
|
-
) : null
|
|
6592
|
-
] })
|
|
6967
|
+
products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6", children: [
|
|
6968
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Focus on a product" }),
|
|
6969
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6970
|
+
ProductStrip,
|
|
6971
|
+
{
|
|
6972
|
+
products,
|
|
6973
|
+
focusProduct,
|
|
6974
|
+
onFocus
|
|
6975
|
+
}
|
|
6976
|
+
)
|
|
6593
6977
|
] }) : null
|
|
6594
6978
|
] });
|
|
6595
6979
|
}
|
|
@@ -6626,7 +7010,8 @@ function ExplorePage({
|
|
|
6626
7010
|
history,
|
|
6627
7011
|
openConversation,
|
|
6628
7012
|
deleteConversation,
|
|
6629
|
-
clearHistory
|
|
7013
|
+
clearHistory,
|
|
7014
|
+
recordClick
|
|
6630
7015
|
} = useExploreChat({ productSlug, pageUrl, onSend, examplePrompts });
|
|
6631
7016
|
const [draft, setDraft] = React.useState("");
|
|
6632
7017
|
const textareaRef = React.useRef(null);
|
|
@@ -6645,6 +7030,7 @@ function ExplorePage({
|
|
|
6645
7030
|
}
|
|
6646
7031
|
});
|
|
6647
7032
|
speechStopRef.current = speech.stop;
|
|
7033
|
+
const [disclaimerOpen, setDisclaimerOpen] = React.useState(false);
|
|
6648
7034
|
const scrollRef = React.useRef(null);
|
|
6649
7035
|
const latestAssistantRef = React.useRef(null);
|
|
6650
7036
|
const alignedForRef = React.useRef(null);
|
|
@@ -6737,7 +7123,7 @@ function ExplorePage({
|
|
|
6737
7123
|
"data-boff-explore": "page",
|
|
6738
7124
|
"data-phase": phase,
|
|
6739
7125
|
className: cn(
|
|
6740
|
-
"flex w-full flex-col bg-background text-foreground",
|
|
7126
|
+
"flex w-full min-w-0 flex-col overflow-x-hidden bg-background text-foreground",
|
|
6741
7127
|
heightMode === "auto" && "min-h-[70vh]",
|
|
6742
7128
|
className
|
|
6743
7129
|
),
|
|
@@ -6879,7 +7265,7 @@ function ExplorePage({
|
|
|
6879
7265
|
role: "log",
|
|
6880
7266
|
"aria-live": "polite",
|
|
6881
7267
|
"aria-label": "Explore conversation",
|
|
6882
|
-
className: "boff-explore-scroll min-h-0 flex-1 overflow-y-auto",
|
|
7268
|
+
className: "boff-explore-scroll min-h-0 min-w-0 flex-1 overflow-y-auto overflow-x-hidden",
|
|
6883
7269
|
children: showWelcome ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6884
7270
|
WelcomeState,
|
|
6885
7271
|
{
|
|
@@ -6892,21 +7278,31 @@ function ExplorePage({
|
|
|
6892
7278
|
onFocus: setFocusProduct,
|
|
6893
7279
|
disabled: !canSend || busy
|
|
6894
7280
|
}
|
|
6895
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full max-w-3xl space-y-6 px-4 py-6", children: messages.map(
|
|
7281
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full min-w-0 max-w-3xl space-y-6 px-4 py-6", children: messages.map(
|
|
6896
7282
|
(message) => message.role === "USER" ? /* @__PURE__ */ jsxRuntime.jsx(UserBubble, { message }, message.id) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
6897
7283
|
AssistantBubble,
|
|
6898
7284
|
{
|
|
6899
7285
|
message,
|
|
6900
7286
|
activity,
|
|
6901
7287
|
onNavigate,
|
|
6902
|
-
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0
|
|
7288
|
+
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
|
|
7289
|
+
onFollow: (kind, url) => recordClick(message.id, kind, url),
|
|
7290
|
+
onFollowUp: sendPrompt
|
|
6903
7291
|
},
|
|
6904
7292
|
message.id
|
|
6905
7293
|
)
|
|
6906
7294
|
) })
|
|
6907
7295
|
}
|
|
6908
7296
|
),
|
|
6909
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7297
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7298
|
+
DisclaimerDialog,
|
|
7299
|
+
{
|
|
7300
|
+
open: disclaimerOpen,
|
|
7301
|
+
onClose: () => setDisclaimerOpen(false),
|
|
7302
|
+
captchaOn
|
|
7303
|
+
}
|
|
7304
|
+
),
|
|
7305
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(
|
|
6910
7306
|
"form",
|
|
6911
7307
|
{
|
|
6912
7308
|
"data-boff-explore": "composer",
|
|
@@ -6921,7 +7317,7 @@ function ExplorePage({
|
|
|
6921
7317
|
"div",
|
|
6922
7318
|
{
|
|
6923
7319
|
className: cn(
|
|
6924
|
-
"flex items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-
|
|
7320
|
+
"flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
|
|
6925
7321
|
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
|
|
6926
7322
|
),
|
|
6927
7323
|
children: [
|
|
@@ -6942,7 +7338,7 @@ function ExplorePage({
|
|
|
6942
7338
|
},
|
|
6943
7339
|
"aria-label": "Ask a question",
|
|
6944
7340
|
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",
|
|
6945
|
-
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"
|
|
7341
|
+
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"
|
|
6946
7342
|
}
|
|
6947
7343
|
),
|
|
6948
7344
|
speech.supported ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -7033,48 +7429,19 @@ function ExplorePage({
|
|
|
7033
7429
|
}
|
|
7034
7430
|
)
|
|
7035
7431
|
] }),
|
|
7036
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7037
|
-
"
|
|
7038
|
-
|
|
7039
|
-
"
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
"
|
|
7043
|
-
|
|
7044
|
-
"
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7050
|
-
"a",
|
|
7051
|
-
{
|
|
7052
|
-
href: "https://policies.google.com/privacy",
|
|
7053
|
-
target: "_blank",
|
|
7054
|
-
rel: "noopener noreferrer",
|
|
7055
|
-
className: "underline underline-offset-2 hover:text-foreground",
|
|
7056
|
-
children: "Privacy Policy"
|
|
7057
|
-
}
|
|
7058
|
-
),
|
|
7059
|
-
" ",
|
|
7060
|
-
"and",
|
|
7061
|
-
" ",
|
|
7062
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7063
|
-
"a",
|
|
7064
|
-
{
|
|
7065
|
-
href: "https://policies.google.com/terms",
|
|
7066
|
-
target: "_blank",
|
|
7067
|
-
rel: "noopener noreferrer",
|
|
7068
|
-
className: "underline underline-offset-2 hover:text-foreground",
|
|
7069
|
-
children: "Terms of Service"
|
|
7070
|
-
}
|
|
7071
|
-
),
|
|
7072
|
-
" ",
|
|
7073
|
-
"apply."
|
|
7074
|
-
] }) : null
|
|
7075
|
-
]
|
|
7076
|
-
}
|
|
7077
|
-
)
|
|
7432
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
7433
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: "Grounded in our docs \u2014 check anything important." }),
|
|
7434
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7435
|
+
"button",
|
|
7436
|
+
{
|
|
7437
|
+
type: "button",
|
|
7438
|
+
"data-boff-explore": "disclaimer",
|
|
7439
|
+
onClick: () => setDisclaimerOpen(true),
|
|
7440
|
+
className: "shrink-0 underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
7441
|
+
children: "Disclaimer"
|
|
7442
|
+
}
|
|
7443
|
+
)
|
|
7444
|
+
] })
|
|
7078
7445
|
]
|
|
7079
7446
|
}
|
|
7080
7447
|
) })
|