@burdenoff/website-sdk 2026.828.5 → 2026.828.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/explore.d.mts +11 -1
- package/dist/components/explore.d.ts +11 -1
- package/dist/components/explore.js +175 -57
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +175 -58
- package/dist/components/explore.mjs.map +1 -1
- package/dist/hooks/index.js +104 -41
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +104 -41
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.js +174 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +174 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5963,11 +5963,40 @@ function getRecognitionCtor() {
|
|
|
5963
5963
|
const w = window;
|
|
5964
5964
|
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
5965
5965
|
}
|
|
5966
|
+
var MIC_DENIED_MESSAGE = "Microphone access was blocked. Allow it in your browser's site settings to dictate.";
|
|
5967
|
+
async function ensureMicrophoneAccess() {
|
|
5968
|
+
const media = typeof navigator === "undefined" ? void 0 : navigator.mediaDevices;
|
|
5969
|
+
if (!media?.getUserMedia) return { ok: true, confirmed: false };
|
|
5970
|
+
try {
|
|
5971
|
+
const status = await navigator.permissions?.query({
|
|
5972
|
+
name: "microphone"
|
|
5973
|
+
});
|
|
5974
|
+
if (status?.state === "granted") return { ok: true, confirmed: true };
|
|
5975
|
+
} catch {
|
|
5976
|
+
}
|
|
5977
|
+
try {
|
|
5978
|
+
const stream = await media.getUserMedia({ audio: true });
|
|
5979
|
+
for (const track of stream.getTracks()) track.stop();
|
|
5980
|
+
return { ok: true, confirmed: true };
|
|
5981
|
+
} catch (error) {
|
|
5982
|
+
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
5983
|
+
if (name === "NotAllowedError" || name === "SecurityError") {
|
|
5984
|
+
return { ok: false, message: MIC_DENIED_MESSAGE };
|
|
5985
|
+
}
|
|
5986
|
+
if (name === "NotFoundError" || name === "DevicesNotFoundError") {
|
|
5987
|
+
return { ok: false, message: "No microphone was found." };
|
|
5988
|
+
}
|
|
5989
|
+
return {
|
|
5990
|
+
ok: false,
|
|
5991
|
+
message: "Dictation could not start. You can type instead."
|
|
5992
|
+
};
|
|
5993
|
+
}
|
|
5994
|
+
}
|
|
5966
5995
|
function describeError(code) {
|
|
5967
5996
|
switch (code) {
|
|
5968
5997
|
case "not-allowed":
|
|
5969
5998
|
case "service-not-allowed":
|
|
5970
|
-
return
|
|
5999
|
+
return MIC_DENIED_MESSAGE;
|
|
5971
6000
|
case "no-speech":
|
|
5972
6001
|
return "I didn't catch anything \u2014 try again a little closer to the mic.";
|
|
5973
6002
|
case "audio-capture":
|
|
@@ -5990,11 +6019,23 @@ function useSpeechInput({
|
|
|
5990
6019
|
const [interim, setInterim] = useState("");
|
|
5991
6020
|
const [error, setError] = useState(null);
|
|
5992
6021
|
const recognitionRef = useRef(null);
|
|
6022
|
+
const startTokenRef = useRef(0);
|
|
6023
|
+
const micConfirmedRef = useRef(false);
|
|
6024
|
+
const beginRecognitionRef = useRef(null);
|
|
6025
|
+
const beginRecognition = useCallback(
|
|
6026
|
+
(Ctor, token) => {
|
|
6027
|
+
beginRecognitionRef.current?.(Ctor, token);
|
|
6028
|
+
},
|
|
6029
|
+
[]
|
|
6030
|
+
);
|
|
5993
6031
|
const finalRef = useRef(onFinalTranscript);
|
|
5994
6032
|
const errorRef = useRef(onError);
|
|
5995
6033
|
finalRef.current = onFinalTranscript;
|
|
5996
6034
|
errorRef.current = onError;
|
|
5997
6035
|
const stop = useCallback(() => {
|
|
6036
|
+
startTokenRef.current += 1;
|
|
6037
|
+
setListening(false);
|
|
6038
|
+
setInterim("");
|
|
5998
6039
|
const recognition = recognitionRef.current;
|
|
5999
6040
|
if (!recognition) return;
|
|
6000
6041
|
try {
|
|
@@ -6008,48 +6049,70 @@ function useSpeechInput({
|
|
|
6008
6049
|
const Ctor = getRecognitionCtor();
|
|
6009
6050
|
if (!Ctor) return;
|
|
6010
6051
|
if (recognitionRef.current) stop();
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
6024
|
-
const result = event.results[i];
|
|
6025
|
-
if (!result) continue;
|
|
6026
|
-
const text = result[0]?.transcript ?? "";
|
|
6027
|
-
if (result.isFinal) settled += text;
|
|
6028
|
-
else pending += text;
|
|
6052
|
+
setError(null);
|
|
6053
|
+
setListening(true);
|
|
6054
|
+
startTokenRef.current += 1;
|
|
6055
|
+
const token = startTokenRef.current;
|
|
6056
|
+
void ensureMicrophoneAccess().then((access) => {
|
|
6057
|
+
if (token !== startTokenRef.current) return;
|
|
6058
|
+
micConfirmedRef.current = access.ok && access.confirmed;
|
|
6059
|
+
if (!access.ok) {
|
|
6060
|
+
setListening(false);
|
|
6061
|
+
setError(access.message);
|
|
6062
|
+
errorRef.current?.(access.message);
|
|
6063
|
+
return;
|
|
6029
6064
|
}
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6065
|
+
beginRecognition(Ctor, token);
|
|
6066
|
+
});
|
|
6067
|
+
}, [beginRecognition, stop]);
|
|
6068
|
+
const beginRecognitionImpl = useCallback(
|
|
6069
|
+
(Ctor, token) => {
|
|
6070
|
+
const recognition = new Ctor();
|
|
6071
|
+
recognition.lang = lang ?? (typeof navigator === "undefined" ? "en-US" : navigator.language || "en-US");
|
|
6072
|
+
recognition.continuous = true;
|
|
6073
|
+
recognition.interimResults = true;
|
|
6074
|
+
recognition.maxAlternatives = 1;
|
|
6075
|
+
recognition.onstart = () => {
|
|
6076
|
+
if (token !== startTokenRef.current) return;
|
|
6077
|
+
setError(null);
|
|
6078
|
+
setListening(true);
|
|
6079
|
+
};
|
|
6080
|
+
recognition.onresult = (event) => {
|
|
6081
|
+
let settled = "";
|
|
6082
|
+
let pending = "";
|
|
6083
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
6084
|
+
const result = event.results[i];
|
|
6085
|
+
if (!result) continue;
|
|
6086
|
+
const text = result[0]?.transcript ?? "";
|
|
6087
|
+
if (result.isFinal) settled += text;
|
|
6088
|
+
else pending += text;
|
|
6089
|
+
}
|
|
6090
|
+
setInterim(pending);
|
|
6091
|
+
if (settled.trim() !== "") finalRef.current(settled);
|
|
6092
|
+
};
|
|
6093
|
+
recognition.onerror = (event) => {
|
|
6094
|
+
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);
|
|
6095
|
+
setListening(false);
|
|
6096
|
+
setInterim("");
|
|
6097
|
+
if (message !== "") {
|
|
6098
|
+
setError(message);
|
|
6099
|
+
errorRef.current?.(message);
|
|
6100
|
+
}
|
|
6101
|
+
};
|
|
6102
|
+
recognition.onend = () => {
|
|
6103
|
+
setListening(false);
|
|
6104
|
+
setInterim("");
|
|
6105
|
+
};
|
|
6106
|
+
recognitionRef.current = recognition;
|
|
6107
|
+
try {
|
|
6108
|
+
recognition.start();
|
|
6109
|
+
} catch {
|
|
6110
|
+
setListening(false);
|
|
6040
6111
|
}
|
|
6041
|
-
}
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
};
|
|
6046
|
-
recognitionRef.current = recognition;
|
|
6047
|
-
try {
|
|
6048
|
-
recognition.start();
|
|
6049
|
-
} catch {
|
|
6050
|
-
setListening(false);
|
|
6051
|
-
}
|
|
6052
|
-
}, [lang, stop]);
|
|
6112
|
+
},
|
|
6113
|
+
[lang]
|
|
6114
|
+
);
|
|
6115
|
+
beginRecognitionRef.current = beginRecognitionImpl;
|
|
6053
6116
|
const toggle = useCallback(() => {
|
|
6054
6117
|
if (listening) stop();
|
|
6055
6118
|
else start();
|
|
@@ -6165,10 +6228,33 @@ function initialOf(value, fallback) {
|
|
|
6165
6228
|
const source = (value || fallback).trim();
|
|
6166
6229
|
return source ? source.slice(0, 1).toUpperCase() : "?";
|
|
6167
6230
|
}
|
|
6231
|
+
function logoUrlOf(url) {
|
|
6232
|
+
try {
|
|
6233
|
+
const { origin, protocol } = new URL(url);
|
|
6234
|
+
if (protocol !== "https:" && protocol !== "http:") return null;
|
|
6235
|
+
return `${origin}/favicon.svg`;
|
|
6236
|
+
} catch {
|
|
6237
|
+
return null;
|
|
6238
|
+
}
|
|
6239
|
+
}
|
|
6168
6240
|
function ReferenceCard({ reference }) {
|
|
6169
6241
|
const [imageFailed, setImageFailed] = useState(false);
|
|
6242
|
+
const [logoFailed, setLogoFailed] = useState(false);
|
|
6170
6243
|
const host = hostnameOf(reference.url);
|
|
6244
|
+
const logoUrl = logoUrlOf(reference.url);
|
|
6171
6245
|
const showImage = Boolean(reference.imageUrl) && !imageFailed;
|
|
6246
|
+
const showLogo = Boolean(logoUrl) && !logoFailed;
|
|
6247
|
+
const letterPlate = /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center bg-gradient-to-br from-primary/25 via-primary/5 to-secondary", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-primary/70 sm:text-2xl", children: initialOf(reference.product, reference.title || host) }) });
|
|
6248
|
+
const logoImg = showLogo ? /* @__PURE__ */ jsx(
|
|
6249
|
+
"img",
|
|
6250
|
+
{
|
|
6251
|
+
src: logoUrl ?? "",
|
|
6252
|
+
alt: "",
|
|
6253
|
+
loading: "lazy",
|
|
6254
|
+
onError: () => setLogoFailed(true),
|
|
6255
|
+
className: "h-full w-full object-contain p-1.5 sm:p-0"
|
|
6256
|
+
}
|
|
6257
|
+
) : letterPlate;
|
|
6172
6258
|
return /* @__PURE__ */ jsxs(
|
|
6173
6259
|
"a",
|
|
6174
6260
|
{
|
|
@@ -6176,9 +6262,10 @@ function ReferenceCard({ reference }) {
|
|
|
6176
6262
|
href: reference.url,
|
|
6177
6263
|
target: "_blank",
|
|
6178
6264
|
rel: "noopener noreferrer",
|
|
6179
|
-
className: "group flex flex-
|
|
6265
|
+
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",
|
|
6180
6266
|
children: [
|
|
6181
|
-
/* @__PURE__ */ jsx("div", { className: "relative
|
|
6267
|
+
/* @__PURE__ */ jsx("div", { className: "relative h-11 w-11 shrink-0 overflow-hidden rounded-lg bg-muted sm:hidden", children: logoImg }),
|
|
6268
|
+
/* @__PURE__ */ jsx("div", { className: "relative hidden aspect-[16/9] w-full overflow-hidden bg-muted sm:block", children: showImage ? /* @__PURE__ */ jsx(
|
|
6182
6269
|
"img",
|
|
6183
6270
|
{
|
|
6184
6271
|
src: reference.imageUrl ?? "",
|
|
@@ -6187,17 +6274,22 @@ function ReferenceCard({ reference }) {
|
|
|
6187
6274
|
onError: () => setImageFailed(true),
|
|
6188
6275
|
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
6189
6276
|
}
|
|
6190
|
-
) : (
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6277
|
+
) : showLogo ? /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center bg-gradient-to-br from-primary/15 via-transparent to-secondary/60 p-6", children: /* @__PURE__ */ jsx(
|
|
6278
|
+
"img",
|
|
6279
|
+
{
|
|
6280
|
+
src: logoUrl ?? "",
|
|
6281
|
+
alt: "",
|
|
6282
|
+
loading: "lazy",
|
|
6283
|
+
onError: () => setLogoFailed(true),
|
|
6284
|
+
className: "max-h-full max-w-full object-contain"
|
|
6285
|
+
}
|
|
6286
|
+
) }) : letterPlate }),
|
|
6287
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 sm:gap-1.5 sm:p-3", children: [
|
|
6196
6288
|
/* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: reference.title || host || reference.url }),
|
|
6197
|
-
reference.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground", children: reference.description }) : null,
|
|
6198
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
6199
|
-
reference.product ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-secondary-foreground", children: reference.product }) : null,
|
|
6200
|
-
/* @__PURE__ */ jsxs("span", { className: "
|
|
6289
|
+
reference.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground max-sm:hidden", children: reference.description }) : null,
|
|
6290
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 sm:mt-auto sm:pt-2", children: [
|
|
6291
|
+
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,
|
|
6292
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground sm:ml-auto", children: [
|
|
6201
6293
|
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3 shrink-0" }),
|
|
6202
6294
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: host })
|
|
6203
6295
|
] })
|
|
@@ -6283,7 +6375,8 @@ function UserBubble({ message }) {
|
|
|
6283
6375
|
function AssistantBubble({
|
|
6284
6376
|
message,
|
|
6285
6377
|
activity,
|
|
6286
|
-
onNavigate
|
|
6378
|
+
onNavigate,
|
|
6379
|
+
anchorRef
|
|
6287
6380
|
}) {
|
|
6288
6381
|
const streaming = message.status === "PENDING" || message.status === "RUNNING";
|
|
6289
6382
|
const body = message.content || message.partialContent || "";
|
|
@@ -6291,9 +6384,10 @@ function AssistantBubble({
|
|
|
6291
6384
|
return /* @__PURE__ */ jsxs(
|
|
6292
6385
|
"div",
|
|
6293
6386
|
{
|
|
6387
|
+
ref: anchorRef,
|
|
6294
6388
|
"data-boff-explore": "assistant",
|
|
6295
6389
|
"data-status": message.status,
|
|
6296
|
-
className: "flex gap-3",
|
|
6390
|
+
className: "flex scroll-mt-4 gap-3",
|
|
6297
6391
|
children: [
|
|
6298
6392
|
/* @__PURE__ */ jsx(
|
|
6299
6393
|
"span",
|
|
@@ -6522,6 +6616,8 @@ function ExplorePage({
|
|
|
6522
6616
|
});
|
|
6523
6617
|
speechStopRef.current = speech.stop;
|
|
6524
6618
|
const scrollRef = useRef(null);
|
|
6619
|
+
const latestAssistantRef = useRef(null);
|
|
6620
|
+
const alignedForRef = useRef(null);
|
|
6525
6621
|
const stickToBottomRef = useRef(true);
|
|
6526
6622
|
const composingRef = useRef(false);
|
|
6527
6623
|
const busy = phase === "sending" || phase === "streaming";
|
|
@@ -6547,12 +6643,32 @@ function ExplorePage({
|
|
|
6547
6643
|
if (!el) return;
|
|
6548
6644
|
stickToBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
|
|
6549
6645
|
}, []);
|
|
6646
|
+
const latestAssistantId = useMemo(() => {
|
|
6647
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
6648
|
+
const message = messages[i];
|
|
6649
|
+
if (message && message.role === "ASSISTANT") return message.id;
|
|
6650
|
+
}
|
|
6651
|
+
return null;
|
|
6652
|
+
}, [messages]);
|
|
6653
|
+
useEffect(() => {
|
|
6654
|
+
const el = scrollRef.current;
|
|
6655
|
+
const anchor = latestAssistantRef.current;
|
|
6656
|
+
if (!el || !anchor || !latestAssistantId) return;
|
|
6657
|
+
if (alignedForRef.current === latestAssistantId) return;
|
|
6658
|
+
if (!stickToBottomRef.current) return;
|
|
6659
|
+
if (anchor.offsetHeight === 0) return;
|
|
6660
|
+
alignedForRef.current = latestAssistantId;
|
|
6661
|
+
const delta = anchor.getBoundingClientRect().top - el.getBoundingClientRect().top;
|
|
6662
|
+
el.scrollTop = Math.max(0, el.scrollTop + delta - 12);
|
|
6663
|
+
}, [latestAssistantId, messages]);
|
|
6550
6664
|
useEffect(() => {
|
|
6551
6665
|
if (!stickToBottomRef.current) return;
|
|
6666
|
+
if (latestAssistantId && alignedForRef.current === latestAssistantId)
|
|
6667
|
+
return;
|
|
6552
6668
|
const el = scrollRef.current;
|
|
6553
6669
|
if (!el) return;
|
|
6554
6670
|
el.scrollTop = el.scrollHeight;
|
|
6555
|
-
}, [
|
|
6671
|
+
}, [activity, latestAssistantId, messages]);
|
|
6556
6672
|
const submitDraft = useCallback(() => {
|
|
6557
6673
|
const text = draft.trim();
|
|
6558
6674
|
if (!text || overLimit || busy || !canSend) return;
|
|
@@ -6752,7 +6868,8 @@ function ExplorePage({
|
|
|
6752
6868
|
{
|
|
6753
6869
|
message,
|
|
6754
6870
|
activity,
|
|
6755
|
-
onNavigate
|
|
6871
|
+
onNavigate,
|
|
6872
|
+
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0
|
|
6756
6873
|
},
|
|
6757
6874
|
message.id
|
|
6758
6875
|
)
|