@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.
package/dist/index.d.mts CHANGED
@@ -18,7 +18,7 @@ export { ProblemSolved, ProblemsSolvedSection, ProblemsSolvedSectionProps } from
18
18
  export { DetectedPlatform, ElectronDownloadLink, ElectronDownloadLinkProps, detectElectronPlatform } from './components/electron-download.mjs';
19
19
  export { ExplorePage, ExplorePageProps } from './components/explore.mjs';
20
20
  export { ExploreCta, ExploreCtaProps } from './components/explore-cta.mjs';
21
- export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, e as EXPLORE_CATALOG_QUERY, f as EXPLORE_MESSAGE_QUERY, g as ExploreCatalog, h as ExploreCatalogQueryData, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, i as ExploreConversation, j as ExploreCtaKind, k as ExploreCtaLink, l as ExploreErrorCode, m as ExploreLimits, n as ExploreMessage, o as ExploreMessageQueryData, p as ExploreMessageRole, q as ExploreMessageStatus, r as ExploreProduct, s as ExploreReference, F as FALLBACK_EXPLORE_LIMITS, S as SEND_EXPLORE_MESSAGE_MUTATION, t as SendExploreMessageInput, v as SendExploreMessageMutationData, w as SendExploreMessageResult, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from './use-explore-chat-DhvZhH8f.mjs';
21
+ export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, e as EXPLORE_CATALOG_QUERY, f as EXPLORE_MESSAGE_QUERY, g as ExploreCatalog, h as ExploreCatalogQueryData, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, i as ExploreConversation, j as ExploreCtaKind, k as ExploreCtaLink, l as ExploreErrorCode, m as ExploreLimits, n as ExploreMessage, o as ExploreMessageQueryData, p as ExploreMessageRole, q as ExploreMessageStatus, r as ExploreProduct, s as ExploreReference, F as FALLBACK_EXPLORE_LIMITS, S as SEND_EXPLORE_MESSAGE_MUTATION, t as SendExploreMessageInput, v as SendExploreMessageMutationData, w as SendExploreMessageResult, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from './use-explore-chat-B-E7jN1H.mjs';
22
22
  export { RybbitAnalytics, RybbitAnalyticsProps } from './analytics/index.mjs';
23
23
  export { PageHead, PageHeadProps } from './seo/index.mjs';
24
24
  export { ProductData, ProductProvider, ProductProviderProps, useProduct, useProductConfig } from './product/index.mjs';
@@ -62,7 +62,7 @@ declare function CareersSubmissionForm({ productName, recaptchaSiteKey, postings
62
62
 
63
63
  declare const buttonVariants: (props?: ({
64
64
  variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
65
- size?: "default" | "sm" | "lg" | "icon" | null | undefined;
65
+ size?: "icon" | "default" | "sm" | "lg" | null | undefined;
66
66
  } & class_variance_authority_types.ClassProp) | undefined) => string;
67
67
  interface ButtonProps extends react.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
68
68
  asChild?: boolean;
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export { ProblemSolved, ProblemsSolvedSection, ProblemsSolvedSectionProps } from
18
18
  export { DetectedPlatform, ElectronDownloadLink, ElectronDownloadLinkProps, detectElectronPlatform } from './components/electron-download.js';
19
19
  export { ExplorePage, ExplorePageProps } from './components/explore.js';
20
20
  export { ExploreCta, ExploreCtaProps } from './components/explore-cta.js';
21
- export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, e as EXPLORE_CATALOG_QUERY, f as EXPLORE_MESSAGE_QUERY, g as ExploreCatalog, h as ExploreCatalogQueryData, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, i as ExploreConversation, j as ExploreCtaKind, k as ExploreCtaLink, l as ExploreErrorCode, m as ExploreLimits, n as ExploreMessage, o as ExploreMessageQueryData, p as ExploreMessageRole, q as ExploreMessageStatus, r as ExploreProduct, s as ExploreReference, F as FALLBACK_EXPLORE_LIMITS, S as SEND_EXPLORE_MESSAGE_MUTATION, t as SendExploreMessageInput, v as SendExploreMessageMutationData, w as SendExploreMessageResult, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from './use-explore-chat-DhvZhH8f.js';
21
+ export { D as DEFAULT_EXPLORE_EXAMPLE_PROMPTS, e as EXPLORE_CATALOG_QUERY, f as EXPLORE_MESSAGE_QUERY, g as ExploreCatalog, h as ExploreCatalogQueryData, E as ExploreChatError, a as ExploreChatErrorKind, b as ExploreChatPhase, i as ExploreConversation, j as ExploreCtaKind, k as ExploreCtaLink, l as ExploreErrorCode, m as ExploreLimits, n as ExploreMessage, o as ExploreMessageQueryData, p as ExploreMessageRole, q as ExploreMessageStatus, r as ExploreProduct, s as ExploreReference, F as FALLBACK_EXPLORE_LIMITS, S as SEND_EXPLORE_MESSAGE_MUTATION, t as SendExploreMessageInput, v as SendExploreMessageMutationData, w as SendExploreMessageResult, U as UseExploreChatOptions, c as UseExploreChatResult, d as classifyExploreError, u as useExploreChat } from './use-explore-chat-B-E7jN1H.js';
22
22
  export { RybbitAnalytics, RybbitAnalyticsProps } from './analytics/index.js';
23
23
  export { PageHead, PageHeadProps } from './seo/index.js';
24
24
  export { ProductData, ProductProvider, ProductProviderProps, useProduct, useProductConfig } from './product/index.js';
@@ -62,7 +62,7 @@ declare function CareersSubmissionForm({ productName, recaptchaSiteKey, postings
62
62
 
63
63
  declare const buttonVariants: (props?: ({
64
64
  variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
65
- size?: "default" | "sm" | "lg" | "icon" | null | undefined;
65
+ size?: "icon" | "default" | "sm" | "lg" | null | undefined;
66
66
  } & class_variance_authority_types.ClassProp) | undefined) => string;
67
67
  interface ButtonProps extends react.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
68
68
  asChild?: boolean;
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,26 @@ 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
+ );
5047
+ var CREATE_EXPLORE_SHARE_LINK_MUTATION = (
5048
+ /* GraphQL */
5049
+ `
5050
+ mutation CreateExploreShareLink($conversationToken: String!) {
5051
+ createExploreShareLink(conversationToken: $conversationToken) {
5052
+ shareToken
5053
+ }
5054
+ }
5055
+ `
5056
+ );
5034
5057
 
5035
5058
  // src/data/products.ts
5036
5059
  var ECOSYSTEM_PRODUCTS = [
@@ -5963,6 +5986,30 @@ function useExploreChat(options = {}) {
5963
5986
  return null;
5964
5987
  }, [messages]);
5965
5988
  const canSend = (phase === "ready" || phase === "error") && catalog.enabled && turnCount < catalog.limits.maxTurnsPerConversation;
5989
+ const createShareLink = React.useCallback(async () => {
5990
+ const token = conversationTokenRef.current;
5991
+ if (!token) return null;
5992
+ try {
5993
+ const result = await client.mutate(CREATE_EXPLORE_SHARE_LINK_MUTATION, { conversationToken: token });
5994
+ return result?.data?.createExploreShareLink?.shareToken ?? null;
5995
+ } catch {
5996
+ return null;
5997
+ }
5998
+ }, [client]);
5999
+ const recordClick = React.useCallback(
6000
+ (messageId, kind, url) => {
6001
+ const token = conversationTokenRef.current;
6002
+ if (!token) return;
6003
+ try {
6004
+ void client.mutate(RECORD_EXPLORE_CLICK_MUTATION, {
6005
+ input: { conversationToken: token, messageId, kind, url }
6006
+ }).catch(() => {
6007
+ });
6008
+ } catch {
6009
+ }
6010
+ },
6011
+ [client]
6012
+ );
5966
6013
  return {
5967
6014
  phase,
5968
6015
  catalog,
@@ -5980,7 +6027,9 @@ function useExploreChat(options = {}) {
5980
6027
  history: archive,
5981
6028
  openConversation,
5982
6029
  deleteConversation,
5983
- clearHistory
6030
+ clearHistory,
6031
+ recordClick,
6032
+ createShareLink
5984
6033
  };
5985
6034
  }
5986
6035
  var optimisticCounter = 0;
@@ -6352,7 +6401,10 @@ function logoUrlOf(url) {
6352
6401
  return null;
6353
6402
  }
6354
6403
  }
6355
- function ReferenceCard({ reference }) {
6404
+ function ReferenceCard({
6405
+ reference,
6406
+ onFollow
6407
+ }) {
6356
6408
  const [imageFailed, setImageFailed] = React.useState(false);
6357
6409
  const [logoFailed, setLogoFailed] = React.useState(false);
6358
6410
  const host = hostnameOf(reference.url);
@@ -6374,9 +6426,11 @@ function ReferenceCard({ reference }) {
6374
6426
  "a",
6375
6427
  {
6376
6428
  "data-boff-explore": "reference",
6429
+ "data-index": reference.index ?? void 0,
6377
6430
  href: reference.url,
6378
6431
  target: "_blank",
6379
6432
  rel: "noopener noreferrer",
6433
+ onClick: () => onFollow?.(reference.url),
6380
6434
  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",
6381
6435
  children: [
6382
6436
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-11 w-11 shrink-0 overflow-hidden rounded-lg bg-muted sm:hidden", children: logoImg }),
@@ -6400,7 +6454,10 @@ function ReferenceCard({ reference }) {
6400
6454
  }
6401
6455
  ) }) : letterPlate }),
6402
6456
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 sm:gap-1.5 sm:p-3", children: [
6403
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: reference.title || host || reference.url }),
6457
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "line-clamp-2 text-sm font-semibold leading-snug text-card-foreground", children: [
6458
+ 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,
6459
+ reference.title || host || reference.url
6460
+ ] }),
6404
6461
  reference.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-2 text-xs leading-relaxed text-muted-foreground max-sm:hidden", children: reference.description }) : null,
6405
6462
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:mt-auto sm:pt-2", children: [
6406
6463
  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,
@@ -6416,7 +6473,8 @@ function ReferenceCard({ reference }) {
6416
6473
  }
6417
6474
  function CtaButton({
6418
6475
  cta,
6419
- onNavigate
6476
+ onNavigate,
6477
+ onFollow
6420
6478
  }) {
6421
6479
  const variant = CTA_VARIANTS[cta.kind] ?? "outline";
6422
6480
  const internal = isInternalPath(cta.url);
@@ -6429,7 +6487,10 @@ function CtaButton({
6429
6487
  type: "button",
6430
6488
  size: "sm",
6431
6489
  variant,
6432
- onClick: () => onNavigate(cta.url),
6490
+ onClick: () => {
6491
+ onFollow?.(cta.url);
6492
+ onNavigate(cta.url);
6493
+ },
6433
6494
  children: cta.label
6434
6495
  }
6435
6496
  );
@@ -6440,6 +6501,7 @@ function CtaButton({
6440
6501
  "data-boff-explore": "cta",
6441
6502
  "data-kind": cta.kind,
6442
6503
  href: cta.url,
6504
+ onClick: () => onFollow?.(cta.url),
6443
6505
  ...internal ? {} : { target: "_blank", rel: "noopener noreferrer" },
6444
6506
  children: [
6445
6507
  cta.label,
@@ -6491,7 +6553,9 @@ function AssistantBubble({
6491
6553
  message,
6492
6554
  activity,
6493
6555
  onNavigate,
6494
- anchorRef
6556
+ anchorRef,
6557
+ onFollow,
6558
+ onFollowUp
6495
6559
  }) {
6496
6560
  const streaming = message.status === "PENDING" || message.status === "RUNNING";
6497
6561
  const body = message.content || message.partialContent || "";
@@ -6513,6 +6577,34 @@ function AssistantBubble({
6513
6577
  }
6514
6578
  ),
6515
6579
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
6580
+ message.status === "COMPLETED" && message.answered === false ? /* @__PURE__ */ jsxRuntime.jsxs(
6581
+ "div",
6582
+ {
6583
+ "data-boff-explore": "no-answer",
6584
+ 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",
6585
+ children: [
6586
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600 dark:text-amber-400" }),
6587
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
6588
+ "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
6589
+ " ",
6590
+ /* @__PURE__ */ jsxRuntime.jsx(
6591
+ "a",
6592
+ {
6593
+ href: "/contact",
6594
+ className: "underline underline-offset-2 hover:text-primary",
6595
+ onClick: (event) => {
6596
+ if (!onNavigate) return;
6597
+ event.preventDefault();
6598
+ onNavigate("/contact");
6599
+ },
6600
+ children: "ask a human"
6601
+ }
6602
+ ),
6603
+ "."
6604
+ ] })
6605
+ ]
6606
+ }
6607
+ ) : null,
6516
6608
  body ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
6517
6609
  /* @__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 }) }),
6518
6610
  streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
@@ -6521,16 +6613,41 @@ function AssistantBubble({
6521
6613
  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,
6522
6614
  message.references.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6523
6615
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Sources" }),
6524
- /* @__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)) })
6616
+ /* @__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(
6617
+ ReferenceCard,
6618
+ {
6619
+ reference,
6620
+ onFollow: (url) => onFollow?.("REFERENCE", url)
6621
+ },
6622
+ reference.url
6623
+ )) })
6525
6624
  ] }) : null,
6526
6625
  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(
6527
6626
  CtaButton,
6528
6627
  {
6529
6628
  cta,
6530
- onNavigate
6629
+ onNavigate,
6630
+ onFollow: (url) => onFollow?.("CTA", url)
6531
6631
  },
6532
6632
  `${cta.kind}-${cta.url}`
6533
- )) }) : null
6633
+ )) }) : null,
6634
+ message.status === "COMPLETED" && (message.followUps?.length ?? 0) > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 pt-0.5", children: [
6635
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Next you could ask" }),
6636
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: (message.followUps ?? []).map((question) => /* @__PURE__ */ jsxRuntime.jsxs(
6637
+ "button",
6638
+ {
6639
+ "data-boff-explore": "follow-up",
6640
+ type: "button",
6641
+ onClick: () => onFollowUp?.(question),
6642
+ 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",
6643
+ children: [
6644
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: question }),
6645
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3 w-3 shrink-0 rotate-45 text-muted-foreground transition-colors group-hover:text-primary" })
6646
+ ]
6647
+ },
6648
+ question
6649
+ )) })
6650
+ ] }) : null
6534
6651
  ] })
6535
6652
  ]
6536
6653
  }
@@ -6604,6 +6721,9 @@ function PromptCarousel({
6604
6721
  "div",
6605
6722
  {
6606
6723
  className: "flex min-w-0 items-center gap-1.5",
6724
+ role: "group",
6725
+ "aria-roledescription": "carousel",
6726
+ "aria-label": "Example questions",
6607
6727
  onMouseEnter: () => setPaused(true),
6608
6728
  onMouseLeave: () => setPaused(false),
6609
6729
  onFocusCapture: () => setPaused(true),
@@ -6626,6 +6746,8 @@ function PromptCarousel({
6626
6746
  "data-chip-kind": "prompt",
6627
6747
  type: "button",
6628
6748
  disabled,
6749
+ "aria-label": `Ask: ${current}`,
6750
+ "aria-live": paused ? "polite" : "off",
6629
6751
  onClick: () => onPrompt(current),
6630
6752
  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",
6631
6753
  children: [
@@ -6660,72 +6782,108 @@ function ProductStrip({
6660
6782
  "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",
6661
6783
  active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-muted-foreground hover:bg-accent hover:text-accent-foreground"
6662
6784
  );
6663
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
6664
- /* @__PURE__ */ jsxRuntime.jsxs(
6665
- "div",
6666
- {
6667
- className: cn(
6668
- "boff-explore-strip flex min-w-0 flex-1 gap-2",
6669
- expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
6785
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6786
+ "div",
6787
+ {
6788
+ className: "flex min-w-0 items-center gap-2",
6789
+ role: "group",
6790
+ "aria-label": "Focus the assistant on one product",
6791
+ children: [
6792
+ /* @__PURE__ */ jsxRuntime.jsxs(
6793
+ "div",
6794
+ {
6795
+ className: cn(
6796
+ "boff-explore-strip flex min-w-0 flex-1 gap-2",
6797
+ expanded ? "flex-wrap" : "flex-nowrap overflow-x-auto"
6798
+ ),
6799
+ children: [
6800
+ /* @__PURE__ */ jsxRuntime.jsx(
6801
+ "button",
6802
+ {
6803
+ "data-boff-explore": "chip",
6804
+ "data-chip-kind": "product",
6805
+ "data-product": "all",
6806
+ type: "button",
6807
+ "aria-pressed": focusProduct === null,
6808
+ onClick: () => onFocus(null),
6809
+ className: chipClass(focusProduct === null),
6810
+ children: "All products"
6811
+ }
6812
+ ),
6813
+ products.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
6814
+ "button",
6815
+ {
6816
+ "data-boff-explore": "chip",
6817
+ "data-chip-kind": "product",
6818
+ "data-product": product.slug,
6819
+ type: "button",
6820
+ "aria-pressed": focusProduct === product.slug,
6821
+ title: product.tagline ?? product.name,
6822
+ onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
6823
+ className: chipClass(focusProduct === product.slug),
6824
+ children: product.name
6825
+ },
6826
+ product.slug
6827
+ ))
6828
+ ]
6829
+ }
6670
6830
  ),
6671
- children: [
6672
- /* @__PURE__ */ jsxRuntime.jsx(
6673
- "button",
6674
- {
6675
- "data-boff-explore": "chip",
6676
- "data-chip-kind": "product",
6677
- "data-product": "all",
6678
- type: "button",
6679
- "aria-pressed": focusProduct === null,
6680
- onClick: () => onFocus(null),
6681
- className: chipClass(focusProduct === null),
6682
- children: "All products"
6683
- }
6684
- ),
6685
- products.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
6686
- "button",
6687
- {
6688
- "data-boff-explore": "chip",
6689
- "data-chip-kind": "product",
6690
- "data-product": product.slug,
6691
- type: "button",
6692
- "aria-pressed": focusProduct === product.slug,
6693
- title: product.tagline ?? product.name,
6694
- onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
6695
- className: chipClass(focusProduct === product.slug),
6696
- children: product.name
6697
- },
6698
- product.slug
6699
- ))
6700
- ]
6701
- }
6702
- ),
6703
- products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
6704
- "button",
6705
- {
6706
- type: "button",
6707
- "data-boff-explore": "products-toggle",
6708
- onClick: () => setExpanded((v) => !v),
6709
- 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",
6710
- children: expanded ? "Show less" : `All ${products.length}`
6711
- }
6712
- ) : null
6713
- ] });
6831
+ products.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
6832
+ "button",
6833
+ {
6834
+ type: "button",
6835
+ "data-boff-explore": "products-toggle",
6836
+ "aria-expanded": expanded,
6837
+ onClick: () => setExpanded((v) => !v),
6838
+ 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",
6839
+ children: expanded ? "Show less" : `All ${products.length}`
6840
+ }
6841
+ ) : null
6842
+ ]
6843
+ }
6844
+ );
6714
6845
  }
6715
6846
  function DisclaimerDialog({
6716
6847
  open,
6717
6848
  onClose,
6718
6849
  captchaOn
6719
6850
  }) {
6851
+ const panelRef = React.useRef(null);
6720
6852
  const closeRef = React.useRef(null);
6853
+ const titleId = "boff-explore-disclaimer-title";
6721
6854
  React.useEffect(() => {
6722
6855
  if (!open) return;
6856
+ const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
6857
+ const focusable = () => Array.from(
6858
+ panelRef.current?.querySelectorAll(
6859
+ 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
6860
+ ) ?? []
6861
+ );
6723
6862
  const onKey = (event) => {
6724
- if (event.key === "Escape") onClose();
6863
+ if (event.key === "Escape") {
6864
+ onClose();
6865
+ return;
6866
+ }
6867
+ if (event.key !== "Tab") return;
6868
+ const items = focusable();
6869
+ if (items.length === 0) return;
6870
+ const first = items[0];
6871
+ const last = items[items.length - 1];
6872
+ const active = document.activeElement;
6873
+ if (event.shiftKey && (active === first || !panelRef.current?.contains(active))) {
6874
+ event.preventDefault();
6875
+ last?.focus();
6876
+ } else if (!event.shiftKey && active === last) {
6877
+ event.preventDefault();
6878
+ first?.focus();
6879
+ }
6725
6880
  };
6726
6881
  document.addEventListener("keydown", onKey);
6727
6882
  closeRef.current?.focus();
6728
- return () => document.removeEventListener("keydown", onKey);
6883
+ return () => {
6884
+ document.removeEventListener("keydown", onKey);
6885
+ opener?.focus();
6886
+ };
6729
6887
  }, [open, onClose]);
6730
6888
  if (!open) return null;
6731
6889
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -6734,19 +6892,34 @@ function DisclaimerDialog({
6734
6892
  "data-boff-explore": "disclaimer-dialog",
6735
6893
  role: "dialog",
6736
6894
  "aria-modal": "true",
6737
- "aria-label": "How this assistant works",
6895
+ "aria-labelledby": titleId,
6738
6896
  className: "fixed inset-0 z-50 flex items-end justify-center bg-foreground/40 p-4 backdrop-blur-sm sm:items-center",
6739
6897
  onClick: onClose,
6740
6898
  children: /* @__PURE__ */ jsxRuntime.jsxs(
6741
6899
  "div",
6742
6900
  {
6901
+ ref: panelRef,
6743
6902
  className: "w-full max-w-md rounded-2xl border border-border bg-card p-5 shadow-xl",
6744
6903
  onClick: (event) => event.stopPropagation(),
6745
6904
  children: [
6746
6905
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
6747
- /* @__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" }) }),
6906
+ /* @__PURE__ */ jsxRuntime.jsx(
6907
+ "span",
6908
+ {
6909
+ "aria-hidden": "true",
6910
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary",
6911
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "h-4 w-4" })
6912
+ }
6913
+ ),
6748
6914
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
6749
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-card-foreground", children: "How this assistant works" }),
6915
+ /* @__PURE__ */ jsxRuntime.jsx(
6916
+ "p",
6917
+ {
6918
+ id: titleId,
6919
+ className: "text-sm font-semibold text-card-foreground",
6920
+ children: "How this assistant works"
6921
+ }
6922
+ ),
6750
6923
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 space-y-2 text-xs leading-relaxed text-muted-foreground", children: [
6751
6924
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Answers are generated from our public documentation and can be imperfect \u2014 check anything important against the linked sources." }),
6752
6925
  /* @__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." }),
@@ -6858,7 +7031,9 @@ function ExplorePage({
6858
7031
  history,
6859
7032
  openConversation,
6860
7033
  deleteConversation,
6861
- clearHistory
7034
+ clearHistory,
7035
+ recordClick,
7036
+ createShareLink
6862
7037
  } = useExploreChat({ productSlug, pageUrl, onSend, examplePrompts });
6863
7038
  const [draft, setDraft] = React.useState("");
6864
7039
  const textareaRef = React.useRef(null);
@@ -6878,6 +7053,7 @@ function ExplorePage({
6878
7053
  });
6879
7054
  speechStopRef.current = speech.stop;
6880
7055
  const [disclaimerOpen, setDisclaimerOpen] = React.useState(false);
7056
+ const [shareLabel, setShareLabel] = React.useState("Share");
6881
7057
  const scrollRef = React.useRef(null);
6882
7058
  const latestAssistantRef = React.useRef(null);
6883
7059
  const alignedForRef = React.useRef(null);
@@ -6932,6 +7108,25 @@ function ExplorePage({
6932
7108
  if (!el) return;
6933
7109
  el.scrollTop = el.scrollHeight;
6934
7110
  }, [activity, latestAssistantId, messages]);
7111
+ const handleShare = React.useCallback(async () => {
7112
+ setShareLabel("Copying\u2026");
7113
+ const token = await createShareLink();
7114
+ if (!token) {
7115
+ setShareLabel("Unavailable");
7116
+ setTimeout(() => setShareLabel("Share"), 2500);
7117
+ return;
7118
+ }
7119
+ const base = (pageUrl ?? window.location.href).split("?")[0];
7120
+ const url = `${base}?c=${encodeURIComponent(token)}`;
7121
+ try {
7122
+ await navigator.clipboard.writeText(url);
7123
+ setShareLabel("Copied");
7124
+ } catch {
7125
+ window.prompt("Copy this link", url);
7126
+ setShareLabel("Share");
7127
+ }
7128
+ setTimeout(() => setShareLabel("Share"), 2500);
7129
+ }, [createShareLink, pageUrl]);
6935
7130
  const submitDraft = React.useCallback(() => {
6936
7131
  const text = draft.trim();
6937
7132
  if (!text || overLimit || busy || !canSend) return;
@@ -7014,6 +7209,24 @@ function ExplorePage({
7014
7209
  ]
7015
7210
  }
7016
7211
  ),
7212
+ messages.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
7213
+ Button,
7214
+ {
7215
+ "data-boff-explore": "share",
7216
+ type: "button",
7217
+ size: "sm",
7218
+ variant: "ghost",
7219
+ className: "shrink-0 text-muted-foreground",
7220
+ title: "Copy a read-only link to this conversation",
7221
+ onClick: () => {
7222
+ void handleShare();
7223
+ },
7224
+ children: [
7225
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link2, { className: "h-3.5 w-3.5" }),
7226
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: shareLabel })
7227
+ ]
7228
+ }
7229
+ ) : null,
7017
7230
  history.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
7018
7231
  Button,
7019
7232
  {
@@ -7132,7 +7345,9 @@ function ExplorePage({
7132
7345
  message,
7133
7346
  activity,
7134
7347
  onNavigate,
7135
- anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0
7348
+ anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
7349
+ onFollow: (kind, url) => recordClick(message.id, kind, url),
7350
+ onFollowUp: sendPrompt
7136
7351
  },
7137
7352
  message.id
7138
7353
  )