@burdenoff/website-sdk 2026.924.10 → 2026.924.12
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 +177 -42
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +177 -42
- 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 +56 -7
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +56 -7
- 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 +177 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +177 -42
- package/dist/index.mjs.map +1 -1
- package/dist/{use-progressive-reveal-Cmy8bA7L.d.mts → use-progressive-reveal-BBLRAnbU.d.mts} +6 -1
- package/dist/{use-progressive-reveal-DHPGGxZu.d.ts → use-progressive-reveal-myNKWb6S.d.ts} +6 -1
- package/package.json +1 -1
|
@@ -2059,6 +2059,45 @@ function buildFallbackCatalog(examplePrompts, welcome) {
|
|
|
2059
2059
|
conceptHighlights: []
|
|
2060
2060
|
};
|
|
2061
2061
|
}
|
|
2062
|
+
var BAKED_CATALOG_ELEMENT_ID = "boff-explore-catalog";
|
|
2063
|
+
function bakedCatalogLocale() {
|
|
2064
|
+
if (typeof navigator === "undefined") return "";
|
|
2065
|
+
return (navigator.language || "").toLowerCase().split("-")[0] ?? "";
|
|
2066
|
+
}
|
|
2067
|
+
function snapshotBakedElement() {
|
|
2068
|
+
if (typeof document === "undefined") return null;
|
|
2069
|
+
const el = document.getElementById(BAKED_CATALOG_ELEMENT_ID);
|
|
2070
|
+
if (!el || el.tagName !== "SCRIPT" || el.getAttribute("type") !== "application/json")
|
|
2071
|
+
return null;
|
|
2072
|
+
return {
|
|
2073
|
+
product: el.getAttribute("data-product") ?? "",
|
|
2074
|
+
locale: el.getAttribute("data-locale") ?? "",
|
|
2075
|
+
text: el.textContent ?? ""
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
var BAKED_AT_LOAD = snapshotBakedElement();
|
|
2079
|
+
function isCatalogShape(value) {
|
|
2080
|
+
if (!value || typeof value !== "object") return false;
|
|
2081
|
+
const c = value;
|
|
2082
|
+
const listOrAbsent = (v) => v === void 0 || v === null || Array.isArray(v);
|
|
2083
|
+
const textOrAbsent = (v) => v === void 0 || v === null || typeof v === "string";
|
|
2084
|
+
return (c["enabled"] === void 0 || typeof c["enabled"] === "boolean") && listOrAbsent(c["products"]) && listOrAbsent(c["examplePrompts"]) && listOrAbsent(c["conceptHighlights"]) && textOrAbsent(c["welcomeTitle"]) && textOrAbsent(c["welcomeBody"]);
|
|
2085
|
+
}
|
|
2086
|
+
function readBakedCatalog(productSlug) {
|
|
2087
|
+
const baked = snapshotBakedElement() ?? BAKED_AT_LOAD;
|
|
2088
|
+
if (!baked) return null;
|
|
2089
|
+
if (baked.product !== (productSlug ?? "")) return null;
|
|
2090
|
+
if (baked.locale && baked.locale !== bakedCatalogLocale()) return null;
|
|
2091
|
+
try {
|
|
2092
|
+
const parsed = JSON.parse(baked.text);
|
|
2093
|
+
return isCatalogShape(parsed) ? parsed : null;
|
|
2094
|
+
} catch {
|
|
2095
|
+
return null;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
function serializeBakedCatalog(raw) {
|
|
2099
|
+
return JSON.stringify(raw).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
2100
|
+
}
|
|
2062
2101
|
function normalizeCatalog(raw, fallback) {
|
|
2063
2102
|
return {
|
|
2064
2103
|
enabled: raw.enabled !== false,
|
|
@@ -2308,9 +2347,17 @@ function useExploreChat(options = {}) {
|
|
|
2308
2347
|
),
|
|
2309
2348
|
[promptKey, welcomeKey]
|
|
2310
2349
|
);
|
|
2311
|
-
const [
|
|
2350
|
+
const [bakedRaw] = React.useState(
|
|
2351
|
+
() => readBakedCatalog(productSlug)
|
|
2352
|
+
);
|
|
2353
|
+
const [catalog, setCatalog] = React.useState(
|
|
2354
|
+
() => bakedRaw ? normalizeCatalog(bakedRaw, fallbackCatalog) : fallbackCatalog
|
|
2355
|
+
);
|
|
2356
|
+
const [serverCatalogRaw, setServerCatalogRaw] = React.useState(bakedRaw);
|
|
2312
2357
|
const [messages, setMessages] = React.useState([]);
|
|
2313
|
-
const [phase, setPhase] = React.useState(
|
|
2358
|
+
const [phase, setPhase] = React.useState(
|
|
2359
|
+
() => bakedRaw ? bakedRaw.enabled === false ? "disabled" : "ready" : "loading"
|
|
2360
|
+
);
|
|
2314
2361
|
const [error, setError] = React.useState(null);
|
|
2315
2362
|
const [remainingToday, setRemainingToday] = React.useState(null);
|
|
2316
2363
|
const [focusProduct, setFocusProductState] = React.useState(null);
|
|
@@ -2328,7 +2375,7 @@ function useExploreChat(options = {}) {
|
|
|
2328
2375
|
shareTokenRef.current = readShareToken();
|
|
2329
2376
|
}
|
|
2330
2377
|
const isSharedView = shareTokenRef.current !== null;
|
|
2331
|
-
const catalogRef = React.useRef(
|
|
2378
|
+
const catalogRef = React.useRef(catalog);
|
|
2332
2379
|
const turnCountRef = React.useRef(0);
|
|
2333
2380
|
const focusProductRef = React.useRef(null);
|
|
2334
2381
|
const lastSentTextRef = React.useRef(null);
|
|
@@ -2474,25 +2521,29 @@ function useExploreChat(options = {}) {
|
|
|
2474
2521
|
const raw = res.data?.exploreCatalog;
|
|
2475
2522
|
if (res.errors?.length || !raw) {
|
|
2476
2523
|
console.warn(
|
|
2477
|
-
"[WebSDK] exploreCatalog unavailable \u2014
|
|
2524
|
+
"[WebSDK] exploreCatalog unavailable \u2014 keeping the catalog the page started with",
|
|
2478
2525
|
res.errors?.[0]?.message
|
|
2479
2526
|
);
|
|
2480
2527
|
setPhase((prev) => prev === "loading" ? "ready" : prev);
|
|
2481
2528
|
return;
|
|
2482
2529
|
}
|
|
2483
|
-
const next = normalizeCatalog(
|
|
2530
|
+
const next = normalizeCatalog(
|
|
2531
|
+
raw,
|
|
2532
|
+
bakedRaw ? normalizeCatalog(bakedRaw, fallbackCatalog) : fallbackCatalog
|
|
2533
|
+
);
|
|
2484
2534
|
setCatalog(next);
|
|
2535
|
+
setServerCatalogRaw(raw);
|
|
2485
2536
|
catalogRef.current = next;
|
|
2486
2537
|
setPhase((prev) => {
|
|
2487
2538
|
if (!next.enabled) return "disabled";
|
|
2488
|
-
return prev === "loading" ? "ready" : prev;
|
|
2539
|
+
return prev === "loading" || prev === "disabled" ? "ready" : prev;
|
|
2489
2540
|
});
|
|
2490
2541
|
};
|
|
2491
2542
|
void load();
|
|
2492
2543
|
return () => {
|
|
2493
2544
|
cancelled = true;
|
|
2494
2545
|
};
|
|
2495
|
-
}, [client, productSlug, fallbackCatalog]);
|
|
2546
|
+
}, [client, productSlug, fallbackCatalog, bakedRaw]);
|
|
2496
2547
|
const startPolling = React.useCallback(
|
|
2497
2548
|
(conversationToken, messageId) => {
|
|
2498
2549
|
pollGenerationRef.current += 1;
|
|
@@ -2960,6 +3011,7 @@ function useExploreChat(options = {}) {
|
|
|
2960
3011
|
return {
|
|
2961
3012
|
phase,
|
|
2962
3013
|
catalog,
|
|
3014
|
+
serverCatalogRaw,
|
|
2963
3015
|
messages,
|
|
2964
3016
|
activity,
|
|
2965
3017
|
remainingToday,
|
|
@@ -3918,6 +3970,48 @@ var EXPLORE_CSS = `
|
|
|
3918
3970
|
}
|
|
3919
3971
|
.boff-explore-strip::-webkit-scrollbar { display: none; }
|
|
3920
3972
|
|
|
3973
|
+
/* The product chips run past the edge of their row (there are 38). A hard edge sliced the
|
|
3974
|
+
last visible chip mid-word ("Bi", "BigC") right beside "All 38"; a short fade says "there
|
|
3975
|
+
is more this way" instead. A mask carries only alpha, so the gradient's colour is a token
|
|
3976
|
+
that never shows. Mirrored for right-to-left locales, whose row runs the other way. */
|
|
3977
|
+
.boff-explore-chips-fade {
|
|
3978
|
+
/* Focus scrolling honours scroll-padding, so a tabbed-to chip is brought clear of the fade
|
|
3979
|
+
rather than left half-hidden under it. */
|
|
3980
|
+
scroll-padding-inline-end: 2rem;
|
|
3981
|
+
-webkit-mask-image: linear-gradient(to right, hsl(var(--foreground)) calc(100% - 2rem), hsl(var(--foreground) / 0));
|
|
3982
|
+
mask-image: linear-gradient(to right, hsl(var(--foreground)) calc(100% - 2rem), hsl(var(--foreground) / 0));
|
|
3983
|
+
}
|
|
3984
|
+
/* A fade over a fixed edge would leave the LAST chip under it once the row is scrolled to
|
|
3985
|
+
the end \u2014 the visitor could never read it. A trailing spacer the width of the fade lets
|
|
3986
|
+
the final chip scroll clear of it. A pseudo-element rather than padding: trailing padding
|
|
3987
|
+
on a flex scroll container is not reliably part of its scrollable overflow. */
|
|
3988
|
+
.boff-explore-chips-fade::after {
|
|
3989
|
+
content: "";
|
|
3990
|
+
flex: 0 0 2rem;
|
|
3991
|
+
}
|
|
3992
|
+
[dir="rtl"] .boff-explore-chips-fade {
|
|
3993
|
+
-webkit-mask-image: linear-gradient(to left, hsl(var(--foreground)) calc(100% - 2rem), hsl(var(--foreground) / 0));
|
|
3994
|
+
mask-image: linear-gradient(to left, hsl(var(--foreground)) calc(100% - 2rem), hsl(var(--foreground) / 0));
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3997
|
+
/* The concept rail had the chip row's problem: the card at its edge was cut hard mid-word
|
|
3998
|
+
("Apparatus Rig", "CONCEPT ILLUST") on a dozen sites. It fades an edge only while there are
|
|
3999
|
+
more cards beyond it \u2014 the same state that lights the arrow on that side \u2014 so a rail
|
|
4000
|
+
scrolled to its end shows its last card whole. Physical edges, because that state is
|
|
4001
|
+
physical: it is measured from the left in either reading direction. */
|
|
4002
|
+
.boff-explore-rail-fade-left {
|
|
4003
|
+
-webkit-mask-image: linear-gradient(to right, hsl(var(--foreground) / 0), hsl(var(--foreground)) 3rem);
|
|
4004
|
+
mask-image: linear-gradient(to right, hsl(var(--foreground) / 0), hsl(var(--foreground)) 3rem);
|
|
4005
|
+
}
|
|
4006
|
+
.boff-explore-rail-fade-right {
|
|
4007
|
+
-webkit-mask-image: linear-gradient(to right, hsl(var(--foreground)) calc(100% - 3rem), hsl(var(--foreground) / 0));
|
|
4008
|
+
mask-image: linear-gradient(to right, hsl(var(--foreground)) calc(100% - 3rem), hsl(var(--foreground) / 0));
|
|
4009
|
+
}
|
|
4010
|
+
.boff-explore-rail-fade-left.boff-explore-rail-fade-right {
|
|
4011
|
+
-webkit-mask-image: linear-gradient(to right, hsl(var(--foreground) / 0), hsl(var(--foreground)) 3rem, hsl(var(--foreground)) calc(100% - 3rem), hsl(var(--foreground) / 0));
|
|
4012
|
+
mask-image: linear-gradient(to right, hsl(var(--foreground) / 0), hsl(var(--foreground)) 3rem, hsl(var(--foreground)) calc(100% - 3rem), hsl(var(--foreground) / 0));
|
|
4013
|
+
}
|
|
4014
|
+
|
|
3921
4015
|
@keyframes boff-explore-fade-in {
|
|
3922
4016
|
from { opacity: 0; transform: translateY(2px); }
|
|
3923
4017
|
to { opacity: 1; transform: none; }
|
|
@@ -3994,10 +4088,10 @@ var EXPLORE_CSS = `
|
|
|
3994
4088
|
Colour comes from the product's --primary token, so it is whatever that site's palette
|
|
3995
4089
|
already is, never a colour chosen here. */
|
|
3996
4090
|
.boff-explore-invite {
|
|
3997
|
-
border-color: hsl(var(--primary) / 0.
|
|
4091
|
+
border-color: hsl(var(--primary) / 0.6);
|
|
3998
4092
|
box-shadow:
|
|
3999
|
-
0 0 0 4px hsl(var(--primary) / 0.
|
|
4000
|
-
0 8px 24px -12px hsl(var(--primary) / 0.
|
|
4093
|
+
0 0 0 4px hsl(var(--primary) / 0.12),
|
|
4094
|
+
0 8px 24px -12px hsl(var(--primary) / 0.55);
|
|
4001
4095
|
animation: boff-explore-breathe 3.2s ease-in-out infinite;
|
|
4002
4096
|
}
|
|
4003
4097
|
/* Focus and typing both mean "I have found it", and the hint has done its job. */
|
|
@@ -4007,8 +4101,8 @@ var EXPLORE_CSS = `
|
|
|
4007
4101
|
box-shadow: 0 0 0 4px hsl(var(--primary) / 0.10);
|
|
4008
4102
|
}
|
|
4009
4103
|
@keyframes boff-explore-breathe {
|
|
4010
|
-
0%, 100% { box-shadow: 0 0 0 4px hsl(var(--primary) / 0.
|
|
4011
|
-
50% { box-shadow: 0 0 0 7px hsl(var(--primary) / 0.
|
|
4104
|
+
0%, 100% { box-shadow: 0 0 0 4px hsl(var(--primary) / 0.10), 0 8px 24px -12px hsl(var(--primary) / 0.50); }
|
|
4105
|
+
50% { box-shadow: 0 0 0 7px hsl(var(--primary) / 0.18), 0 8px 28px -12px hsl(var(--primary) / 0.65); }
|
|
4012
4106
|
}
|
|
4013
4107
|
|
|
4014
4108
|
/* Every one of the above is decoration. Reduced motion removes the movement and keeps the
|
|
@@ -4096,8 +4190,18 @@ var EXPLORE_CSS = `
|
|
|
4096
4190
|
.boff-explore-scroll .boff-explore-capabilities { display: none; }
|
|
4097
4191
|
}
|
|
4098
4192
|
@container boff-explore-pane (min-width: ${toRem(WELCOME_FIT_MIN_WIDTH_PX)}) and (max-height: ${toRem(WELCOME_FIT_STEP_PX[1])}) {
|
|
4099
|
-
/* Step 2.
|
|
4193
|
+
/* Step 2. At a 4:1 crop the picture is ~44px tall, and the full-size caveat badge covered
|
|
4194
|
+
half of it \u2014 on ten sites, every card, whenever the consent banner was up. It stays (the
|
|
4195
|
+
picture is still a mockup), just smaller and tucked into the corner. */
|
|
4100
4196
|
.boff-explore-scroll .boff-explore-concept-media { aspect-ratio: 4 / 1; }
|
|
4197
|
+
.boff-explore-scroll .boff-explore-concept-badge {
|
|
4198
|
+
top: 0.25rem;
|
|
4199
|
+
left: 0.25rem;
|
|
4200
|
+
padding: 0 0.3125rem;
|
|
4201
|
+
font-size: 0.5rem;
|
|
4202
|
+
line-height: 0.75rem;
|
|
4203
|
+
letter-spacing: 0.02em;
|
|
4204
|
+
}
|
|
4101
4205
|
}
|
|
4102
4206
|
@container boff-explore-pane (min-width: ${toRem(WELCOME_FIT_MIN_WIDTH_PX)}) and (max-height: ${toRem(WELCOME_FIT_STEP_PX[2])}) {
|
|
4103
4207
|
/* Step 3. Title-only cards. The "concept illustration" caveat lives on the picture, so it
|
|
@@ -5256,6 +5360,7 @@ function ErrorBanner({
|
|
|
5256
5360
|
}
|
|
5257
5361
|
function PromptCarousel({
|
|
5258
5362
|
strings: T,
|
|
5363
|
+
label,
|
|
5259
5364
|
prompts,
|
|
5260
5365
|
onPrompt,
|
|
5261
5366
|
disabled
|
|
@@ -5287,7 +5392,7 @@ function PromptCarousel({
|
|
|
5287
5392
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5288
5393
|
"div",
|
|
5289
5394
|
{
|
|
5290
|
-
className: "
|
|
5395
|
+
className: "grid min-w-0 touch-pan-y grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-x-1.5 gap-y-1.5 sm:grid-cols-[auto_minmax(0,1fr)_auto]",
|
|
5291
5396
|
role: "group",
|
|
5292
5397
|
"aria-roledescription": "carousel",
|
|
5293
5398
|
"aria-label": "Example questions",
|
|
@@ -5297,13 +5402,14 @@ function PromptCarousel({
|
|
|
5297
5402
|
onBlurCapture: () => setPaused(false),
|
|
5298
5403
|
...swipe,
|
|
5299
5404
|
children: [
|
|
5405
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "col-start-1 row-start-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground sm:col-span-3", children: label }),
|
|
5300
5406
|
count > 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5301
5407
|
"button",
|
|
5302
5408
|
{
|
|
5303
5409
|
type: "button",
|
|
5304
5410
|
"aria-label": T.prevSuggestion,
|
|
5305
5411
|
onClick: () => go(-1),
|
|
5306
|
-
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",
|
|
5412
|
+
className: "col-start-2 row-start-1 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 sm:col-start-1 sm:row-start-2",
|
|
5307
5413
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
|
|
5308
5414
|
}
|
|
5309
5415
|
) : null,
|
|
@@ -5317,14 +5423,23 @@ function PromptCarousel({
|
|
|
5317
5423
|
"aria-label": `Ask: ${current}`,
|
|
5318
5424
|
"aria-live": paused ? "polite" : "off",
|
|
5319
5425
|
onClick: () => onPrompt(current),
|
|
5320
|
-
className:
|
|
5426
|
+
className: cn(
|
|
5427
|
+
"group col-span-3 row-start-2 flex min-w-0 items-center gap-2 rounded-full border border-border bg-card px-4 py-2 max-sm:min-h-[3.625rem] max-sm:rounded-2xl 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",
|
|
5428
|
+
count > 1 ? "sm:col-span-1 sm:col-start-2" : "sm:col-span-3"
|
|
5429
|
+
),
|
|
5321
5430
|
children: [
|
|
5322
5431
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "h-3.5 w-3.5 shrink-0 text-primary" }),
|
|
5323
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5324
|
-
|
|
5432
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5433
|
+
"span",
|
|
5434
|
+
{
|
|
5435
|
+
className: "boff-explore-fade min-w-0 max-sm:line-clamp-3 sm:truncate",
|
|
5436
|
+
children: current
|
|
5437
|
+
},
|
|
5438
|
+
current
|
|
5439
|
+
),
|
|
5440
|
+
/* @__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 max-sm:hidden" })
|
|
5325
5441
|
]
|
|
5326
|
-
}
|
|
5327
|
-
current
|
|
5442
|
+
}
|
|
5328
5443
|
),
|
|
5329
5444
|
count > 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5330
5445
|
"button",
|
|
@@ -5332,7 +5447,7 @@ function PromptCarousel({
|
|
|
5332
5447
|
type: "button",
|
|
5333
5448
|
"aria-label": T.nextSuggestion,
|
|
5334
5449
|
onClick: () => go(1),
|
|
5335
|
-
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",
|
|
5450
|
+
className: "col-start-3 row-start-1 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 sm:row-start-2",
|
|
5336
5451
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
|
|
5337
5452
|
}
|
|
5338
5453
|
) : null
|
|
@@ -5363,7 +5478,7 @@ function ProductStrip({
|
|
|
5363
5478
|
{
|
|
5364
5479
|
className: cn(
|
|
5365
5480
|
"boff-explore-strip flex min-w-0 flex-1 gap-2",
|
|
5366
|
-
expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
|
|
5481
|
+
expanded ? "flex-wrap" : "boff-explore-chips-fade flex-nowrap overflow-x-auto"
|
|
5367
5482
|
),
|
|
5368
5483
|
children: [
|
|
5369
5484
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5431,8 +5546,11 @@ function ConceptStrip({
|
|
|
5431
5546
|
const measureRail = React.useCallback(() => {
|
|
5432
5547
|
const el = railRef.current;
|
|
5433
5548
|
if (!el) return;
|
|
5434
|
-
|
|
5435
|
-
|
|
5549
|
+
const max = el.scrollWidth - el.clientWidth;
|
|
5550
|
+
const rtl = typeof getComputedStyle === "function" && getComputedStyle(el).direction === "rtl";
|
|
5551
|
+
const fromLeft = rtl ? max + el.scrollLeft : el.scrollLeft;
|
|
5552
|
+
setCanLeft(fromLeft > 1);
|
|
5553
|
+
setCanRight(fromLeft < max - 1);
|
|
5436
5554
|
}, []);
|
|
5437
5555
|
React.useEffect(() => {
|
|
5438
5556
|
const el = railRef.current;
|
|
@@ -5501,11 +5619,15 @@ function ConceptStrip({
|
|
|
5501
5619
|
onScroll: measureRail,
|
|
5502
5620
|
"data-boff-explore": "concept-strip",
|
|
5503
5621
|
"aria-label": T.conceptStripLabel,
|
|
5504
|
-
className:
|
|
5622
|
+
className: cn(
|
|
5623
|
+
"boff-explore-strip flex min-w-0 snap-x gap-3 overflow-x-auto pb-1 sm:mx-10",
|
|
5624
|
+
canLeft && "boff-explore-rail-fade-left",
|
|
5625
|
+
canRight && "boff-explore-rail-fade-right"
|
|
5626
|
+
),
|
|
5505
5627
|
children: visible.map((highlight) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5506
5628
|
"li",
|
|
5507
5629
|
{
|
|
5508
|
-
className: "shrink-0 snap-start",
|
|
5630
|
+
className: "flex shrink-0 snap-start",
|
|
5509
5631
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5510
5632
|
"button",
|
|
5511
5633
|
{
|
|
@@ -5541,14 +5663,14 @@ function ConceptStrip({
|
|
|
5541
5663
|
"span",
|
|
5542
5664
|
{
|
|
5543
5665
|
"aria-hidden": "true",
|
|
5544
|
-
className: "pointer-events-none absolute left-1.5 top-1.5 rounded-full border border-border bg-background/90 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-foreground",
|
|
5666
|
+
className: "boff-explore-concept-badge pointer-events-none absolute left-1.5 top-1.5 rounded-full border border-border bg-background/90 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-foreground",
|
|
5545
5667
|
children: T.conceptIllustration
|
|
5546
5668
|
}
|
|
5547
5669
|
)
|
|
5548
5670
|
] }),
|
|
5549
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col gap-0.5 p-2.5", children: [
|
|
5671
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5 p-2.5", children: [
|
|
5550
5672
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-xs font-semibold leading-snug text-card-foreground", children: highlight.title }),
|
|
5551
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: highlight.productName })
|
|
5673
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-auto truncate text-[11px] text-muted-foreground", children: highlight.productName })
|
|
5552
5674
|
] })
|
|
5553
5675
|
]
|
|
5554
5676
|
}
|
|
@@ -5696,18 +5818,16 @@ function WelcomeState({
|
|
|
5696
5818
|
},
|
|
5697
5819
|
i
|
|
5698
5820
|
)) }),
|
|
5699
|
-
prompts.length > 0 ? /* @__PURE__ */ jsxRuntime.
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
)
|
|
5710
|
-
] }) : null,
|
|
5821
|
+
prompts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-explore-rise boff-explore-rise-2 mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5822
|
+
PromptCarousel,
|
|
5823
|
+
{
|
|
5824
|
+
strings: T,
|
|
5825
|
+
label: T.tryAsking,
|
|
5826
|
+
prompts,
|
|
5827
|
+
onPrompt,
|
|
5828
|
+
disabled
|
|
5829
|
+
}
|
|
5830
|
+
) }) : null,
|
|
5711
5831
|
products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "boff-explore-rise boff-explore-rise-3 mt-4", children: [
|
|
5712
5832
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: T.focusProduct }),
|
|
5713
5833
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5765,6 +5885,7 @@ function ExplorePage({
|
|
|
5765
5885
|
const {
|
|
5766
5886
|
phase,
|
|
5767
5887
|
catalog,
|
|
5888
|
+
serverCatalogRaw,
|
|
5768
5889
|
messages,
|
|
5769
5890
|
activity,
|
|
5770
5891
|
remainingToday,
|
|
@@ -6196,6 +6317,10 @@ function ExplorePage({
|
|
|
6196
6317
|
const captchaOn = Boolean(
|
|
6197
6318
|
catalog.captchaRequired && catalog.recaptchaSiteKey
|
|
6198
6319
|
);
|
|
6320
|
+
const bakedCatalogJson = React.useMemo(
|
|
6321
|
+
() => serverCatalogRaw && serverCatalogRaw.enabled !== false ? serializeBakedCatalog(serverCatalogRaw) : null,
|
|
6322
|
+
[serverCatalogRaw]
|
|
6323
|
+
);
|
|
6199
6324
|
const rootStyle = {};
|
|
6200
6325
|
if (heightMode === "viewport") {
|
|
6201
6326
|
rootStyle["--boff-explore-offset"] = paneOffset === null ? normalizeOffset(viewportOffset) : `${paneOffset}px`;
|
|
@@ -6235,6 +6360,16 @@ function ExplorePage({
|
|
|
6235
6360
|
),
|
|
6236
6361
|
/* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
|
|
6237
6362
|
/* @__PURE__ */ jsxRuntime.jsx(ExploreStyles, {}),
|
|
6363
|
+
bakedCatalogJson ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6364
|
+
"script",
|
|
6365
|
+
{
|
|
6366
|
+
id: BAKED_CATALOG_ELEMENT_ID,
|
|
6367
|
+
type: "application/json",
|
|
6368
|
+
"data-product": productSlug ?? "",
|
|
6369
|
+
"data-locale": bakedCatalogLocale(),
|
|
6370
|
+
dangerouslySetInnerHTML: { __html: bakedCatalogJson }
|
|
6371
|
+
}
|
|
6372
|
+
) : null,
|
|
6238
6373
|
/* @__PURE__ */ jsxRuntime.jsx(ExploreAnswerStyles, {}),
|
|
6239
6374
|
!showWelcome ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 border-b border-border px-4 py-2.5", children: [
|
|
6240
6375
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6709,7 +6844,7 @@ function ExplorePage({
|
|
|
6709
6844
|
)
|
|
6710
6845
|
] }) : null,
|
|
6711
6846
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
6712
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: T.disclaimerShort }),
|
|
6847
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 sm:truncate", children: T.disclaimerShort }),
|
|
6713
6848
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6714
6849
|
"button",
|
|
6715
6850
|
{
|