@burdenoff/website-sdk 2026.829.1 → 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.
@@ -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;
@@ -1981,7 +2009,10 @@ function logoUrlOf(url) {
1981
2009
  return null;
1982
2010
  }
1983
2011
  }
1984
- function ReferenceCard({ reference }) {
2012
+ function ReferenceCard({
2013
+ reference,
2014
+ onFollow
2015
+ }) {
1985
2016
  const [imageFailed, setImageFailed] = useState(false);
1986
2017
  const [logoFailed, setLogoFailed] = useState(false);
1987
2018
  const host = hostnameOf(reference.url);
@@ -2003,9 +2034,11 @@ function ReferenceCard({ reference }) {
2003
2034
  "a",
2004
2035
  {
2005
2036
  "data-boff-explore": "reference",
2037
+ "data-index": reference.index ?? void 0,
2006
2038
  href: reference.url,
2007
2039
  target: "_blank",
2008
2040
  rel: "noopener noreferrer",
2041
+ onClick: () => onFollow?.(reference.url),
2009
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",
2010
2043
  children: [
2011
2044
  /* @__PURE__ */ jsx("div", { className: "relative h-11 w-11 shrink-0 overflow-hidden rounded-lg bg-muted sm:hidden", children: logoImg }),
@@ -2029,7 +2062,10 @@ function ReferenceCard({ reference }) {
2029
2062
  }
2030
2063
  ) }) : letterPlate }),
2031
2064
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 sm:gap-1.5 sm:p-3", children: [
2032
- /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: reference.title || host || reference.url }),
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
+ ] }),
2033
2069
  reference.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground max-sm:hidden", children: reference.description }) : null,
2034
2070
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 sm:mt-auto sm:pt-2", children: [
2035
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,
@@ -2045,7 +2081,8 @@ function ReferenceCard({ reference }) {
2045
2081
  }
2046
2082
  function CtaButton({
2047
2083
  cta,
2048
- onNavigate
2084
+ onNavigate,
2085
+ onFollow
2049
2086
  }) {
2050
2087
  const variant = CTA_VARIANTS[cta.kind] ?? "outline";
2051
2088
  const internal = isInternalPath(cta.url);
@@ -2058,7 +2095,10 @@ function CtaButton({
2058
2095
  type: "button",
2059
2096
  size: "sm",
2060
2097
  variant,
2061
- onClick: () => onNavigate(cta.url),
2098
+ onClick: () => {
2099
+ onFollow?.(cta.url);
2100
+ onNavigate(cta.url);
2101
+ },
2062
2102
  children: cta.label
2063
2103
  }
2064
2104
  );
@@ -2069,6 +2109,7 @@ function CtaButton({
2069
2109
  "data-boff-explore": "cta",
2070
2110
  "data-kind": cta.kind,
2071
2111
  href: cta.url,
2112
+ onClick: () => onFollow?.(cta.url),
2072
2113
  ...internal ? {} : { target: "_blank", rel: "noopener noreferrer" },
2073
2114
  children: [
2074
2115
  cta.label,
@@ -2120,7 +2161,9 @@ function AssistantBubble({
2120
2161
  message,
2121
2162
  activity,
2122
2163
  onNavigate,
2123
- anchorRef
2164
+ anchorRef,
2165
+ onFollow,
2166
+ onFollowUp
2124
2167
  }) {
2125
2168
  const streaming = message.status === "PENDING" || message.status === "RUNNING";
2126
2169
  const body = message.content || message.partialContent || "";
@@ -2142,6 +2185,34 @@ function AssistantBubble({
2142
2185
  }
2143
2186
  ),
2144
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,
2145
2216
  body ? /* @__PURE__ */ jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
2146
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 }) }),
2147
2218
  streaming ? /* @__PURE__ */ jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
@@ -2150,16 +2221,41 @@ function AssistantBubble({
2150
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,
2151
2222
  message.references.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
2152
2223
  /* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Sources" }),
2153
- /* @__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(ReferenceCard, { reference }, reference.url)) })
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
+ )) })
2154
2232
  ] }) : null,
2155
2233
  message.ctas.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 pt-0.5", children: message.ctas.map((cta) => /* @__PURE__ */ jsx(
2156
2234
  CtaButton,
2157
2235
  {
2158
2236
  cta,
2159
- onNavigate
2237
+ onNavigate,
2238
+ onFollow: (url) => onFollow?.("CTA", url)
2160
2239
  },
2161
2240
  `${cta.kind}-${cta.url}`
2162
- )) }) : 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
2163
2259
  ] })
2164
2260
  ]
2165
2261
  }
@@ -2233,6 +2329,9 @@ function PromptCarousel({
2233
2329
  "div",
2234
2330
  {
2235
2331
  className: "flex min-w-0 items-center gap-1.5",
2332
+ role: "group",
2333
+ "aria-roledescription": "carousel",
2334
+ "aria-label": "Example questions",
2236
2335
  onMouseEnter: () => setPaused(true),
2237
2336
  onMouseLeave: () => setPaused(false),
2238
2337
  onFocusCapture: () => setPaused(true),
@@ -2255,6 +2354,8 @@ function PromptCarousel({
2255
2354
  "data-chip-kind": "prompt",
2256
2355
  type: "button",
2257
2356
  disabled,
2357
+ "aria-label": `Ask: ${current}`,
2358
+ "aria-live": paused ? "polite" : "off",
2258
2359
  onClick: () => onPrompt(current),
2259
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",
2260
2361
  children: [
@@ -2289,72 +2390,108 @@ function ProductStrip({
2289
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",
2290
2391
  active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
2291
2392
  );
2292
- return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2293
- /* @__PURE__ */ jsxs(
2294
- "div",
2295
- {
2296
- className: cn(
2297
- "boff-explore-strip flex min-w-0 flex-1 gap-2",
2298
- expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
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
+ }
2299
2438
  ),
2300
- children: [
2301
- /* @__PURE__ */ jsx(
2302
- "button",
2303
- {
2304
- "data-boff-explore": "chip",
2305
- "data-chip-kind": "product",
2306
- "data-product": "all",
2307
- type: "button",
2308
- "aria-pressed": focusProduct === null,
2309
- onClick: () => onFocus(null),
2310
- className: chipClass(focusProduct === null),
2311
- children: "All products"
2312
- }
2313
- ),
2314
- products.map((product) => /* @__PURE__ */ jsx(
2315
- "button",
2316
- {
2317
- "data-boff-explore": "chip",
2318
- "data-chip-kind": "product",
2319
- "data-product": product.slug,
2320
- type: "button",
2321
- "aria-pressed": focusProduct === product.slug,
2322
- title: product.tagline ?? product.name,
2323
- onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
2324
- className: chipClass(focusProduct === product.slug),
2325
- children: product.name
2326
- },
2327
- product.slug
2328
- ))
2329
- ]
2330
- }
2331
- ),
2332
- products.length > 0 ? /* @__PURE__ */ jsx(
2333
- "button",
2334
- {
2335
- type: "button",
2336
- "data-boff-explore": "products-toggle",
2337
- onClick: () => setExpanded((v) => !v),
2338
- 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",
2339
- children: expanded ? "Show less" : `All ${products.length}`
2340
- }
2341
- ) : null
2342
- ] });
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
+ );
2343
2453
  }
2344
2454
  function DisclaimerDialog({
2345
2455
  open,
2346
2456
  onClose,
2347
2457
  captchaOn
2348
2458
  }) {
2459
+ const panelRef = useRef(null);
2349
2460
  const closeRef = useRef(null);
2461
+ const titleId = "boff-explore-disclaimer-title";
2350
2462
  useEffect(() => {
2351
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
+ );
2352
2470
  const onKey = (event) => {
2353
- if (event.key === "Escape") onClose();
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
+ }
2354
2488
  };
2355
2489
  document.addEventListener("keydown", onKey);
2356
2490
  closeRef.current?.focus();
2357
- return () => document.removeEventListener("keydown", onKey);
2491
+ return () => {
2492
+ document.removeEventListener("keydown", onKey);
2493
+ opener?.focus();
2494
+ };
2358
2495
  }, [open, onClose]);
2359
2496
  if (!open) return null;
2360
2497
  return /* @__PURE__ */ jsx(
@@ -2363,19 +2500,34 @@ function DisclaimerDialog({
2363
2500
  "data-boff-explore": "disclaimer-dialog",
2364
2501
  role: "dialog",
2365
2502
  "aria-modal": "true",
2366
- "aria-label": "How this assistant works",
2503
+ "aria-labelledby": titleId,
2367
2504
  className: "fixed inset-0 z-50 flex items-end justify-center bg-foreground/40 p-4 backdrop-blur-sm sm:items-center",
2368
2505
  onClick: onClose,
2369
2506
  children: /* @__PURE__ */ jsxs(
2370
2507
  "div",
2371
2508
  {
2509
+ ref: panelRef,
2372
2510
  className: "w-full max-w-md rounded-2xl border border-border bg-card p-5 shadow-xl",
2373
2511
  onClick: (event) => event.stopPropagation(),
2374
2512
  children: [
2375
2513
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
2376
- /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(TriangleAlert, { className: "h-4 w-4" }) }),
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
+ ),
2377
2522
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2378
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-card-foreground", children: "How this assistant works" }),
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
+ ),
2379
2531
  /* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2 text-xs leading-relaxed text-muted-foreground", children: [
2380
2532
  /* @__PURE__ */ jsx("p", { children: "Answers are generated from our public documentation and can be imperfect \u2014 check anything important against the linked sources." }),
2381
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." }),
@@ -2487,7 +2639,8 @@ function ExplorePage({
2487
2639
  history,
2488
2640
  openConversation,
2489
2641
  deleteConversation,
2490
- clearHistory
2642
+ clearHistory,
2643
+ recordClick
2491
2644
  } = useExploreChat({ productSlug, pageUrl, onSend, examplePrompts });
2492
2645
  const [draft, setDraft] = useState("");
2493
2646
  const textareaRef = useRef(null);
@@ -2761,7 +2914,9 @@ function ExplorePage({
2761
2914
  message,
2762
2915
  activity,
2763
2916
  onNavigate,
2764
- 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
2765
2920
  },
2766
2921
  message.id
2767
2922
  )