@burdenoff/website-sdk 2026.925.2 → 2026.926.1

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.
Files changed (67) hide show
  1. package/dist/components/contact.js +8 -0
  2. package/dist/components/contact.js.map +1 -1
  3. package/dist/components/contact.mjs +8 -0
  4. package/dist/components/contact.mjs.map +1 -1
  5. package/dist/components/explore.d.mts +1 -1
  6. package/dist/components/explore.d.ts +1 -1
  7. package/dist/components/explore.js +1101 -518
  8. package/dist/components/explore.js.map +1 -1
  9. package/dist/components/explore.mjs +1103 -521
  10. package/dist/components/explore.mjs.map +1 -1
  11. package/dist/components/newsletter.js +8 -0
  12. package/dist/components/newsletter.js.map +1 -1
  13. package/dist/components/newsletter.mjs +8 -0
  14. package/dist/components/newsletter.mjs.map +1 -1
  15. package/dist/components/partners.js +11 -2
  16. package/dist/components/partners.js.map +1 -1
  17. package/dist/components/partners.mjs +11 -2
  18. package/dist/components/partners.mjs.map +1 -1
  19. package/dist/components/resource-links.js +8 -0
  20. package/dist/components/resource-links.js.map +1 -1
  21. package/dist/components/resource-links.mjs +8 -0
  22. package/dist/components/resource-links.mjs.map +1 -1
  23. package/dist/components/social-links.js +8 -0
  24. package/dist/components/social-links.js.map +1 -1
  25. package/dist/components/social-links.mjs +8 -0
  26. package/dist/components/social-links.mjs.map +1 -1
  27. package/dist/components/waitlist.js +8 -0
  28. package/dist/components/waitlist.js.map +1 -1
  29. package/dist/components/waitlist.mjs +8 -0
  30. package/dist/components/waitlist.mjs.map +1 -1
  31. package/dist/content/index.d.mts +1 -1
  32. package/dist/content/index.d.ts +1 -1
  33. package/dist/content/index.js +11 -2
  34. package/dist/content/index.js.map +1 -1
  35. package/dist/content/index.mjs +11 -2
  36. package/dist/content/index.mjs.map +1 -1
  37. package/dist/{explore-C0RFTKAi.d.ts → explore-Cam_ElSJ.d.ts} +70 -12
  38. package/dist/{explore-DzOf7n4m.d.mts → explore-hTQm-_SF.d.mts} +70 -12
  39. package/dist/hooks/index.js +51 -13
  40. package/dist/hooks/index.js.map +1 -1
  41. package/dist/hooks/index.mjs +51 -13
  42. package/dist/hooks/index.mjs.map +1 -1
  43. package/dist/{index-BMWQNZDX.d.mts → index-gm3pBCJT.d.mts} +8 -2
  44. package/dist/{index-BqHF-G-S.d.ts → index-ySbOLSD5.d.ts} +8 -2
  45. package/dist/index.d.mts +32 -11
  46. package/dist/index.d.ts +32 -11
  47. package/dist/index.js +901 -267
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +892 -269
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/product/index.d.mts +16 -1
  52. package/dist/product/index.d.ts +16 -1
  53. package/dist/product/index.js +61 -17
  54. package/dist/product/index.js.map +1 -1
  55. package/dist/product/index.mjs +61 -18
  56. package/dist/product/index.mjs.map +1 -1
  57. package/dist/store/index.js +11 -2
  58. package/dist/store/index.js.map +1 -1
  59. package/dist/store/index.mjs +11 -2
  60. package/dist/store/index.mjs.map +1 -1
  61. package/dist/utils/index.d.mts +41 -1
  62. package/dist/utils/index.d.ts +41 -1
  63. package/dist/utils/index.js +42 -0
  64. package/dist/utils/index.js.map +1 -1
  65. package/dist/utils/index.mjs +35 -1
  66. package/dist/utils/index.mjs.map +1 -1
  67. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { createContext, useMemo, useContext, useState, useCallback, useLayoutEffect, useRef, useEffect, useId, version, Children, isValidElement, cloneElement } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
- import { Instagram, Youtube, Facebook, Github, Linkedin, Twitter, Mail, Phone, MapPin, Clock, Send, CheckCircle, Upload, Globe, ChevronDown, Search, Rocket, ArrowRight, Download, ImageIcon, ChevronRight, Maximize2, Check, ArrowLeft, Sparkles, RotateCcw, Link2, History, Trash2, Paperclip, X, Mic, Square, ArrowUp, Info, ExternalLink, ChevronLeft, BookOpenText, Layers, TriangleAlert, FileText, Copy, ThumbsUp, ThumbsDown, Volume2 } from 'lucide-react';
4
+ import { Instagram, Youtube, Facebook, Github, Linkedin, Twitter, Mail, Phone, MapPin, Clock, Send, CheckCircle, Upload, Globe, ChevronDown, Search, Rocket, ArrowRight, Download, ImageIcon, ChevronRight, Maximize2, Check, ArrowLeft, Sparkles, RotateCcw, Link2, History, Trash2, Paperclip, X, Mic, Square, ArrowUp, Info, ExternalLink, ChevronLeft, BookOpenText, Layers, TriangleAlert, FileText, Film, Play, Copy, ThumbsUp, ThumbsDown, Minimize, Maximize, VideoOff, Volume2 } from 'lucide-react';
5
5
  import ReCAPTCHA4 from 'react-google-recaptcha';
6
6
  import { toast } from 'sonner';
7
7
  import { Helmet } from 'react-helmet-async';
@@ -18,6 +18,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
18
18
  import { useForm } from 'react-hook-form';
19
19
  import { z } from 'zod';
20
20
  import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
21
+ import { createPortal } from 'react-dom';
21
22
 
22
23
  var __defProp = Object.defineProperty;
23
24
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -344,6 +345,40 @@ function useWebSDKConfig() {
344
345
  const client = useWebSDK();
345
346
  return client.getConfig();
346
347
  }
348
+
349
+ // src/utils/product-slug.ts
350
+ var PRODUCT_SLUG_RENAMES = Object.freeze(
351
+ [Object.freeze({ legacy: "botlit", current: "botmanor" })]
352
+ );
353
+ var PRODUCT_SLUG_ALIASES = Object.freeze(
354
+ Object.fromEntries(PRODUCT_SLUG_RENAMES.map((r) => [r.legacy, r.current]))
355
+ );
356
+ var PRODUCT_SLUG_EMIT = Object.freeze({ productSlug: "legacy" });
357
+ function findRename(slug) {
358
+ const wanted = slug.toLowerCase();
359
+ return PRODUCT_SLUG_RENAMES.find(
360
+ (r) => r.legacy === wanted || r.current === wanted
361
+ );
362
+ }
363
+ function normalizeProductSlug(slug) {
364
+ if (slug == null) return slug;
365
+ return findRename(slug)?.current ?? slug;
366
+ }
367
+ function legacyProductSlug(slug) {
368
+ return findRename(slug)?.legacy ?? slug;
369
+ }
370
+ function productSlugVariants(slug) {
371
+ return [
372
+ .../* @__PURE__ */ new Set([slug, normalizeProductSlug(slug), legacyProductSlug(slug)])
373
+ ];
374
+ }
375
+ function isSameProductSlug(a, b) {
376
+ if (a == null || b == null) return a === b;
377
+ return a === b || normalizeProductSlug(a) === normalizeProductSlug(b);
378
+ }
379
+ function emitProductSlug(slug) {
380
+ return PRODUCT_SLUG_EMIT.productSlug === "current" ? normalizeProductSlug(slug) : legacyProductSlug(slug);
381
+ }
347
382
  var PRODUCT_FIELDS_BASE = `
348
383
  id
349
384
  name
@@ -391,6 +426,35 @@ var ProductContext = createContext({
391
426
  refetch: () => {
392
427
  }
393
428
  });
429
+ async function queryPublicProduct(client, slug) {
430
+ const result = await client.query(
431
+ PUBLIC_PRODUCT_QUERY,
432
+ { slug }
433
+ );
434
+ const schemaMissingResourceLinks = result.errors?.some(
435
+ (e) => /Cannot query field ["']?resourceLinks["']?/i.test(e?.message ?? "")
436
+ );
437
+ if (!schemaMissingResourceLinks) return result;
438
+ return client.query(
439
+ PUBLIC_PRODUCT_QUERY_FALLBACK,
440
+ { slug }
441
+ );
442
+ }
443
+ async function fetchPublicProduct(client, slug) {
444
+ for (const candidate of productSlugVariants(slug)) {
445
+ const result = await queryPublicProduct(client, candidate);
446
+ if (result.errors?.length) {
447
+ return {
448
+ product: null,
449
+ error: result.errors[0]?.message ?? "Unknown error"
450
+ };
451
+ }
452
+ if (result.data?.publicProduct) {
453
+ return { product: result.data.publicProduct, error: null };
454
+ }
455
+ }
456
+ return { product: null, error: null };
457
+ }
394
458
  function ProductProvider({ children, fallback }) {
395
459
  const client = useWebSDK();
396
460
  const config = useWebSDKConfig();
@@ -407,23 +471,11 @@ function ProductProvider({ children, fallback }) {
407
471
  setLoading(true);
408
472
  setError(null);
409
473
  try {
410
- let result = await client.query(
411
- PUBLIC_PRODUCT_QUERY,
412
- { slug: config.productSlug }
413
- );
414
- const schemaMissingResourceLinks = result.errors?.some(
415
- (e) => /Cannot query field ["']?resourceLinks["']?/i.test(e?.message ?? "")
416
- );
417
- if (schemaMissingResourceLinks) {
418
- result = await client.query(
419
- PUBLIC_PRODUCT_QUERY_FALLBACK,
420
- { slug: config.productSlug }
421
- );
422
- }
423
- if (result.errors?.length) {
424
- setError(result.errors[0]?.message ?? "Unknown error");
425
- } else if (result.data?.publicProduct) {
426
- setProduct(result.data.publicProduct);
474
+ const outcome = await fetchPublicProduct(client, config.productSlug);
475
+ if (outcome.error) {
476
+ setError(outcome.error);
477
+ } else if (outcome.product) {
478
+ setProduct(outcome.product);
427
479
  }
428
480
  } catch (err) {
429
481
  setError(err instanceof Error ? err.message : "Failed to fetch product");
@@ -1108,13 +1160,14 @@ function Markdown({
1108
1160
  children,
1109
1161
  className,
1110
1162
  inline,
1111
- components
1163
+ components,
1164
+ rehypePlugins
1112
1165
  }) {
1113
1166
  const content = /* @__PURE__ */ jsx(
1114
1167
  ReactMarkdown,
1115
1168
  {
1116
1169
  remarkPlugins: [remarkGfm],
1117
- rehypePlugins: [rehypeSanitize],
1170
+ rehypePlugins: [rehypeSanitize, ...rehypePlugins ?? []],
1118
1171
  components: { ...defaultComponents, ...components },
1119
1172
  ...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
1120
1173
  children
@@ -6135,6 +6188,13 @@ var EN = {
6135
6188
  readAloud: "Read aloud",
6136
6189
  stopReading: "Stop reading",
6137
6190
  answerTable: "Table",
6191
+ productVideo: "Product video",
6192
+ productVideoTitle: "{product} product video",
6193
+ playVideo: "Play {title}",
6194
+ closeVideo: "Close video",
6195
+ enterFullScreen: "Full screen",
6196
+ exitFullScreen: "Exit full screen",
6197
+ videoUnavailable: "This video can't be played right now.",
6138
6198
  groundedIn: "What this answer is based on",
6139
6199
  sourceCountOne: "1 source",
6140
6200
  sourceCountMany: "{count} sources",
@@ -6236,6 +6296,13 @@ var ZH = {
6236
6296
  readAloud: "\u6717\u8BFB",
6237
6297
  stopReading: "\u505C\u6B62\u6717\u8BFB",
6238
6298
  answerTable: "\u8868\u683C",
6299
+ productVideo: "\u4EA7\u54C1\u89C6\u9891",
6300
+ productVideoTitle: "{product} \u4EA7\u54C1\u89C6\u9891",
6301
+ playVideo: "\u64AD\u653E {title}",
6302
+ closeVideo: "\u5173\u95ED\u89C6\u9891",
6303
+ enterFullScreen: "\u5168\u5C4F",
6304
+ exitFullScreen: "\u9000\u51FA\u5168\u5C4F",
6305
+ videoUnavailable: "\u6682\u65F6\u65E0\u6CD5\u64AD\u653E\u6B64\u89C6\u9891\u3002",
6239
6306
  groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
6240
6307
  sourceCountOne: "1 \u4E2A\u6765\u6E90",
6241
6308
  sourceCountMany: "{count} \u4E2A\u6765\u6E90",
@@ -6333,6 +6400,13 @@ var HI = {
6333
6400
  readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
6334
6401
  stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
6335
6402
  answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
6403
+ productVideo: "\u0909\u0924\u094D\u092A\u093E\u0926 \u0935\u0940\u0921\u093F\u092F\u094B",
6404
+ productVideoTitle: "{product} \u0909\u0924\u094D\u092A\u093E\u0926 \u0935\u0940\u0921\u093F\u092F\u094B",
6405
+ playVideo: "{title} \u091A\u0932\u093E\u090F\u0901",
6406
+ closeVideo: "\u0935\u0940\u0921\u093F\u092F\u094B \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
6407
+ enterFullScreen: "\u092A\u0942\u0930\u094D\u0923 \u0938\u094D\u0915\u094D\u0930\u0940\u0928",
6408
+ exitFullScreen: "\u092A\u0942\u0930\u094D\u0923 \u0938\u094D\u0915\u094D\u0930\u0940\u0928 \u0938\u0947 \u092C\u093E\u0939\u0930 \u0928\u093F\u0915\u0932\u0947\u0902",
6409
+ videoUnavailable: "\u092F\u0939 \u0935\u0940\u0921\u093F\u092F\u094B \u0905\u092D\u0940 \u0928\u0939\u0940\u0902 \u091A\u0932 \u0938\u0915\u0924\u093E\u0964",
6336
6410
  groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
6337
6411
  sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
6338
6412
  sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
@@ -6434,6 +6508,13 @@ var ES = {
6434
6508
  readAloud: "Leer en voz alta",
6435
6509
  stopReading: "Dejar de leer",
6436
6510
  answerTable: "Tabla",
6511
+ productVideo: "V\xEDdeo del producto",
6512
+ productVideoTitle: "V\xEDdeo de {product}",
6513
+ playVideo: "Reproducir {title}",
6514
+ closeVideo: "Cerrar v\xEDdeo",
6515
+ enterFullScreen: "Pantalla completa",
6516
+ exitFullScreen: "Salir de pantalla completa",
6517
+ videoUnavailable: "Ahora mismo no se puede reproducir este v\xEDdeo.",
6437
6518
  groundedIn: "En qu\xE9 se basa esta respuesta",
6438
6519
  sourceCountOne: "1 fuente",
6439
6520
  sourceCountMany: "{count} fuentes",
@@ -6535,6 +6616,13 @@ var AR = {
6535
6616
  readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
6536
6617
  stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
6537
6618
  answerTable: "\u062C\u062F\u0648\u0644",
6619
+ productVideo: "\u0641\u064A\u062F\u064A\u0648 \u0627\u0644\u0645\u0646\u062A\u062C",
6620
+ productVideoTitle: "\u0641\u064A\u062F\u064A\u0648 {product}",
6621
+ playVideo: "\u062A\u0634\u063A\u064A\u0644 {title}",
6622
+ closeVideo: "\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0641\u064A\u062F\u064A\u0648",
6623
+ enterFullScreen: "\u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629",
6624
+ exitFullScreen: "\u0627\u0644\u062E\u0631\u0648\u062C \u0645\u0646 \u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629",
6625
+ videoUnavailable: "\u062A\u0639\u0630\u0651\u0631 \u062A\u0634\u063A\u064A\u0644 \u0647\u0630\u0627 \u0627\u0644\u0641\u064A\u062F\u064A\u0648 \u0627\u0644\u0622\u0646.",
6538
6626
  groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
6539
6627
  sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
6540
6628
  sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
@@ -6636,6 +6724,13 @@ var TA = {
6636
6724
  readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
6637
6725
  stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
6638
6726
  answerTable: "\u0B85\u0B9F\u0BCD\u0B9F\u0BB5\u0BA3\u0BC8",
6727
+ productVideo: "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1 \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB",
6728
+ productVideoTitle: "{product} \u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1 \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB",
6729
+ playVideo: "{title} \u0B87\u0BAF\u0B95\u0BCD\u0B95\u0BC1",
6730
+ closeVideo: "\u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB\u0BB5\u0BC8 \u0BAE\u0BC2\u0B9F\u0BC1",
6731
+ enterFullScreen: "\u0BAE\u0BC1\u0BB4\u0BC1\u0BA4\u0BCD \u0BA4\u0BBF\u0BB0\u0BC8",
6732
+ exitFullScreen: "\u0BAE\u0BC1\u0BB4\u0BC1\u0BA4\u0BCD \u0BA4\u0BBF\u0BB0\u0BC8\u0BAF\u0BBF\u0BB2\u0BBF\u0BB0\u0BC1\u0BA8\u0BCD\u0BA4\u0BC1 \u0BB5\u0BC6\u0BB3\u0BBF\u0BAF\u0BC7\u0BB1\u0BC1",
6733
+ videoUnavailable: "\u0B87\u0BA8\u0BCD\u0BA4 \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB\u0BB5\u0BC8 \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B87\u0BAF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
6639
6734
  groundedIn: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B8E\u0BA4\u0BC8 \u0B85\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0B9F\u0BC8\u0BAF\u0BBE\u0B95\u0B95\u0BCD \u0B95\u0BCA\u0BA3\u0BCD\u0B9F\u0BA4\u0BC1",
6640
6735
  sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
6641
6736
  sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
@@ -6925,7 +7020,14 @@ function answerStrings(locale) {
6925
7020
  ...chrome,
6926
7021
  answerTable: explore.answerTable,
6927
7022
  conceptIllustration: explore.conceptIllustration,
6928
- opensInNewTab: explore.opensInNewTab
7023
+ opensInNewTab: explore.opensInNewTab,
7024
+ productVideo: explore.productVideo,
7025
+ productVideoTitle: explore.productVideoTitle,
7026
+ playVideo: explore.playVideo,
7027
+ closeVideo: explore.closeVideo,
7028
+ enterFullScreen: explore.enterFullScreen,
7029
+ exitFullScreen: explore.exitFullScreen,
7030
+ videoUnavailable: explore.videoUnavailable
6929
7031
  };
6930
7032
  }
6931
7033
  var DATE_TAGS = {
@@ -6993,6 +7095,253 @@ function answerSourceHost(url) {
6993
7095
  return "";
6994
7096
  }
6995
7097
  }
7098
+ function fullscreenSupport() {
7099
+ if (typeof document === "undefined") return null;
7100
+ if (document.fullscreenEnabled && typeof document.documentElement.requestFullscreen === "function") {
7101
+ return "element";
7102
+ }
7103
+ if (typeof HTMLVideoElement !== "undefined" && "webkitEnterFullscreen" in HTMLVideoElement.prototype) {
7104
+ return "video";
7105
+ }
7106
+ return null;
7107
+ }
7108
+ function ignoreRejection(result) {
7109
+ result?.catch(() => void 0);
7110
+ }
7111
+ var VIDEO_DIALOG_CSS = `
7112
+ .boff-explore-video-panel:fullscreen {
7113
+ display: flex;
7114
+ flex-direction: column;
7115
+ gap: 0.75rem;
7116
+ padding: 1rem;
7117
+ background-color: hsl(var(--background));
7118
+ color: hsl(var(--foreground));
7119
+ }
7120
+ .boff-explore-video-panel:fullscreen .boff-explore-video-frame {
7121
+ display: flex;
7122
+ flex: 1 1 auto;
7123
+ min-height: 0;
7124
+ align-items: center;
7125
+ justify-content: center;
7126
+ border: 0;
7127
+ border-radius: 0;
7128
+ box-shadow: none;
7129
+ background-color: hsl(var(--background));
7130
+ }
7131
+ .boff-explore-video-panel:fullscreen video {
7132
+ width: 100%;
7133
+ height: 100%;
7134
+ max-height: none;
7135
+ }
7136
+ `;
7137
+ var DIALOG_BUTTON = "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-border bg-card text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
7138
+ function ExploreVideoDialog({
7139
+ videoUrl,
7140
+ posterUrl,
7141
+ title,
7142
+ strings: T,
7143
+ dir,
7144
+ onClose
7145
+ }) {
7146
+ const dialogRef = useRef(null);
7147
+ const panelRef = useRef(null);
7148
+ const videoRef = useRef(null);
7149
+ const closeRef = useRef(null);
7150
+ const nativeModalRef = useRef(false);
7151
+ const titleId = useId();
7152
+ const [support] = useState(fullscreenSupport);
7153
+ const [fullscreen, setFullscreen] = useState(false);
7154
+ const [failed, setFailed] = useState(false);
7155
+ useEffect(() => {
7156
+ const dialog = dialogRef.current;
7157
+ if (!dialog) return;
7158
+ if (typeof dialog.showModal === "function") {
7159
+ if (!dialog.open) dialog.showModal();
7160
+ nativeModalRef.current = true;
7161
+ } else {
7162
+ dialog.setAttribute("open", "");
7163
+ nativeModalRef.current = false;
7164
+ }
7165
+ closeRef.current?.focus();
7166
+ ignoreRejection(videoRef.current?.play());
7167
+ return () => {
7168
+ if (dialog.open && typeof dialog.close === "function") dialog.close();
7169
+ };
7170
+ }, []);
7171
+ useEffect(() => {
7172
+ const sync = () => {
7173
+ const current = document.fullscreenElement;
7174
+ setFullscreen(
7175
+ current !== null && dialogRef.current?.contains(current) === true
7176
+ );
7177
+ };
7178
+ const enter = () => setFullscreen(true);
7179
+ const leave = () => setFullscreen(false);
7180
+ const video = videoRef.current;
7181
+ document.addEventListener("fullscreenchange", sync);
7182
+ video?.addEventListener("webkitbeginfullscreen", enter);
7183
+ video?.addEventListener("webkitendfullscreen", leave);
7184
+ return () => {
7185
+ document.removeEventListener("fullscreenchange", sync);
7186
+ video?.removeEventListener("webkitbeginfullscreen", enter);
7187
+ video?.removeEventListener("webkitendfullscreen", leave);
7188
+ };
7189
+ }, [failed]);
7190
+ const leaveFullscreen = useCallback(() => {
7191
+ const current = document.fullscreenElement;
7192
+ if (current && dialogRef.current?.contains(current) && typeof document.exitFullscreen === "function") {
7193
+ ignoreRejection(document.exitFullscreen());
7194
+ }
7195
+ const video = videoRef.current;
7196
+ if (video?.webkitDisplayingFullscreen) video.webkitExitFullscreen?.();
7197
+ }, []);
7198
+ const requestClose = useCallback(() => {
7199
+ videoRef.current?.pause();
7200
+ leaveFullscreen();
7201
+ onClose();
7202
+ }, [leaveFullscreen, onClose]);
7203
+ const toggleFullscreen = useCallback(() => {
7204
+ if (fullscreen) {
7205
+ leaveFullscreen();
7206
+ return;
7207
+ }
7208
+ if (support === "element") {
7209
+ const panel = panelRef.current;
7210
+ if (panel) ignoreRejection(panel.requestFullscreen());
7211
+ return;
7212
+ }
7213
+ const video = videoRef.current;
7214
+ video?.webkitEnterFullscreen?.();
7215
+ }, [fullscreen, leaveFullscreen, support]);
7216
+ const handleClose = useCallback(
7217
+ (event) => {
7218
+ if (event.currentTarget.open) return;
7219
+ requestClose();
7220
+ },
7221
+ [requestClose]
7222
+ );
7223
+ const handleKeyDown = useCallback(
7224
+ (event) => {
7225
+ if (event.key === "Escape" && !nativeModalRef.current) {
7226
+ event.preventDefault();
7227
+ requestClose();
7228
+ }
7229
+ },
7230
+ [requestClose]
7231
+ );
7232
+ const handleStageClick = useCallback(
7233
+ (event) => {
7234
+ if (event.target === event.currentTarget) requestClose();
7235
+ },
7236
+ [requestClose]
7237
+ );
7238
+ if (typeof document === "undefined") return null;
7239
+ const fullscreenLabel = fullscreen ? T.exitFullScreen : T.enterFullScreen;
7240
+ return createPortal(
7241
+ /* @__PURE__ */ jsxs(
7242
+ "dialog",
7243
+ {
7244
+ ref: dialogRef,
7245
+ "data-boff-explore": "video-dialog",
7246
+ "aria-labelledby": titleId,
7247
+ dir,
7248
+ onClose: handleClose,
7249
+ onKeyDown: handleKeyDown,
7250
+ className: "fixed inset-0 z-50 m-0 h-full max-h-none w-full max-w-none overflow-y-auto overscroll-contain border-0 bg-background/95 p-0 text-foreground backdrop-blur-sm",
7251
+ children: [
7252
+ /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: VIDEO_DIALOG_CSS } }),
7253
+ /* @__PURE__ */ jsx(
7254
+ "div",
7255
+ {
7256
+ onClick: handleStageClick,
7257
+ className: "flex min-h-full items-center justify-center p-4 sm:p-6",
7258
+ children: /* @__PURE__ */ jsxs(
7259
+ "div",
7260
+ {
7261
+ ref: panelRef,
7262
+ className: "boff-explore-video-panel flex w-full min-w-0 max-w-[960px] flex-col gap-3",
7263
+ children: [
7264
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
7265
+ /* @__PURE__ */ jsx(
7266
+ "h2",
7267
+ {
7268
+ id: titleId,
7269
+ className: "min-w-0 flex-1 truncate text-sm font-semibold text-foreground sm:text-base",
7270
+ children: title
7271
+ }
7272
+ ),
7273
+ support !== null && !failed ? (
7274
+ // A plain button whose name says what pressing it does NOW, and no
7275
+ // `aria-pressed`: a toggle button's name must not change with its state, or it
7276
+ // announces "Exit full screen, pressed" — two answers to one question.
7277
+ /* @__PURE__ */ jsx(
7278
+ "button",
7279
+ {
7280
+ type: "button",
7281
+ "data-boff-explore": "video-fullscreen",
7282
+ "aria-label": fullscreenLabel,
7283
+ title: fullscreenLabel,
7284
+ onClick: toggleFullscreen,
7285
+ className: DIALOG_BUTTON,
7286
+ children: fullscreen ? /* @__PURE__ */ jsx(Minimize, { "aria-hidden": "true", className: "h-5 w-5" }) : /* @__PURE__ */ jsx(Maximize, { "aria-hidden": "true", className: "h-5 w-5" })
7287
+ }
7288
+ )
7289
+ ) : null,
7290
+ /* @__PURE__ */ jsx(
7291
+ "button",
7292
+ {
7293
+ ref: closeRef,
7294
+ type: "button",
7295
+ "data-boff-explore": "video-close",
7296
+ "aria-label": T.closeVideo,
7297
+ title: T.closeVideo,
7298
+ onClick: requestClose,
7299
+ className: DIALOG_BUTTON,
7300
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "h-5 w-5" })
7301
+ }
7302
+ )
7303
+ ] }),
7304
+ /* @__PURE__ */ jsx("div", { className: "boff-explore-video-frame overflow-hidden rounded-xl border border-border bg-muted shadow-lg", children: failed ? (
7305
+ // A black box with a spinning control tells the visitor nothing; this says
7306
+ // what happened, in their language.
7307
+ /* @__PURE__ */ jsxs(
7308
+ "div",
7309
+ {
7310
+ role: "alert",
7311
+ className: "flex aspect-video w-full flex-col items-center justify-center gap-2 p-6 text-center text-sm text-muted-foreground",
7312
+ children: [
7313
+ /* @__PURE__ */ jsx(VideoOff, { "aria-hidden": "true", className: "h-6 w-6" }),
7314
+ /* @__PURE__ */ jsx("p", { children: T.videoUnavailable })
7315
+ ]
7316
+ }
7317
+ )
7318
+ ) : /* @__PURE__ */ jsx(
7319
+ "video",
7320
+ {
7321
+ ref: videoRef,
7322
+ "data-boff-explore": "video-player",
7323
+ src: videoUrl,
7324
+ poster: posterUrl,
7325
+ controls: true,
7326
+ controlsList: support === "element" ? "nofullscreen" : void 0,
7327
+ playsInline: true,
7328
+ autoPlay: true,
7329
+ preload: "metadata",
7330
+ onError: () => setFailed(true),
7331
+ className: "block max-h-[80vh] w-full bg-muted object-contain"
7332
+ }
7333
+ ) })
7334
+ ]
7335
+ }
7336
+ )
7337
+ }
7338
+ )
7339
+ ]
7340
+ }
7341
+ ),
7342
+ document.body
7343
+ );
7344
+ }
6996
7345
  var EXPLORE_ANSWER_CSS = `
6997
7346
  /* Nothing inside an answer may widen the page. Model output carries long URLs, code and
6998
7347
  tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
@@ -7025,6 +7374,14 @@ var EXPLORE_ANSWER_CSS = `
7025
7374
  max-width: 100%;
7026
7375
  height: auto;
7027
7376
  }
7377
+ /* A product-video poster fills the card's reserved 16:9 frame and is cropped to it. The
7378
+ blanket \`img { height: auto }\` above \u2014 and ProseStyles' own \u2014 outrank a height utility,
7379
+ and would size it by its own aspect ratio instead, leaving a band of the frame showing
7380
+ whenever a poster is not exactly 16:9. */
7381
+ .boff-explore-body .boff-explore-video-poster {
7382
+ height: 100%;
7383
+ max-width: none;
7384
+ }
7028
7385
 
7029
7386
  /* ---- Comparison and pricing tables ----------------------------------------------------
7030
7387
  The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
@@ -7155,28 +7512,81 @@ function inlineImageOrigins(products) {
7155
7512
  }
7156
7513
  return origins;
7157
7514
  }
7158
- function isInlineImageAllowed(src, origins) {
7159
- if (typeof src !== "string" || src === "") return false;
7515
+ var VIDEO_FILE_PATH = /\.(?:mp4|webm|m4v|mov|vtt)$/i;
7516
+ function catalogUrlOf(value, origins) {
7517
+ if (typeof value !== "string" || value === "") return null;
7160
7518
  let url;
7161
7519
  try {
7162
- url = new URL(src);
7520
+ url = new URL(value);
7521
+ } catch {
7522
+ return null;
7523
+ }
7524
+ if (url.protocol !== "https:" || url.username || url.password) return null;
7525
+ return origins.has(url.origin) ? url : null;
7526
+ }
7527
+ function isInlineImageAllowed(src, origins) {
7528
+ const url = catalogUrlOf(src, origins);
7529
+ return url !== null && !VIDEO_FILE_PATH.test(url.pathname);
7530
+ }
7531
+ var INLINE_VIDEO_PATH = /\.(?:mp4|webm)$/i;
7532
+ function isInlineVideoAllowed(href, origins) {
7533
+ const url = catalogUrlOf(href, origins);
7534
+ return url !== null && INLINE_VIDEO_PATH.test(url.pathname);
7535
+ }
7536
+ function apexOriginOf(url) {
7537
+ const apex = url.hostname.replace(/^www\./, "");
7538
+ const port = url.port ? `:${url.port}` : "";
7539
+ return `https://${apex}${port}`;
7540
+ }
7541
+ function productSlugsByOrigin(products) {
7542
+ const slugs = /* @__PURE__ */ new Map();
7543
+ for (const product of products) {
7544
+ let url;
7545
+ try {
7546
+ url = new URL(product.website);
7547
+ } catch {
7548
+ continue;
7549
+ }
7550
+ if (url.protocol !== "https:") continue;
7551
+ const key = apexOriginOf(url);
7552
+ if (!slugs.has(key)) slugs.set(key, product.slug);
7553
+ }
7554
+ return slugs;
7555
+ }
7556
+ function videoProductOf(href, slugs) {
7557
+ const url = new URL(href);
7558
+ return slugs.get(apexOriginOf(url)) ?? url.hostname.replace(/^www\./, "");
7559
+ }
7560
+ function isVideoFileTarget(href) {
7561
+ if (typeof href !== "string") return false;
7562
+ try {
7563
+ return VIDEO_FILE_PATH.test(
7564
+ new URL(href, "https://answer.invalid").pathname
7565
+ );
7163
7566
  } catch {
7164
7567
  return false;
7165
7568
  }
7166
- if (url.protocol !== "https:" || url.username || url.password) return false;
7167
- return origins.has(url.origin);
7168
7569
  }
7169
7570
  var CODE_FENCE = /^\s*(?:```|~~~)/;
7170
7571
  var TABLE_ROW_LINE = /^\s{0,3}\|/;
7171
7572
  var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
7573
+ var LINKED_IMAGE = /\[!\[([^[\]]*)\]\([^)\s]*(?:\s+"[^"]*")?\)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
7574
+ var IMAGE = /!\[[^[\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g;
7575
+ var LINK = /\[([^[\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
7172
7576
  function readableLine(raw, mode) {
7173
7577
  let line = raw;
7174
- line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
7175
- line = mode === "speech" ? (
7176
- // A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
7177
- // prompt asks for two to five links in every answer. The label IS the words.
7178
- line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
7179
- ) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
7578
+ if (line.includes("](")) {
7579
+ line = line.replace(LINKED_IMAGE, (_match, alt, href) => {
7580
+ if (mode === "speech") return "";
7581
+ return alt.trim() === "" ? href : `${alt.trim()} (${href})`;
7582
+ });
7583
+ line = line.replace(IMAGE, "");
7584
+ line = mode === "speech" ? (
7585
+ // A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
7586
+ // prompt asks for two to five links in every answer. The label IS the words.
7587
+ line.replace(LINK, "$1")
7588
+ ) : line.replace(LINK, "$1 ($2)");
7589
+ }
7180
7590
  line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
7181
7591
  line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
7182
7592
  line = line.replace(/^\s{0,3}>\s?/, "");
@@ -7267,6 +7677,21 @@ function answerSpeechText(markdown) {
7267
7677
  var AnswerScopeContext = createContext(null);
7268
7678
  var InsideLinkContext = createContext(false);
7269
7679
  function AnswerLink(props) {
7680
+ const scope = useContext(AnswerScopeContext);
7681
+ const role = videoRoleOf(props.node);
7682
+ if (role !== void 0 && scope !== null) {
7683
+ const video = role === "card" && scope.enabled ? productVideoOf(props.node) : null;
7684
+ if (video === null) return null;
7685
+ return /* @__PURE__ */ jsx(
7686
+ InlineVideoCard,
7687
+ {
7688
+ href: video.href,
7689
+ poster: video.poster,
7690
+ alt: video.alt,
7691
+ scope
7692
+ }
7693
+ );
7694
+ }
7270
7695
  return /* @__PURE__ */ jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx(MarkdownLink, { ...props }) });
7271
7696
  }
7272
7697
  var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
@@ -7378,16 +7803,173 @@ function AnswerTableRow({
7378
7803
  });
7379
7804
  return /* @__PURE__ */ jsx("tr", { ...props, children: cells });
7380
7805
  }
7806
+ function linkedImageOf(node) {
7807
+ if (node?.tagName !== "a") return null;
7808
+ const content = (node.children ?? []).filter(
7809
+ (child) => child.tagName !== void 0 || (child.value ?? "").trim() !== ""
7810
+ );
7811
+ const image = content.length === 1 ? content[0] : void 0;
7812
+ if (image?.tagName !== "img") return null;
7813
+ const alt = image.properties?.alt;
7814
+ return {
7815
+ href: node.properties?.href,
7816
+ src: image.properties?.src,
7817
+ alt: typeof alt === "string" ? alt.trim() : ""
7818
+ };
7819
+ }
7820
+ function isCardableVideo(embed, origins) {
7821
+ if (!isInlineVideoAllowed(embed.href, origins) || !isInlineImageAllowed(embed.src, origins)) {
7822
+ return false;
7823
+ }
7824
+ return apexOriginOf(new URL(embed.href)) === apexOriginOf(new URL(embed.src));
7825
+ }
7826
+ var NO_VIDEO_CONTAINERS = /* @__PURE__ */ new Set(["li", "table"]);
7827
+ function rehypeProductVideo(options) {
7828
+ return (tree) => {
7829
+ let claimed = false;
7830
+ const visit = (node, parent, contained) => {
7831
+ const embed = linkedImageOf(node);
7832
+ if (embed !== null && isVideoFileTarget(embed.href)) {
7833
+ const card = !claimed && !contained && parent?.tagName === "p" && isCardableVideo(embed, options.origins);
7834
+ if (card) claimed = true;
7835
+ const data = typeof node.data === "object" ? node.data : null;
7836
+ node.data = { ...data, boffVideo: card ? "card" : "drop" };
7837
+ return;
7838
+ }
7839
+ const inside = contained || node.tagName !== void 0 && NO_VIDEO_CONTAINERS.has(node.tagName);
7840
+ for (const child of node.children ?? []) visit(child, node, inside);
7841
+ };
7842
+ visit(tree, null, false);
7843
+ };
7844
+ }
7845
+ function videoRoleOf(node) {
7846
+ const data = node?.data;
7847
+ if (typeof data !== "object" || data === null || !("boffVideo" in data)) {
7848
+ return void 0;
7849
+ }
7850
+ return data.boffVideo === "card" || data.boffVideo === "drop" ? data.boffVideo : void 0;
7851
+ }
7852
+ function productVideoOf(node) {
7853
+ const embed = linkedImageOf(node);
7854
+ if (embed === null || typeof embed.href !== "string" || typeof embed.src !== "string") {
7855
+ return null;
7856
+ }
7857
+ return { href: embed.href, poster: embed.src, alt: embed.alt };
7858
+ }
7859
+ var CANONICAL_VIDEO_ALT = /^(.+?)\s+product video$/i;
7860
+ function videoTitleOf(alt, T) {
7861
+ const product = CANONICAL_VIDEO_ALT.exec(alt)?.[1]?.trim();
7862
+ if (product) return fill(T.productVideoTitle, { product });
7863
+ return alt === "" ? T.productVideo : alt;
7864
+ }
7865
+ var INLINE_VIDEO_FRAME = "group relative block aspect-video w-full max-w-md overflow-hidden rounded-xl border border-border bg-muted shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
7866
+ function InlineVideoCard({
7867
+ href,
7868
+ poster,
7869
+ alt,
7870
+ scope
7871
+ }) {
7872
+ const { strings: T, failed, onFailed, productSlugs } = scope;
7873
+ const [dialogDir, setDialogDir] = useState(null);
7874
+ const buttonRef = useRef(null);
7875
+ const returnFocusRef = useRef(false);
7876
+ const open = dialogDir !== null;
7877
+ useEffect(() => {
7878
+ if (open || !returnFocusRef.current) return;
7879
+ returnFocusRef.current = false;
7880
+ const button = buttonRef.current;
7881
+ if (button?.isConnected) button.focus();
7882
+ }, [open]);
7883
+ const openPlayer = useCallback((event) => {
7884
+ const dir = event.currentTarget.closest("[dir]")?.getAttribute("dir");
7885
+ returnFocusRef.current = true;
7886
+ setDialogDir(dir === "rtl" ? "rtl" : "ltr");
7887
+ }, []);
7888
+ const closePlayer = useCallback(() => setDialogDir(null), []);
7889
+ const name = videoTitleOf(alt, T);
7890
+ const posterFailed = failed.has(poster);
7891
+ return /* @__PURE__ */ jsxs(
7892
+ "span",
7893
+ {
7894
+ "data-boff-explore": "inline-video",
7895
+ "data-product": videoProductOf(href, productSlugs),
7896
+ className: "my-3 block",
7897
+ children: [
7898
+ /* @__PURE__ */ jsxs(
7899
+ "button",
7900
+ {
7901
+ ref: buttonRef,
7902
+ type: "button",
7903
+ "aria-haspopup": "dialog",
7904
+ "aria-label": fill(T.playVideo, { title: name }),
7905
+ onClick: openPlayer,
7906
+ className: INLINE_VIDEO_FRAME,
7907
+ children: [
7908
+ posterFailed ? /* @__PURE__ */ jsx(
7909
+ "span",
7910
+ {
7911
+ "aria-hidden": "true",
7912
+ className: "absolute inset-0 flex items-center justify-center bg-muted text-muted-foreground",
7913
+ children: /* @__PURE__ */ jsx(Film, { className: "h-10 w-10" })
7914
+ }
7915
+ ) : /* @__PURE__ */ jsx(
7916
+ "img",
7917
+ {
7918
+ src: poster,
7919
+ alt: "",
7920
+ loading: "lazy",
7921
+ decoding: "async",
7922
+ onError: () => onFailed(poster),
7923
+ className: "boff-explore-video-poster absolute inset-0 h-full w-full object-cover"
7924
+ }
7925
+ ),
7926
+ /* @__PURE__ */ jsx(
7927
+ "span",
7928
+ {
7929
+ "aria-hidden": "true",
7930
+ className: "pointer-events-none absolute inset-0 flex items-center justify-center",
7931
+ children: /* @__PURE__ */ jsx("span", { className: "flex h-14 w-14 items-center justify-center rounded-full border border-border bg-background/90 text-foreground shadow-md transition-transform group-hover:scale-105", children: /* @__PURE__ */ jsx(Play, { className: "h-6 w-6 fill-current" }) })
7932
+ }
7933
+ ),
7934
+ /* @__PURE__ */ jsx(
7935
+ "span",
7936
+ {
7937
+ "aria-hidden": "true",
7938
+ className: "pointer-events-none absolute start-2 top-2 rounded-full border border-border bg-background/90 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-foreground shadow-sm",
7939
+ children: T.productVideo
7940
+ }
7941
+ )
7942
+ ]
7943
+ }
7944
+ ),
7945
+ open ? /* @__PURE__ */ jsx(
7946
+ ExploreVideoDialog,
7947
+ {
7948
+ videoUrl: href,
7949
+ posterUrl: poster,
7950
+ title: name,
7951
+ strings: T,
7952
+ dir: dialogDir,
7953
+ onClose: closePlayer
7954
+ }
7955
+ ) : null
7956
+ ]
7957
+ }
7958
+ );
7959
+ }
7381
7960
  var ANSWER_MARKDOWN_COMPONENTS = {
7382
7961
  a: AnswerLink,
7383
7962
  img: InlineConceptImage,
7384
7963
  table: AnswerTable,
7385
7964
  tr: AnswerTableRow
7386
7965
  };
7966
+ var NO_PRODUCT_SLUGS = /* @__PURE__ */ new Map();
7387
7967
  function ExploreAnswerBody({
7388
7968
  children,
7389
7969
  strings,
7390
7970
  imageOrigins,
7971
+ videoOrigins = imageOrigins,
7972
+ productSlugs = NO_PRODUCT_SLUGS,
7391
7973
  enabled = true,
7392
7974
  components,
7393
7975
  className
@@ -7403,16 +7985,21 @@ function ExploreAnswerBody({
7403
7985
  strings,
7404
7986
  enabled,
7405
7987
  origins: imageOrigins,
7988
+ productSlugs,
7406
7989
  failed,
7407
7990
  onFailed
7408
7991
  }),
7409
- [strings, enabled, imageOrigins, failed, onFailed]
7992
+ [strings, enabled, imageOrigins, productSlugs, failed, onFailed]
7993
+ );
7994
+ const rehypePlugins = useMemo(
7995
+ () => [[rehypeProductVideo, { origins: videoOrigins }]],
7996
+ [videoOrigins]
7410
7997
  );
7411
7998
  const overrides = useMemo(
7412
7999
  () => components ? { ...ANSWER_MARKDOWN_COMPONENTS, ...components } : ANSWER_MARKDOWN_COMPONENTS,
7413
8000
  [components]
7414
8001
  );
7415
- return /* @__PURE__ */ jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsx(Markdown, { components: overrides, children }) }) });
8002
+ return /* @__PURE__ */ jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsx(Markdown, { components: overrides, rehypePlugins, children }) }) });
7416
8003
  }
7417
8004
  var PROSE_CSS = `
7418
8005
  .boff-prose {
@@ -7590,32 +8177,216 @@ var PROSE_CSS = `
7590
8177
  function ProseStyles() {
7591
8178
  return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: PROSE_CSS } });
7592
8179
  }
7593
- var META_DESCRIPTION_LIMIT2 = 160;
7594
- var TITLE_LIMIT = 100;
7595
- var RELATED_LIMIT2 = 4;
7596
- function normaliseSiteUrl2(siteUrl) {
7597
- return siteUrl.trim().replace(/\/+$/, "");
7598
- }
7599
- function absoluteUrl2(base, path) {
7600
- if (/^https?:\/\//i.test(path)) return path;
7601
- return `${base}${path.startsWith("/") ? path : `/${path}`}`;
7602
- }
7603
- function sortByOrder2(answers) {
7604
- return [...answers].sort(
7605
- (a, b) => a.order - b.order || a.slug.localeCompare(b.slug)
7606
- );
7607
- }
7608
- function clampText2(text, limit) {
7609
- if (text.length <= limit) return text;
7610
- const cut = text.slice(0, limit - 1);
7611
- const lastSpace = cut.lastIndexOf(" ");
7612
- return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[\s,;:—–-]+$/, "")}\u2026`;
7613
- }
7614
- function questionCount(T, count) {
7615
- return count === 1 ? T.questionCountOne : fill(T.questionCountMany, { count: String(count) });
7616
- }
7617
- function sourceCount(T, count) {
7618
- return count === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(count) });
8180
+
8181
+ // src/data/products.ts
8182
+ var ECOSYSTEM_PRODUCTS = [
8183
+ {
8184
+ slug: "workspaces",
8185
+ name: "Workspaces",
8186
+ websiteUrl: "https://burdenoff.com"
8187
+ },
8188
+ {
8189
+ slug: "vibecontrols",
8190
+ name: "VibeControls",
8191
+ websiteUrl: "https://vibecontrols.com"
8192
+ },
8193
+ // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore
8194
+ // catalog and the site's own sitemap use this origin, and so must the fallback catalog
8195
+ // built from this list, or /explore rejects FluidGrids concept images until it loads.
8196
+ {
8197
+ slug: "fluidgrids",
8198
+ name: "FluidGrids",
8199
+ websiteUrl: "https://www.fluidgrids.ai"
8200
+ },
8201
+ // BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain
8202
+ // still serves the same site but is no longer the name we link to).
8203
+ // The slug follows the botlit → botmanor rename switch (utils/product-slug.ts).
8204
+ {
8205
+ slug: emitProductSlug("botmanor"),
8206
+ name: "BotManor",
8207
+ websiteUrl: "https://www.botmanor.com"
8208
+ },
8209
+ {
8210
+ slug: "bigconsole",
8211
+ name: "BigConsole",
8212
+ websiteUrl: "https://bigconsole.com"
8213
+ },
8214
+ {
8215
+ slug: "semanticfed",
8216
+ name: "SemanticFed",
8217
+ websiteUrl: "https://semanticfed.com"
8218
+ },
8219
+ {
8220
+ slug: "intelwatchtower",
8221
+ name: "IntelWatchtower",
8222
+ websiteUrl: "https://intelwatchtower.com"
8223
+ },
8224
+ { slug: "buildmyiq", name: "BuildMyIQ", websiteUrl: "https://buildmyiq.com" },
8225
+ {
8226
+ slug: "assethandler",
8227
+ name: "AssetHandler",
8228
+ websiteUrl: "https://assethandler.com"
8229
+ },
8230
+ {
8231
+ slug: "crewfoundry",
8232
+ name: "CrewFoundry",
8233
+ websiteUrl: "https://crewfoundry.com"
8234
+ },
8235
+ {
8236
+ slug: "planmagnet",
8237
+ name: "PlanMagnet",
8238
+ websiteUrl: "https://planmagnet.com"
8239
+ },
8240
+ {
8241
+ slug: "eventfullymanaged",
8242
+ name: "EventfullyManaged",
8243
+ websiteUrl: "https://eventfullymanaged.com"
8244
+ },
8245
+ {
8246
+ slug: "headshotmarketing",
8247
+ name: "HeadshotMarketing",
8248
+ websiteUrl: "https://headshotmarketing.com"
8249
+ },
8250
+ {
8251
+ slug: "technospam",
8252
+ name: "TechnoSpam",
8253
+ websiteUrl: "https://technospam.com"
8254
+ },
8255
+ { slug: "kadaikodi", name: "Kadaikodi", websiteUrl: "https://kadaikodi.com" },
8256
+ {
8257
+ slug: "movethewheels",
8258
+ name: "MoveTheWheels",
8259
+ websiteUrl: "https://movethewheels.com"
8260
+ },
8261
+ {
8262
+ slug: "healthybowl",
8263
+ name: "Healthy Bowl",
8264
+ websiteUrl: "https://gethealthybowl.com"
8265
+ },
8266
+ { slug: "ggnomad", name: "GGNomad", websiteUrl: "https://ggnomad.com" },
8267
+ {
8268
+ slug: "timecampus",
8269
+ name: "TimeCampus",
8270
+ websiteUrl: "https://timecampus.com"
8271
+ },
8272
+ {
8273
+ slug: "manufacturedops",
8274
+ name: "ManufacturedOps",
8275
+ websiteUrl: "https://manufacturedops.com"
8276
+ },
8277
+ {
8278
+ slug: "theglobalfuel",
8279
+ name: "TheGlobalFuel",
8280
+ websiteUrl: "https://theglobalfuel.com"
8281
+ },
8282
+ {
8283
+ slug: "housingvista",
8284
+ name: "HousingVista",
8285
+ websiteUrl: "https://housingvista.com"
8286
+ },
8287
+ {
8288
+ slug: "proserviceworld",
8289
+ name: "ProServiceWorld",
8290
+ websiteUrl: "https://proserviceworld.com"
8291
+ },
8292
+ {
8293
+ slug: "mybodyshield",
8294
+ name: "MyBodyShield",
8295
+ websiteUrl: "https://mybodyshield.com"
8296
+ },
8297
+ {
8298
+ slug: "artistrybase",
8299
+ name: "ArtistryBase",
8300
+ websiteUrl: "https://artistrybase.com"
8301
+ },
8302
+ {
8303
+ slug: "theculturepost",
8304
+ name: "TheCulturePost",
8305
+ websiteUrl: "https://theculturepost.com"
8306
+ },
8307
+ { slug: "collabkin", name: "CollabKin", websiteUrl: "https://collabkin.com" },
8308
+ {
8309
+ slug: "comradecircle",
8310
+ name: "ComradeCircle",
8311
+ websiteUrl: "https://comradecircle.com"
8312
+ },
8313
+ {
8314
+ slug: "brainyrich",
8315
+ name: "BrainyRich",
8316
+ websiteUrl: "https://brainyrich.com"
8317
+ },
8318
+ {
8319
+ slug: "cosmicintersection",
8320
+ name: "CosmicIntersection",
8321
+ websiteUrl: "https://cosmicintersection.com"
8322
+ },
8323
+ {
8324
+ slug: "govcitizenhub",
8325
+ name: "GovCitizenHub",
8326
+ websiteUrl: "https://govcitizenhub.com"
8327
+ },
8328
+ {
8329
+ slug: "adaptercloud",
8330
+ name: "AdapterCloud",
8331
+ websiteUrl: "https://adaptercloud.com"
8332
+ },
8333
+ {
8334
+ slug: "subscriberbot",
8335
+ name: "Subscriber Bot",
8336
+ websiteUrl: "https://subscriberbot.com"
8337
+ },
8338
+ {
8339
+ slug: "hookmovies",
8340
+ name: "HookMovies",
8341
+ websiteUrl: "https://hookmovies.com"
8342
+ },
8343
+ {
8344
+ slug: "coolandlovely",
8345
+ name: "CoolAndLovely",
8346
+ websiteUrl: "https://coolandlovely.com"
8347
+ },
8348
+ {
8349
+ slug: "atheletebridge",
8350
+ name: "AthleteBridge",
8351
+ websiteUrl: "https://atheletebridge.com"
8352
+ },
8353
+ {
8354
+ slug: "labsofscience",
8355
+ name: "LabsOfScience",
8356
+ websiteUrl: "https://labsofscience.com"
8357
+ },
8358
+ {
8359
+ slug: "ecoimpacthub",
8360
+ name: "EcoImpactHub",
8361
+ websiteUrl: "https://ecoimpacthub.com"
8362
+ }
8363
+ ];
8364
+ var META_DESCRIPTION_LIMIT2 = 160;
8365
+ var TITLE_LIMIT = 100;
8366
+ var RELATED_LIMIT2 = 4;
8367
+ function normaliseSiteUrl2(siteUrl) {
8368
+ return siteUrl.trim().replace(/\/+$/, "");
8369
+ }
8370
+ function absoluteUrl2(base, path) {
8371
+ if (/^https?:\/\//i.test(path)) return path;
8372
+ return `${base}${path.startsWith("/") ? path : `/${path}`}`;
8373
+ }
8374
+ function sortByOrder2(answers) {
8375
+ return [...answers].sort(
8376
+ (a, b) => a.order - b.order || a.slug.localeCompare(b.slug)
8377
+ );
8378
+ }
8379
+ function clampText2(text, limit) {
8380
+ if (text.length <= limit) return text;
8381
+ const cut = text.slice(0, limit - 1);
8382
+ const lastSpace = cut.lastIndexOf(" ");
8383
+ return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[\s,;:—–-]+$/, "")}\u2026`;
8384
+ }
8385
+ function questionCount(T, count) {
8386
+ return count === 1 ? T.questionCountOne : fill(T.questionCountMany, { count: String(count) });
8387
+ }
8388
+ function sourceCount(T, count) {
8389
+ return count === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(count) });
7619
8390
  }
7620
8391
  function slugifyAnchor2(value) {
7621
8392
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -7695,6 +8466,12 @@ var ANSWER_HEADING_SHIFT = {
7695
8466
  h6: demoteHeading("h6")
7696
8467
  };
7697
8468
  var ENGLISH_TEXT = { lang: "en" };
8469
+ var ECOSYSTEM_SITES = ECOSYSTEM_PRODUCTS.map((product) => ({
8470
+ slug: product.slug,
8471
+ website: product.websiteUrl
8472
+ }));
8473
+ var ECOSYSTEM_PRODUCT_SLUGS = productSlugsByOrigin(ECOSYSTEM_SITES);
8474
+ var ECOSYSTEM_ORIGINS = inlineImageOrigins(ECOSYSTEM_SITES);
7698
8475
  function DefaultAnswerLink({
7699
8476
  href,
7700
8477
  className,
@@ -8193,6 +8970,10 @@ function AnswerDetail({
8193
8970
  ]),
8194
8971
  [base, sources, imageOrigins]
8195
8972
  );
8973
+ const answerVideoOrigins = useMemo(
8974
+ () => /* @__PURE__ */ new Set([...answerImageOrigins, ...ECOSYSTEM_ORIGINS]),
8975
+ [answerImageOrigins]
8976
+ );
8196
8977
  const canonical = absoluteUrl2(base, answerPath(answer.slug));
8197
8978
  const answerText = useMemo(
8198
8979
  () => answerSpeechText(answer.answer),
@@ -8354,6 +9135,8 @@ function AnswerDetail({
8354
9135
  {
8355
9136
  strings: T,
8356
9137
  imageOrigins: answerImageOrigins,
9138
+ videoOrigins: answerVideoOrigins,
9139
+ productSlugs: ECOSYSTEM_PRODUCT_SLUGS,
8357
9140
  components: ANSWER_HEADING_SHIFT,
8358
9141
  className: "mt-4",
8359
9142
  children: answer.answer
@@ -8715,185 +9498,6 @@ var EXPLORE_SHARED_CONVERSATION_QUERY = (
8715
9498
  `
8716
9499
  );
8717
9500
 
8718
- // src/data/products.ts
8719
- var ECOSYSTEM_PRODUCTS = [
8720
- {
8721
- slug: "workspaces",
8722
- name: "Workspaces",
8723
- websiteUrl: "https://burdenoff.com"
8724
- },
8725
- {
8726
- slug: "vibecontrols",
8727
- name: "VibeControls",
8728
- websiteUrl: "https://vibecontrols.com"
8729
- },
8730
- // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore
8731
- // catalog and the site's own sitemap use this origin, and so must the fallback catalog
8732
- // built from this list, or /explore rejects FluidGrids concept images until it loads.
8733
- {
8734
- slug: "fluidgrids",
8735
- name: "FluidGrids",
8736
- websiteUrl: "https://www.fluidgrids.ai"
8737
- },
8738
- // BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain
8739
- // still serves the same site but is no longer the name we link to).
8740
- { slug: "botlit", name: "BotManor", websiteUrl: "https://www.botmanor.com" },
8741
- {
8742
- slug: "bigconsole",
8743
- name: "BigConsole",
8744
- websiteUrl: "https://bigconsole.com"
8745
- },
8746
- {
8747
- slug: "semanticfed",
8748
- name: "SemanticFed",
8749
- websiteUrl: "https://semanticfed.com"
8750
- },
8751
- {
8752
- slug: "intelwatchtower",
8753
- name: "IntelWatchtower",
8754
- websiteUrl: "https://intelwatchtower.com"
8755
- },
8756
- { slug: "buildmyiq", name: "BuildMyIQ", websiteUrl: "https://buildmyiq.com" },
8757
- {
8758
- slug: "assethandler",
8759
- name: "AssetHandler",
8760
- websiteUrl: "https://assethandler.com"
8761
- },
8762
- {
8763
- slug: "crewfoundry",
8764
- name: "CrewFoundry",
8765
- websiteUrl: "https://crewfoundry.com"
8766
- },
8767
- {
8768
- slug: "planmagnet",
8769
- name: "PlanMagnet",
8770
- websiteUrl: "https://planmagnet.com"
8771
- },
8772
- {
8773
- slug: "eventfullymanaged",
8774
- name: "EventfullyManaged",
8775
- websiteUrl: "https://eventfullymanaged.com"
8776
- },
8777
- {
8778
- slug: "headshotmarketing",
8779
- name: "HeadshotMarketing",
8780
- websiteUrl: "https://headshotmarketing.com"
8781
- },
8782
- {
8783
- slug: "technospam",
8784
- name: "TechnoSpam",
8785
- websiteUrl: "https://technospam.com"
8786
- },
8787
- { slug: "kadaikodi", name: "Kadaikodi", websiteUrl: "https://kadaikodi.com" },
8788
- {
8789
- slug: "movethewheels",
8790
- name: "MoveTheWheels",
8791
- websiteUrl: "https://movethewheels.com"
8792
- },
8793
- {
8794
- slug: "healthybowl",
8795
- name: "Healthy Bowl",
8796
- websiteUrl: "https://gethealthybowl.com"
8797
- },
8798
- { slug: "ggnomad", name: "GGNomad", websiteUrl: "https://ggnomad.com" },
8799
- {
8800
- slug: "timecampus",
8801
- name: "TimeCampus",
8802
- websiteUrl: "https://timecampus.com"
8803
- },
8804
- {
8805
- slug: "manufacturedops",
8806
- name: "ManufacturedOps",
8807
- websiteUrl: "https://manufacturedops.com"
8808
- },
8809
- {
8810
- slug: "theglobalfuel",
8811
- name: "TheGlobalFuel",
8812
- websiteUrl: "https://theglobalfuel.com"
8813
- },
8814
- {
8815
- slug: "housingvista",
8816
- name: "HousingVista",
8817
- websiteUrl: "https://housingvista.com"
8818
- },
8819
- {
8820
- slug: "proserviceworld",
8821
- name: "ProServiceWorld",
8822
- websiteUrl: "https://proserviceworld.com"
8823
- },
8824
- {
8825
- slug: "mybodyshield",
8826
- name: "MyBodyShield",
8827
- websiteUrl: "https://mybodyshield.com"
8828
- },
8829
- {
8830
- slug: "artistrybase",
8831
- name: "ArtistryBase",
8832
- websiteUrl: "https://artistrybase.com"
8833
- },
8834
- {
8835
- slug: "theculturepost",
8836
- name: "TheCulturePost",
8837
- websiteUrl: "https://theculturepost.com"
8838
- },
8839
- { slug: "collabkin", name: "CollabKin", websiteUrl: "https://collabkin.com" },
8840
- {
8841
- slug: "comradecircle",
8842
- name: "ComradeCircle",
8843
- websiteUrl: "https://comradecircle.com"
8844
- },
8845
- {
8846
- slug: "brainyrich",
8847
- name: "BrainyRich",
8848
- websiteUrl: "https://brainyrich.com"
8849
- },
8850
- {
8851
- slug: "cosmicintersection",
8852
- name: "CosmicIntersection",
8853
- websiteUrl: "https://cosmicintersection.com"
8854
- },
8855
- {
8856
- slug: "govcitizenhub",
8857
- name: "GovCitizenHub",
8858
- websiteUrl: "https://govcitizenhub.com"
8859
- },
8860
- {
8861
- slug: "adaptercloud",
8862
- name: "AdapterCloud",
8863
- websiteUrl: "https://adaptercloud.com"
8864
- },
8865
- {
8866
- slug: "subscriberbot",
8867
- name: "Subscriber Bot",
8868
- websiteUrl: "https://subscriberbot.com"
8869
- },
8870
- {
8871
- slug: "hookmovies",
8872
- name: "HookMovies",
8873
- websiteUrl: "https://hookmovies.com"
8874
- },
8875
- {
8876
- slug: "coolandlovely",
8877
- name: "CoolAndLovely",
8878
- websiteUrl: "https://coolandlovely.com"
8879
- },
8880
- {
8881
- slug: "atheletebridge",
8882
- name: "AthleteBridge",
8883
- websiteUrl: "https://atheletebridge.com"
8884
- },
8885
- {
8886
- slug: "labsofscience",
8887
- name: "LabsOfScience",
8888
- websiteUrl: "https://labsofscience.com"
8889
- },
8890
- {
8891
- slug: "ecoimpacthub",
8892
- name: "EcoImpactHub",
8893
- websiteUrl: "https://ecoimpacthub.com"
8894
- }
8895
- ];
8896
-
8897
9501
  // src/utils/recaptcha-v3.ts
8898
9502
  var SCRIPT_ID = "boff-recaptcha-v3";
8899
9503
  var SCRIPT_SRC = "https://www.google.com/recaptcha/api.js?render=";
@@ -10011,7 +10615,7 @@ function isHighSurrogate(code) {
10011
10615
  }
10012
10616
  function safeInlineEnd(line) {
10013
10617
  let codeSpan = null;
10014
- let link = null;
10618
+ const links = [];
10015
10619
  const emphasis = [];
10016
10620
  let at = 0;
10017
10621
  while (at < line.length) {
@@ -10020,6 +10624,13 @@ function safeInlineEnd(line) {
10020
10624
  at += 2;
10021
10625
  continue;
10022
10626
  }
10627
+ const innermost = links[links.length - 1];
10628
+ if (innermost !== void 0 && innermost.kind !== "label") {
10629
+ if (char === "(") links.push({ kind: "paren", start: innermost.start });
10630
+ else if (char === ")") links.pop();
10631
+ at += 1;
10632
+ continue;
10633
+ }
10023
10634
  if (char === "`") {
10024
10635
  let run = 1;
10025
10636
  while (line[at + run] === "`") run += 1;
@@ -10032,17 +10643,17 @@ function safeInlineEnd(line) {
10032
10643
  continue;
10033
10644
  }
10034
10645
  if (char === "[") {
10035
- if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
10646
+ links.push({ kind: "label", start: line[at - 1] === "!" ? at - 1 : at });
10036
10647
  at += 1;
10037
10648
  continue;
10038
10649
  }
10039
- if (char === "]") {
10040
- if (link !== null && line[at + 1] !== "(") link = null;
10041
- at += 1;
10042
- continue;
10043
- }
10044
- if (char === ")") {
10045
- link = null;
10650
+ if (char === "]" && innermost !== void 0) {
10651
+ links.pop();
10652
+ if (line[at + 1] === "(") {
10653
+ links.push({ kind: "destination", start: innermost.start });
10654
+ at += 2;
10655
+ continue;
10656
+ }
10046
10657
  at += 1;
10047
10658
  continue;
10048
10659
  }
@@ -10069,9 +10680,11 @@ function safeInlineEnd(line) {
10069
10680
  }
10070
10681
  at += 1;
10071
10682
  }
10072
- const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
10073
- (value) => value !== null
10074
- );
10683
+ const open = [
10684
+ codeSpan,
10685
+ links[0]?.start ?? null,
10686
+ emphasis[0]?.start ?? null
10687
+ ].filter((value) => value !== null);
10075
10688
  return open.length === 0 ? line.length : Math.min(...open);
10076
10689
  }
10077
10690
  function revealBoundary(text, limit) {
@@ -11933,6 +12546,7 @@ function AssistantBubble({
11933
12546
  message,
11934
12547
  activity,
11935
12548
  imageOrigins,
12549
+ productSlugs,
11936
12550
  productNames,
11937
12551
  speechLang,
11938
12552
  onNavigate,
@@ -12047,6 +12661,7 @@ function AssistantBubble({
12047
12661
  {
12048
12662
  strings: T,
12049
12663
  imageOrigins,
12664
+ productSlugs,
12050
12665
  enabled: completed,
12051
12666
  className: "boff-prose-sm text-card-foreground",
12052
12667
  children: visible
@@ -12317,21 +12932,24 @@ function ProductStrip({
12317
12932
  children: T.allProducts
12318
12933
  }
12319
12934
  ),
12320
- products.map((product) => /* @__PURE__ */ jsx(
12321
- "button",
12322
- {
12323
- "data-boff-explore": "chip",
12324
- "data-chip-kind": "product",
12325
- "data-product": product.slug,
12326
- type: "button",
12327
- "aria-pressed": focusProduct === product.slug,
12328
- title: product.tagline ?? product.name,
12329
- onClick: () => onFocus(focusProduct === product.slug ? null : product.slug),
12330
- className: chipClass(focusProduct === product.slug),
12331
- children: product.name
12332
- },
12333
- product.slug
12334
- ))
12935
+ products.map((product) => {
12936
+ const focused = isSameProductSlug(focusProduct, product.slug);
12937
+ return /* @__PURE__ */ jsx(
12938
+ "button",
12939
+ {
12940
+ "data-boff-explore": "chip",
12941
+ "data-chip-kind": "product",
12942
+ "data-product": product.slug,
12943
+ type: "button",
12944
+ "aria-pressed": focused,
12945
+ title: product.tagline ?? product.name,
12946
+ onClick: () => onFocus(focused ? null : product.slug),
12947
+ className: chipClass(focused),
12948
+ children: product.name
12949
+ },
12950
+ product.slug
12951
+ );
12952
+ })
12335
12953
  ]
12336
12954
  }
12337
12955
  ),
@@ -12789,11 +13407,11 @@ function ExplorePage({
12789
13407
  const resolvedBody = welcomeBody ?? catalog.welcomeBody;
12790
13408
  const prompts = catalog.examplePrompts;
12791
13409
  const focusedProductName = useMemo(
12792
- () => catalog.products.find((p) => p.slug === focusProduct)?.name ?? null,
13410
+ () => catalog.products.find((p) => isSameProductSlug(p.slug, focusProduct))?.name ?? null,
12793
13411
  [catalog.products, focusProduct]
12794
13412
  );
12795
13413
  const headProductName = useMemo(
12796
- () => productSlug ? catalog.products.find((p) => p.slug === productSlug)?.name ?? null : null,
13414
+ () => productSlug ? catalog.products.find((p) => isSameProductSlug(p.slug, productSlug))?.name ?? null : null,
12797
13415
  [catalog.products, productSlug]
12798
13416
  );
12799
13417
  const imageOrigins = useMemo(
@@ -12804,6 +13422,10 @@ function ExplorePage({
12804
13422
  () => productNamesByOrigin(catalog.products),
12805
13423
  [catalog.products]
12806
13424
  );
13425
+ const productSlugs = useMemo(
13426
+ () => productSlugsByOrigin(catalog.products),
13427
+ [catalog.products]
13428
+ );
12807
13429
  useEffect(() => {
12808
13430
  const el = textareaRef.current;
12809
13431
  if (!el) return;
@@ -13366,6 +13988,7 @@ function ExplorePage({
13366
13988
  message,
13367
13989
  activity,
13368
13990
  imageOrigins,
13991
+ productSlugs,
13369
13992
  productNames,
13370
13993
  speechLang,
13371
13994
  onNavigate,
@@ -18361,6 +18984,6 @@ function shouldRenderSiteFooter(pathname) {
18361
18984
  return !isFooterlessPath(pathname);
18362
18985
  }
18363
18986
 
18364
- export { ANSWERS_PATH, ANSWER_HEADING_SHIFT, ANSWER_PATH_PREFIX, AnswerDetailPage, AnswersPage, BlogListPage, BlogPostPage, Button, CONCEPTS_PATH, CONCEPT_ILLUSTRATION_NOTICE, CONCEPT_PATH_PREFIX, CareersApplyForm, CareersSubmissionForm, CaseStudiesListPage, CaseStudyPage, ChangelogPage, ConceptDetailPage, ConceptsPage, ContactPage, ContentRenderer, ContractsPage, DEFAULT_CONTACT_CATEGORIES, DEFAULT_EXPLORE_EXAMPLE_PROMPTS, DOMAINS_URL, ECOSYSTEM_PRODUCTS, ESCALATE_EXPLORE_CONVERSATION_MUTATION, EXPLORE_CATALOG_QUERY, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS, EXPLORE_ESCALATION_NOTE_SOFT_MAX, EXPLORE_LOCALES, EXPLORE_MESSAGE_QUERY, EXPLORE_PREFILL_MAX_LENGTH, EXPLORE_RECAPTCHA_ACTION, ElectronDownloadLink, ExploreAnswerBody, ExploreAnswerStyles, ExploreCta, ExplorePage, FALLBACK_EXPLORE_LIMITS, FOOTERLESS_ROUTES, Input, Label, LaunchCountdown, Markdown, NewsletterForm, NewsletterPage, PUBLISHED_ANSWER_FIELDS, PageHead, PartnersPage, PressKitPage, PricingPage, PricingSection, ProblemsSolvedSection, ProductCard, ProductProvider, ResourceLinks, RybbitAnalytics, SEND_EXPLORE_MESSAGE_MUTATION, Select, SocialLinks, StarRating, StoreBrowsePage, StoreHomePage, StoreProductDetailPage, Textarea, TypeBadge, UnsubscribePage, WaitlistForm, WaitlistPage, WebSDKClient, WebSDKProvider, WebsiteSwitcher, answerPath, answerPlainText, answerSourceHost, answerSpeechText, answerStrings, buttonVariants, classifyExploreError, cn, conceptPath, detectElectronPlatform, exploreQuestionHref, fetchPublicJobPosting, fetchPublicJobPostings, fetchPublicJobPostingsForSubmission, formatAnswerDate, formatDownloads, formatPrice2 as formatPrice, getAnswerBySlug, getConceptBySlug, getRecaptchaV3Token, inlineImageOrigins, isFooterlessPath, isInlineImageAllowed, isPublishedSourceUrl, isValidUrl, loadRecaptchaV3, looksLikeDeliverableEmail, normalizeRoutePath, shouldRenderSiteFooter, speechLocaleOf, stopSpeaking, useContentPage, useContentPages, useExploreChat, useProduct, useProductConfig, useProgressiveReveal, usePublicJobPosting, usePublicJobPostings, usePublicJobPostingsForSubmission, useSpeechOutput, useWebSDK, useWebSDKConfig };
18987
+ export { ANSWERS_PATH, ANSWER_HEADING_SHIFT, ANSWER_PATH_PREFIX, AnswerDetailPage, AnswersPage, BlogListPage, BlogPostPage, Button, CONCEPTS_PATH, CONCEPT_ILLUSTRATION_NOTICE, CONCEPT_PATH_PREFIX, CareersApplyForm, CareersSubmissionForm, CaseStudiesListPage, CaseStudyPage, ChangelogPage, ConceptDetailPage, ConceptsPage, ContactPage, ContentRenderer, ContractsPage, DEFAULT_CONTACT_CATEGORIES, DEFAULT_EXPLORE_EXAMPLE_PROMPTS, DOMAINS_URL, ECOSYSTEM_PRODUCTS, ESCALATE_EXPLORE_CONVERSATION_MUTATION, EXPLORE_CATALOG_QUERY, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS, EXPLORE_ESCALATION_NOTE_SOFT_MAX, EXPLORE_LOCALES, EXPLORE_MESSAGE_QUERY, EXPLORE_PREFILL_MAX_LENGTH, EXPLORE_RECAPTCHA_ACTION, ElectronDownloadLink, ExploreAnswerBody, ExploreAnswerStyles, ExploreCta, ExplorePage, FALLBACK_EXPLORE_LIMITS, FOOTERLESS_ROUTES, Input, Label, LaunchCountdown, Markdown, NewsletterForm, NewsletterPage, PRODUCT_SLUG_ALIASES, PRODUCT_SLUG_EMIT, PRODUCT_SLUG_RENAMES, PUBLISHED_ANSWER_FIELDS, PageHead, PartnersPage, PressKitPage, PricingPage, PricingSection, ProblemsSolvedSection, ProductCard, ProductProvider, ResourceLinks, RybbitAnalytics, SEND_EXPLORE_MESSAGE_MUTATION, Select, SocialLinks, StarRating, StoreBrowsePage, StoreHomePage, StoreProductDetailPage, Textarea, TypeBadge, UnsubscribePage, WaitlistForm, WaitlistPage, WebSDKClient, WebSDKProvider, WebsiteSwitcher, answerPath, answerPlainText, answerSourceHost, answerSpeechText, answerStrings, buttonVariants, classifyExploreError, cn, conceptPath, detectElectronPlatform, emitProductSlug, exploreQuestionHref, fetchPublicJobPosting, fetchPublicJobPostings, fetchPublicJobPostingsForSubmission, fetchPublicProduct, formatAnswerDate, formatDownloads, formatPrice2 as formatPrice, getAnswerBySlug, getConceptBySlug, getRecaptchaV3Token, inlineImageOrigins, isFooterlessPath, isInlineImageAllowed, isInlineVideoAllowed, isPublishedSourceUrl, isSameProductSlug, isValidUrl, legacyProductSlug, loadRecaptchaV3, looksLikeDeliverableEmail, normalizeProductSlug, normalizeRoutePath, productSlugVariants, productSlugsByOrigin, shouldRenderSiteFooter, speechLocaleOf, stopSpeaking, useContentPage, useContentPages, useExploreChat, useProduct, useProductConfig, useProgressiveReveal, usePublicJobPosting, usePublicJobPostings, usePublicJobPostingsForSubmission, useSpeechOutput, useWebSDK, useWebSDKConfig };
18365
18988
  //# sourceMappingURL=index.mjs.map
18366
18989
  //# sourceMappingURL=index.mjs.map