@burdenoff/website-sdk 2026.829.1 → 2026.829.3

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.
@@ -489,6 +489,7 @@ var EXPLORE_MESSAGE_FIELDS = (
489
489
  description
490
490
  product
491
491
  imageUrl
492
+ index
492
493
  }
493
494
  ctas {
494
495
  kind
@@ -496,6 +497,8 @@ var EXPLORE_MESSAGE_FIELDS = (
496
497
  url
497
498
  product
498
499
  }
500
+ followUps
501
+ answered
499
502
  errorCode
500
503
  createdAt
501
504
  completedAt
@@ -561,6 +564,26 @@ var SEND_EXPLORE_MESSAGE_MUTATION = (
561
564
  }
562
565
  `
563
566
  );
567
+ var RECORD_EXPLORE_CLICK_MUTATION = (
568
+ /* GraphQL */
569
+ `
570
+ mutation RecordExploreClick($input: RecordExploreClickInput!) {
571
+ recordExploreClick(input: $input) {
572
+ recorded
573
+ }
574
+ }
575
+ `
576
+ );
577
+ var CREATE_EXPLORE_SHARE_LINK_MUTATION = (
578
+ /* GraphQL */
579
+ `
580
+ mutation CreateExploreShareLink($conversationToken: String!) {
581
+ createExploreShareLink(conversationToken: $conversationToken) {
582
+ shareToken
583
+ }
584
+ }
585
+ `
586
+ );
564
587
 
565
588
  // src/data/products.ts
566
589
  var ECOSYSTEM_PRODUCTS = [
@@ -1493,6 +1516,30 @@ function useExploreChat(options = {}) {
1493
1516
  return null;
1494
1517
  }, [messages]);
1495
1518
  const canSend = (phase === "ready" || phase === "error") && catalog.enabled && turnCount < catalog.limits.maxTurnsPerConversation;
1519
+ const createShareLink = React.useCallback(async () => {
1520
+ const token = conversationTokenRef.current;
1521
+ if (!token) return null;
1522
+ try {
1523
+ const result = await client.mutate(CREATE_EXPLORE_SHARE_LINK_MUTATION, { conversationToken: token });
1524
+ return result?.data?.createExploreShareLink?.shareToken ?? null;
1525
+ } catch {
1526
+ return null;
1527
+ }
1528
+ }, [client]);
1529
+ const recordClick = React.useCallback(
1530
+ (messageId, kind, url) => {
1531
+ const token = conversationTokenRef.current;
1532
+ if (!token) return;
1533
+ try {
1534
+ void client.mutate(RECORD_EXPLORE_CLICK_MUTATION, {
1535
+ input: { conversationToken: token, messageId, kind, url }
1536
+ }).catch(() => {
1537
+ });
1538
+ } catch {
1539
+ }
1540
+ },
1541
+ [client]
1542
+ );
1496
1543
  return {
1497
1544
  phase,
1498
1545
  catalog,
@@ -1510,7 +1557,9 @@ function useExploreChat(options = {}) {
1510
1557
  history: archive,
1511
1558
  openConversation,
1512
1559
  deleteConversation,
1513
- clearHistory
1560
+ clearHistory,
1561
+ recordClick,
1562
+ createShareLink
1514
1563
  };
1515
1564
  }
1516
1565
  var optimisticCounter = 0;
@@ -2007,7 +2056,10 @@ function logoUrlOf(url) {
2007
2056
  return null;
2008
2057
  }
2009
2058
  }
2010
- function ReferenceCard({ reference }) {
2059
+ function ReferenceCard({
2060
+ reference,
2061
+ onFollow
2062
+ }) {
2011
2063
  const [imageFailed, setImageFailed] = React.useState(false);
2012
2064
  const [logoFailed, setLogoFailed] = React.useState(false);
2013
2065
  const host = hostnameOf(reference.url);
@@ -2029,9 +2081,11 @@ function ReferenceCard({ reference }) {
2029
2081
  "a",
2030
2082
  {
2031
2083
  "data-boff-explore": "reference",
2084
+ "data-index": reference.index ?? void 0,
2032
2085
  href: reference.url,
2033
2086
  target: "_blank",
2034
2087
  rel: "noopener noreferrer",
2088
+ onClick: () => onFollow?.(reference.url),
2035
2089
  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",
2036
2090
  children: [
2037
2091
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-11 w-11 shrink-0 overflow-hidden rounded-lg bg-muted sm:hidden", children: logoImg }),
@@ -2055,7 +2109,10 @@ function ReferenceCard({ reference }) {
2055
2109
  }
2056
2110
  ) }) : letterPlate }),
2057
2111
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 sm:gap-1.5 sm:p-3", children: [
2058
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: reference.title || host || reference.url }),
2112
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: [
2113
+ 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,
2114
+ reference.title || host || reference.url
2115
+ ] }),
2059
2116
  reference.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground max-sm:hidden", children: reference.description }) : null,
2060
2117
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:mt-auto sm:pt-2", children: [
2061
2118
  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,
@@ -2071,7 +2128,8 @@ function ReferenceCard({ reference }) {
2071
2128
  }
2072
2129
  function CtaButton({
2073
2130
  cta,
2074
- onNavigate
2131
+ onNavigate,
2132
+ onFollow
2075
2133
  }) {
2076
2134
  const variant = CTA_VARIANTS[cta.kind] ?? "outline";
2077
2135
  const internal = isInternalPath(cta.url);
@@ -2084,7 +2142,10 @@ function CtaButton({
2084
2142
  type: "button",
2085
2143
  size: "sm",
2086
2144
  variant,
2087
- onClick: () => onNavigate(cta.url),
2145
+ onClick: () => {
2146
+ onFollow?.(cta.url);
2147
+ onNavigate(cta.url);
2148
+ },
2088
2149
  children: cta.label
2089
2150
  }
2090
2151
  );
@@ -2095,6 +2156,7 @@ function CtaButton({
2095
2156
  "data-boff-explore": "cta",
2096
2157
  "data-kind": cta.kind,
2097
2158
  href: cta.url,
2159
+ onClick: () => onFollow?.(cta.url),
2098
2160
  ...internal ? {} : { target: "_blank", rel: "noopener noreferrer" },
2099
2161
  children: [
2100
2162
  cta.label,
@@ -2146,7 +2208,9 @@ function AssistantBubble({
2146
2208
  message,
2147
2209
  activity,
2148
2210
  onNavigate,
2149
- anchorRef
2211
+ anchorRef,
2212
+ onFollow,
2213
+ onFollowUp
2150
2214
  }) {
2151
2215
  const streaming = message.status === "PENDING" || message.status === "RUNNING";
2152
2216
  const body = message.content || message.partialContent || "";
@@ -2168,6 +2232,34 @@ function AssistantBubble({
2168
2232
  }
2169
2233
  ),
2170
2234
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
2235
+ message.status === "COMPLETED" && message.answered === false ? /* @__PURE__ */ jsxRuntime.jsxs(
2236
+ "div",
2237
+ {
2238
+ "data-boff-explore": "no-answer",
2239
+ 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",
2240
+ children: [
2241
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600 dark:text-amber-400" }),
2242
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2243
+ "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
2244
+ " ",
2245
+ /* @__PURE__ */ jsxRuntime.jsx(
2246
+ "a",
2247
+ {
2248
+ href: "/contact",
2249
+ className: "underline underline-offset-2 hover:text-primary",
2250
+ onClick: (event) => {
2251
+ if (!onNavigate) return;
2252
+ event.preventDefault();
2253
+ onNavigate("/contact");
2254
+ },
2255
+ children: "ask a human"
2256
+ }
2257
+ ),
2258
+ "."
2259
+ ] })
2260
+ ]
2261
+ }
2262
+ ) : null,
2171
2263
  body ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
2172
2264
  /* @__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 }) }),
2173
2265
  streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
@@ -2176,16 +2268,41 @@ function AssistantBubble({
2176
2268
  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,
2177
2269
  message.references.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2178
2270
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Sources" }),
2179
- /* @__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(ReferenceCard, { reference }, reference.url)) })
2271
+ /* @__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(
2272
+ ReferenceCard,
2273
+ {
2274
+ reference,
2275
+ onFollow: (url) => onFollow?.("REFERENCE", url)
2276
+ },
2277
+ reference.url
2278
+ )) })
2180
2279
  ] }) : null,
2181
2280
  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(
2182
2281
  CtaButton,
2183
2282
  {
2184
2283
  cta,
2185
- onNavigate
2284
+ onNavigate,
2285
+ onFollow: (url) => onFollow?.("CTA", url)
2186
2286
  },
2187
2287
  `${cta.kind}-${cta.url}`
2188
- )) }) : null
2288
+ )) }) : null,
2289
+ message.status === "COMPLETED" && (message.followUps?.length ?? 0) > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 pt-0.5", children: [
2290
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Next you could ask" }),
2291
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: (message.followUps ?? []).map((question) => /* @__PURE__ */ jsxRuntime.jsxs(
2292
+ "button",
2293
+ {
2294
+ "data-boff-explore": "follow-up",
2295
+ type: "button",
2296
+ onClick: () => onFollowUp?.(question),
2297
+ 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",
2298
+ children: [
2299
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: question }),
2300
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3 w-3 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
2301
+ ]
2302
+ },
2303
+ question
2304
+ )) })
2305
+ ] }) : null
2189
2306
  ] })
2190
2307
  ]
2191
2308
  }
@@ -2259,6 +2376,9 @@ function PromptCarousel({
2259
2376
  "div",
2260
2377
  {
2261
2378
  className: "flex min-w-0 items-center gap-1.5",
2379
+ role: "group",
2380
+ "aria-roledescription": "carousel",
2381
+ "aria-label": "Example questions",
2262
2382
  onMouseEnter: () => setPaused(true),
2263
2383
  onMouseLeave: () => setPaused(false),
2264
2384
  onFocusCapture: () => setPaused(true),
@@ -2281,6 +2401,8 @@ function PromptCarousel({
2281
2401
  "data-chip-kind": "prompt",
2282
2402
  type: "button",
2283
2403
  disabled,
2404
+ "aria-label": `Ask: ${current}`,
2405
+ "aria-live": paused ? "polite" : "off",
2284
2406
  onClick: () => onPrompt(current),
2285
2407
  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",
2286
2408
  children: [
@@ -2315,72 +2437,108 @@ function ProductStrip({
2315
2437
  "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",
2316
2438
  active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
2317
2439
  );
2318
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2319
- /* @__PURE__ */ jsxRuntime.jsxs(
2320
- "div",
2321
- {
2322
- className: cn(
2323
- "boff-explore-strip flex min-w-0 flex-1 gap-2",
2324
- expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
2440
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2441
+ "div",
2442
+ {
2443
+ className: "flex min-w-0 items-center gap-2",
2444
+ role: "group",
2445
+ "aria-label": "Focus the assistant on one product",
2446
+ children: [
2447
+ /* @__PURE__ */ jsxRuntime.jsxs(
2448
+ "div",
2449
+ {
2450
+ className: cn(
2451
+ "boff-explore-strip flex min-w-0 flex-1 gap-2",
2452
+ expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
2453
+ ),
2454
+ children: [
2455
+ /* @__PURE__ */ jsxRuntime.jsx(
2456
+ "button",
2457
+ {
2458
+ "data-boff-explore": "chip",
2459
+ "data-chip-kind": "product",
2460
+ "data-product": "all",
2461
+ type: "button",
2462
+ "aria-pressed": focusProduct === null,
2463
+ onClick: () => onFocus(null),
2464
+ className: chipClass(focusProduct === null),
2465
+ children: "All products"
2466
+ }
2467
+ ),
2468
+ products.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
2469
+ "button",
2470
+ {
2471
+ "data-boff-explore": "chip",
2472
+ "data-chip-kind": "product",
2473
+ "data-product": product.slug,
2474
+ type: "button",
2475
+ "aria-pressed": focusProduct === product.slug,
2476
+ title: product.tagline ?? product.name,
2477
+ onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
2478
+ className: chipClass(focusProduct === product.slug),
2479
+ children: product.name
2480
+ },
2481
+ product.slug
2482
+ ))
2483
+ ]
2484
+ }
2325
2485
  ),
2326
- children: [
2327
- /* @__PURE__ */ jsxRuntime.jsx(
2328
- "button",
2329
- {
2330
- "data-boff-explore": "chip",
2331
- "data-chip-kind": "product",
2332
- "data-product": "all",
2333
- type: "button",
2334
- "aria-pressed": focusProduct === null,
2335
- onClick: () => onFocus(null),
2336
- className: chipClass(focusProduct === null),
2337
- children: "All products"
2338
- }
2339
- ),
2340
- products.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
2341
- "button",
2342
- {
2343
- "data-boff-explore": "chip",
2344
- "data-chip-kind": "product",
2345
- "data-product": product.slug,
2346
- type: "button",
2347
- "aria-pressed": focusProduct === product.slug,
2348
- title: product.tagline ?? product.name,
2349
- onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
2350
- className: chipClass(focusProduct === product.slug),
2351
- children: product.name
2352
- },
2353
- product.slug
2354
- ))
2355
- ]
2356
- }
2357
- ),
2358
- products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
2359
- "button",
2360
- {
2361
- type: "button",
2362
- "data-boff-explore": "products-toggle",
2363
- onClick: () => setExpanded((v) => !v),
2364
- 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",
2365
- children: expanded ? "Show less" : `All ${products.length}`
2366
- }
2367
- ) : null
2368
- ] });
2486
+ products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
2487
+ "button",
2488
+ {
2489
+ type: "button",
2490
+ "data-boff-explore": "products-toggle",
2491
+ "aria-expanded": expanded,
2492
+ onClick: () => setExpanded((v) => !v),
2493
+ 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",
2494
+ children: expanded ? "Show less" : `All ${products.length}`
2495
+ }
2496
+ ) : null
2497
+ ]
2498
+ }
2499
+ );
2369
2500
  }
2370
2501
  function DisclaimerDialog({
2371
2502
  open,
2372
2503
  onClose,
2373
2504
  captchaOn
2374
2505
  }) {
2506
+ const panelRef = React.useRef(null);
2375
2507
  const closeRef = React.useRef(null);
2508
+ const titleId = "boff-explore-disclaimer-title";
2376
2509
  React.useEffect(() => {
2377
2510
  if (!open) return;
2511
+ const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
2512
+ const focusable = () => Array.from(
2513
+ panelRef.current?.querySelectorAll(
2514
+ 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
2515
+ ) ?? []
2516
+ );
2378
2517
  const onKey = (event) => {
2379
- if (event.key === "Escape") onClose();
2518
+ if (event.key === "Escape") {
2519
+ onClose();
2520
+ return;
2521
+ }
2522
+ if (event.key !== "Tab") return;
2523
+ const items = focusable();
2524
+ if (items.length === 0) return;
2525
+ const first = items[0];
2526
+ const last = items[items.length - 1];
2527
+ const active = document.activeElement;
2528
+ if (event.shiftKey && (active === first || !panelRef.current?.contains(active))) {
2529
+ event.preventDefault();
2530
+ last?.focus();
2531
+ } else if (!event.shiftKey && active === last) {
2532
+ event.preventDefault();
2533
+ first?.focus();
2534
+ }
2380
2535
  };
2381
2536
  document.addEventListener("keydown", onKey);
2382
2537
  closeRef.current?.focus();
2383
- return () => document.removeEventListener("keydown", onKey);
2538
+ return () => {
2539
+ document.removeEventListener("keydown", onKey);
2540
+ opener?.focus();
2541
+ };
2384
2542
  }, [open, onClose]);
2385
2543
  if (!open) return null;
2386
2544
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -2389,19 +2547,34 @@ function DisclaimerDialog({
2389
2547
  "data-boff-explore": "disclaimer-dialog",
2390
2548
  role: "dialog",
2391
2549
  "aria-modal": "true",
2392
- "aria-label": "How this assistant works",
2550
+ "aria-labelledby": titleId,
2393
2551
  className: "fixed inset-0 z-50 flex items-end justify-center bg-foreground/40 p-4 backdrop-blur-sm sm:items-center",
2394
2552
  onClick: onClose,
2395
2553
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2396
2554
  "div",
2397
2555
  {
2556
+ ref: panelRef,
2398
2557
  className: "w-full max-w-md rounded-2xl border border-border bg-card p-5 shadow-xl",
2399
2558
  onClick: (event) => event.stopPropagation(),
2400
2559
  children: [
2401
2560
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
2402
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "h-4 w-4" }) }),
2561
+ /* @__PURE__ */ jsxRuntime.jsx(
2562
+ "span",
2563
+ {
2564
+ "aria-hidden": "true",
2565
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary",
2566
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "h-4 w-4" })
2567
+ }
2568
+ ),
2403
2569
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
2404
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-card-foreground", children: "How this assistant works" }),
2570
+ /* @__PURE__ */ jsxRuntime.jsx(
2571
+ "p",
2572
+ {
2573
+ id: titleId,
2574
+ className: "text-sm font-semibold text-card-foreground",
2575
+ children: "How this assistant works"
2576
+ }
2577
+ ),
2405
2578
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 space-y-2 text-xs leading-relaxed text-muted-foreground", children: [
2406
2579
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Answers are generated from our public documentation and can be imperfect \u2014 check anything important against the linked sources." }),
2407
2580
  /* @__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." }),
@@ -2513,7 +2686,9 @@ function ExplorePage({
2513
2686
  history,
2514
2687
  openConversation,
2515
2688
  deleteConversation,
2516
- clearHistory
2689
+ clearHistory,
2690
+ recordClick,
2691
+ createShareLink
2517
2692
  } = useExploreChat({ productSlug, pageUrl, onSend, examplePrompts });
2518
2693
  const [draft, setDraft] = React.useState("");
2519
2694
  const textareaRef = React.useRef(null);
@@ -2533,6 +2708,7 @@ function ExplorePage({
2533
2708
  });
2534
2709
  speechStopRef.current = speech.stop;
2535
2710
  const [disclaimerOpen, setDisclaimerOpen] = React.useState(false);
2711
+ const [shareLabel, setShareLabel] = React.useState("Share");
2536
2712
  const scrollRef = React.useRef(null);
2537
2713
  const latestAssistantRef = React.useRef(null);
2538
2714
  const alignedForRef = React.useRef(null);
@@ -2587,6 +2763,25 @@ function ExplorePage({
2587
2763
  if (!el) return;
2588
2764
  el.scrollTop = el.scrollHeight;
2589
2765
  }, [activity, latestAssistantId, messages]);
2766
+ const handleShare = React.useCallback(async () => {
2767
+ setShareLabel("Copying\u2026");
2768
+ const token = await createShareLink();
2769
+ if (!token) {
2770
+ setShareLabel("Unavailable");
2771
+ setTimeout(() => setShareLabel("Share"), 2500);
2772
+ return;
2773
+ }
2774
+ const base = (pageUrl ?? window.location.href).split("?")[0];
2775
+ const url = `${base}?c=${encodeURIComponent(token)}`;
2776
+ try {
2777
+ await navigator.clipboard.writeText(url);
2778
+ setShareLabel("Copied");
2779
+ } catch {
2780
+ window.prompt("Copy this link", url);
2781
+ setShareLabel("Share");
2782
+ }
2783
+ setTimeout(() => setShareLabel("Share"), 2500);
2784
+ }, [createShareLink, pageUrl]);
2590
2785
  const submitDraft = React.useCallback(() => {
2591
2786
  const text = draft.trim();
2592
2787
  if (!text || overLimit || busy || !canSend) return;
@@ -2669,6 +2864,24 @@ function ExplorePage({
2669
2864
  ]
2670
2865
  }
2671
2866
  ),
2867
+ messages.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
2868
+ Button,
2869
+ {
2870
+ "data-boff-explore": "share",
2871
+ type: "button",
2872
+ size: "sm",
2873
+ variant: "ghost",
2874
+ className: "shrink-0 text-muted-foreground",
2875
+ title: "Copy a read-only link to this conversation",
2876
+ onClick: () => {
2877
+ void handleShare();
2878
+ },
2879
+ children: [
2880
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link2, { className: "h-3.5 w-3.5" }),
2881
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: shareLabel })
2882
+ ]
2883
+ }
2884
+ ) : null,
2672
2885
  history.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
2673
2886
  Button,
2674
2887
  {
@@ -2787,7 +3000,9 @@ function ExplorePage({
2787
3000
  message,
2788
3001
  activity,
2789
3002
  onNavigate,
2790
- anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0
3003
+ anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
3004
+ onFollow: (kind, url) => recordClick(message.id, kind, url),
3005
+ onFollowUp: sendPrompt
2791
3006
  },
2792
3007
  message.id
2793
3008
  )