@burdenoff/website-sdk 2026.925.2 → 2026.925.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.
Files changed (35) hide show
  1. package/dist/components/explore.d.mts +1 -1
  2. package/dist/components/explore.d.ts +1 -1
  3. package/dist/components/explore.js +1046 -500
  4. package/dist/components/explore.js.map +1 -1
  5. package/dist/components/explore.mjs +1048 -503
  6. package/dist/components/explore.mjs.map +1 -1
  7. package/dist/components/partners.js +3 -2
  8. package/dist/components/partners.js.map +1 -1
  9. package/dist/components/partners.mjs +3 -2
  10. package/dist/components/partners.mjs.map +1 -1
  11. package/dist/content/index.d.mts +1 -1
  12. package/dist/content/index.d.ts +1 -1
  13. package/dist/content/index.js +3 -2
  14. package/dist/content/index.js.map +1 -1
  15. package/dist/content/index.mjs +3 -2
  16. package/dist/content/index.mjs.map +1 -1
  17. package/dist/{explore-C0RFTKAi.d.ts → explore-Cam_ElSJ.d.ts} +70 -12
  18. package/dist/{explore-DzOf7n4m.d.mts → explore-hTQm-_SF.d.mts} +70 -12
  19. package/dist/hooks/index.js +21 -12
  20. package/dist/hooks/index.js.map +1 -1
  21. package/dist/hooks/index.mjs +21 -12
  22. package/dist/hooks/index.mjs.map +1 -1
  23. package/dist/{index-BMWQNZDX.d.mts → index-gm3pBCJT.d.mts} +8 -2
  24. package/dist/{index-BqHF-G-S.d.ts → index-ySbOLSD5.d.ts} +8 -2
  25. package/dist/index.d.mts +30 -9
  26. package/dist/index.d.ts +30 -9
  27. package/dist/index.js +827 -261
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +827 -263
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/store/index.js +3 -2
  32. package/dist/store/index.js.map +1 -1
  33. package/dist/store/index.mjs +3 -2
  34. package/dist/store/index.mjs.map +1 -1
  35. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ var zod$1 = require('@hookform/resolvers/zod');
19
19
  var reactHookForm = require('react-hook-form');
20
20
  var zod = require('zod');
21
21
  var DropdownMenu = require('@radix-ui/react-dropdown-menu');
22
+ var reactDom = require('react-dom');
22
23
 
23
24
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
24
25
 
@@ -1138,13 +1139,14 @@ function Markdown({
1138
1139
  children,
1139
1140
  className,
1140
1141
  inline,
1141
- components
1142
+ components,
1143
+ rehypePlugins
1142
1144
  }) {
1143
1145
  const content = /* @__PURE__ */ jsxRuntime.jsx(
1144
1146
  ReactMarkdown__default.default,
1145
1147
  {
1146
1148
  remarkPlugins: [remarkGfm__default.default],
1147
- rehypePlugins: [rehypeSanitize__default.default],
1149
+ rehypePlugins: [rehypeSanitize__default.default, ...rehypePlugins ?? []],
1148
1150
  components: { ...defaultComponents, ...components },
1149
1151
  ...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
1150
1152
  children
@@ -6165,6 +6167,13 @@ var EN = {
6165
6167
  readAloud: "Read aloud",
6166
6168
  stopReading: "Stop reading",
6167
6169
  answerTable: "Table",
6170
+ productVideo: "Product video",
6171
+ productVideoTitle: "{product} product video",
6172
+ playVideo: "Play {title}",
6173
+ closeVideo: "Close video",
6174
+ enterFullScreen: "Full screen",
6175
+ exitFullScreen: "Exit full screen",
6176
+ videoUnavailable: "This video can't be played right now.",
6168
6177
  groundedIn: "What this answer is based on",
6169
6178
  sourceCountOne: "1 source",
6170
6179
  sourceCountMany: "{count} sources",
@@ -6266,6 +6275,13 @@ var ZH = {
6266
6275
  readAloud: "\u6717\u8BFB",
6267
6276
  stopReading: "\u505C\u6B62\u6717\u8BFB",
6268
6277
  answerTable: "\u8868\u683C",
6278
+ productVideo: "\u4EA7\u54C1\u89C6\u9891",
6279
+ productVideoTitle: "{product} \u4EA7\u54C1\u89C6\u9891",
6280
+ playVideo: "\u64AD\u653E {title}",
6281
+ closeVideo: "\u5173\u95ED\u89C6\u9891",
6282
+ enterFullScreen: "\u5168\u5C4F",
6283
+ exitFullScreen: "\u9000\u51FA\u5168\u5C4F",
6284
+ videoUnavailable: "\u6682\u65F6\u65E0\u6CD5\u64AD\u653E\u6B64\u89C6\u9891\u3002",
6269
6285
  groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
6270
6286
  sourceCountOne: "1 \u4E2A\u6765\u6E90",
6271
6287
  sourceCountMany: "{count} \u4E2A\u6765\u6E90",
@@ -6363,6 +6379,13 @@ var HI = {
6363
6379
  readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
6364
6380
  stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
6365
6381
  answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
6382
+ productVideo: "\u0909\u0924\u094D\u092A\u093E\u0926 \u0935\u0940\u0921\u093F\u092F\u094B",
6383
+ productVideoTitle: "{product} \u0909\u0924\u094D\u092A\u093E\u0926 \u0935\u0940\u0921\u093F\u092F\u094B",
6384
+ playVideo: "{title} \u091A\u0932\u093E\u090F\u0901",
6385
+ closeVideo: "\u0935\u0940\u0921\u093F\u092F\u094B \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
6386
+ enterFullScreen: "\u092A\u0942\u0930\u094D\u0923 \u0938\u094D\u0915\u094D\u0930\u0940\u0928",
6387
+ 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",
6388
+ videoUnavailable: "\u092F\u0939 \u0935\u0940\u0921\u093F\u092F\u094B \u0905\u092D\u0940 \u0928\u0939\u0940\u0902 \u091A\u0932 \u0938\u0915\u0924\u093E\u0964",
6366
6389
  groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
6367
6390
  sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
6368
6391
  sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
@@ -6464,6 +6487,13 @@ var ES = {
6464
6487
  readAloud: "Leer en voz alta",
6465
6488
  stopReading: "Dejar de leer",
6466
6489
  answerTable: "Tabla",
6490
+ productVideo: "V\xEDdeo del producto",
6491
+ productVideoTitle: "V\xEDdeo de {product}",
6492
+ playVideo: "Reproducir {title}",
6493
+ closeVideo: "Cerrar v\xEDdeo",
6494
+ enterFullScreen: "Pantalla completa",
6495
+ exitFullScreen: "Salir de pantalla completa",
6496
+ videoUnavailable: "Ahora mismo no se puede reproducir este v\xEDdeo.",
6467
6497
  groundedIn: "En qu\xE9 se basa esta respuesta",
6468
6498
  sourceCountOne: "1 fuente",
6469
6499
  sourceCountMany: "{count} fuentes",
@@ -6565,6 +6595,13 @@ var AR = {
6565
6595
  readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
6566
6596
  stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
6567
6597
  answerTable: "\u062C\u062F\u0648\u0644",
6598
+ productVideo: "\u0641\u064A\u062F\u064A\u0648 \u0627\u0644\u0645\u0646\u062A\u062C",
6599
+ productVideoTitle: "\u0641\u064A\u062F\u064A\u0648 {product}",
6600
+ playVideo: "\u062A\u0634\u063A\u064A\u0644 {title}",
6601
+ closeVideo: "\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0641\u064A\u062F\u064A\u0648",
6602
+ enterFullScreen: "\u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629",
6603
+ exitFullScreen: "\u0627\u0644\u062E\u0631\u0648\u062C \u0645\u0646 \u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629",
6604
+ 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.",
6568
6605
  groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
6569
6606
  sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
6570
6607
  sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
@@ -6666,6 +6703,13 @@ var TA = {
6666
6703
  readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
6667
6704
  stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
6668
6705
  answerTable: "\u0B85\u0B9F\u0BCD\u0B9F\u0BB5\u0BA3\u0BC8",
6706
+ productVideo: "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1 \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB",
6707
+ productVideoTitle: "{product} \u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1 \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB",
6708
+ playVideo: "{title} \u0B87\u0BAF\u0B95\u0BCD\u0B95\u0BC1",
6709
+ closeVideo: "\u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB\u0BB5\u0BC8 \u0BAE\u0BC2\u0B9F\u0BC1",
6710
+ enterFullScreen: "\u0BAE\u0BC1\u0BB4\u0BC1\u0BA4\u0BCD \u0BA4\u0BBF\u0BB0\u0BC8",
6711
+ 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",
6712
+ 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.",
6669
6713
  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",
6670
6714
  sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
6671
6715
  sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
@@ -6955,7 +6999,14 @@ function answerStrings(locale) {
6955
6999
  ...chrome,
6956
7000
  answerTable: explore.answerTable,
6957
7001
  conceptIllustration: explore.conceptIllustration,
6958
- opensInNewTab: explore.opensInNewTab
7002
+ opensInNewTab: explore.opensInNewTab,
7003
+ productVideo: explore.productVideo,
7004
+ productVideoTitle: explore.productVideoTitle,
7005
+ playVideo: explore.playVideo,
7006
+ closeVideo: explore.closeVideo,
7007
+ enterFullScreen: explore.enterFullScreen,
7008
+ exitFullScreen: explore.exitFullScreen,
7009
+ videoUnavailable: explore.videoUnavailable
6959
7010
  };
6960
7011
  }
6961
7012
  var DATE_TAGS = {
@@ -7023,6 +7074,253 @@ function answerSourceHost(url) {
7023
7074
  return "";
7024
7075
  }
7025
7076
  }
7077
+ function fullscreenSupport() {
7078
+ if (typeof document === "undefined") return null;
7079
+ if (document.fullscreenEnabled && typeof document.documentElement.requestFullscreen === "function") {
7080
+ return "element";
7081
+ }
7082
+ if (typeof HTMLVideoElement !== "undefined" && "webkitEnterFullscreen" in HTMLVideoElement.prototype) {
7083
+ return "video";
7084
+ }
7085
+ return null;
7086
+ }
7087
+ function ignoreRejection(result) {
7088
+ result?.catch(() => void 0);
7089
+ }
7090
+ var VIDEO_DIALOG_CSS = `
7091
+ .boff-explore-video-panel:fullscreen {
7092
+ display: flex;
7093
+ flex-direction: column;
7094
+ gap: 0.75rem;
7095
+ padding: 1rem;
7096
+ background-color: hsl(var(--background));
7097
+ color: hsl(var(--foreground));
7098
+ }
7099
+ .boff-explore-video-panel:fullscreen .boff-explore-video-frame {
7100
+ display: flex;
7101
+ flex: 1 1 auto;
7102
+ min-height: 0;
7103
+ align-items: center;
7104
+ justify-content: center;
7105
+ border: 0;
7106
+ border-radius: 0;
7107
+ box-shadow: none;
7108
+ background-color: hsl(var(--background));
7109
+ }
7110
+ .boff-explore-video-panel:fullscreen video {
7111
+ width: 100%;
7112
+ height: 100%;
7113
+ max-height: none;
7114
+ }
7115
+ `;
7116
+ 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";
7117
+ function ExploreVideoDialog({
7118
+ videoUrl,
7119
+ posterUrl,
7120
+ title,
7121
+ strings: T,
7122
+ dir,
7123
+ onClose
7124
+ }) {
7125
+ const dialogRef = React.useRef(null);
7126
+ const panelRef = React.useRef(null);
7127
+ const videoRef = React.useRef(null);
7128
+ const closeRef = React.useRef(null);
7129
+ const nativeModalRef = React.useRef(false);
7130
+ const titleId = React.useId();
7131
+ const [support] = React.useState(fullscreenSupport);
7132
+ const [fullscreen, setFullscreen] = React.useState(false);
7133
+ const [failed, setFailed] = React.useState(false);
7134
+ React.useEffect(() => {
7135
+ const dialog = dialogRef.current;
7136
+ if (!dialog) return;
7137
+ if (typeof dialog.showModal === "function") {
7138
+ if (!dialog.open) dialog.showModal();
7139
+ nativeModalRef.current = true;
7140
+ } else {
7141
+ dialog.setAttribute("open", "");
7142
+ nativeModalRef.current = false;
7143
+ }
7144
+ closeRef.current?.focus();
7145
+ ignoreRejection(videoRef.current?.play());
7146
+ return () => {
7147
+ if (dialog.open && typeof dialog.close === "function") dialog.close();
7148
+ };
7149
+ }, []);
7150
+ React.useEffect(() => {
7151
+ const sync = () => {
7152
+ const current = document.fullscreenElement;
7153
+ setFullscreen(
7154
+ current !== null && dialogRef.current?.contains(current) === true
7155
+ );
7156
+ };
7157
+ const enter = () => setFullscreen(true);
7158
+ const leave = () => setFullscreen(false);
7159
+ const video = videoRef.current;
7160
+ document.addEventListener("fullscreenchange", sync);
7161
+ video?.addEventListener("webkitbeginfullscreen", enter);
7162
+ video?.addEventListener("webkitendfullscreen", leave);
7163
+ return () => {
7164
+ document.removeEventListener("fullscreenchange", sync);
7165
+ video?.removeEventListener("webkitbeginfullscreen", enter);
7166
+ video?.removeEventListener("webkitendfullscreen", leave);
7167
+ };
7168
+ }, [failed]);
7169
+ const leaveFullscreen = React.useCallback(() => {
7170
+ const current = document.fullscreenElement;
7171
+ if (current && dialogRef.current?.contains(current) && typeof document.exitFullscreen === "function") {
7172
+ ignoreRejection(document.exitFullscreen());
7173
+ }
7174
+ const video = videoRef.current;
7175
+ if (video?.webkitDisplayingFullscreen) video.webkitExitFullscreen?.();
7176
+ }, []);
7177
+ const requestClose = React.useCallback(() => {
7178
+ videoRef.current?.pause();
7179
+ leaveFullscreen();
7180
+ onClose();
7181
+ }, [leaveFullscreen, onClose]);
7182
+ const toggleFullscreen = React.useCallback(() => {
7183
+ if (fullscreen) {
7184
+ leaveFullscreen();
7185
+ return;
7186
+ }
7187
+ if (support === "element") {
7188
+ const panel = panelRef.current;
7189
+ if (panel) ignoreRejection(panel.requestFullscreen());
7190
+ return;
7191
+ }
7192
+ const video = videoRef.current;
7193
+ video?.webkitEnterFullscreen?.();
7194
+ }, [fullscreen, leaveFullscreen, support]);
7195
+ const handleClose = React.useCallback(
7196
+ (event) => {
7197
+ if (event.currentTarget.open) return;
7198
+ requestClose();
7199
+ },
7200
+ [requestClose]
7201
+ );
7202
+ const handleKeyDown = React.useCallback(
7203
+ (event) => {
7204
+ if (event.key === "Escape" && !nativeModalRef.current) {
7205
+ event.preventDefault();
7206
+ requestClose();
7207
+ }
7208
+ },
7209
+ [requestClose]
7210
+ );
7211
+ const handleStageClick = React.useCallback(
7212
+ (event) => {
7213
+ if (event.target === event.currentTarget) requestClose();
7214
+ },
7215
+ [requestClose]
7216
+ );
7217
+ if (typeof document === "undefined") return null;
7218
+ const fullscreenLabel = fullscreen ? T.exitFullScreen : T.enterFullScreen;
7219
+ return reactDom.createPortal(
7220
+ /* @__PURE__ */ jsxRuntime.jsxs(
7221
+ "dialog",
7222
+ {
7223
+ ref: dialogRef,
7224
+ "data-boff-explore": "video-dialog",
7225
+ "aria-labelledby": titleId,
7226
+ dir,
7227
+ onClose: handleClose,
7228
+ onKeyDown: handleKeyDown,
7229
+ 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",
7230
+ children: [
7231
+ /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: VIDEO_DIALOG_CSS } }),
7232
+ /* @__PURE__ */ jsxRuntime.jsx(
7233
+ "div",
7234
+ {
7235
+ onClick: handleStageClick,
7236
+ className: "flex min-h-full items-center justify-center p-4 sm:p-6",
7237
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
7238
+ "div",
7239
+ {
7240
+ ref: panelRef,
7241
+ className: "boff-explore-video-panel flex w-full min-w-0 max-w-[960px] flex-col gap-3",
7242
+ children: [
7243
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
7244
+ /* @__PURE__ */ jsxRuntime.jsx(
7245
+ "h2",
7246
+ {
7247
+ id: titleId,
7248
+ className: "min-w-0 flex-1 truncate text-sm font-semibold text-foreground sm:text-base",
7249
+ children: title
7250
+ }
7251
+ ),
7252
+ support !== null && !failed ? (
7253
+ // A plain button whose name says what pressing it does NOW, and no
7254
+ // `aria-pressed`: a toggle button's name must not change with its state, or it
7255
+ // announces "Exit full screen, pressed" — two answers to one question.
7256
+ /* @__PURE__ */ jsxRuntime.jsx(
7257
+ "button",
7258
+ {
7259
+ type: "button",
7260
+ "data-boff-explore": "video-fullscreen",
7261
+ "aria-label": fullscreenLabel,
7262
+ title: fullscreenLabel,
7263
+ onClick: toggleFullscreen,
7264
+ className: DIALOG_BUTTON,
7265
+ children: fullscreen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize, { "aria-hidden": "true", className: "h-5 w-5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize, { "aria-hidden": "true", className: "h-5 w-5" })
7266
+ }
7267
+ )
7268
+ ) : null,
7269
+ /* @__PURE__ */ jsxRuntime.jsx(
7270
+ "button",
7271
+ {
7272
+ ref: closeRef,
7273
+ type: "button",
7274
+ "data-boff-explore": "video-close",
7275
+ "aria-label": T.closeVideo,
7276
+ title: T.closeVideo,
7277
+ onClick: requestClose,
7278
+ className: DIALOG_BUTTON,
7279
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": "true", className: "h-5 w-5" })
7280
+ }
7281
+ )
7282
+ ] }),
7283
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-explore-video-frame overflow-hidden rounded-xl border border-border bg-muted shadow-lg", children: failed ? (
7284
+ // A black box with a spinning control tells the visitor nothing; this says
7285
+ // what happened, in their language.
7286
+ /* @__PURE__ */ jsxRuntime.jsxs(
7287
+ "div",
7288
+ {
7289
+ role: "alert",
7290
+ className: "flex aspect-video w-full flex-col items-center justify-center gap-2 p-6 text-center text-sm text-muted-foreground",
7291
+ children: [
7292
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.VideoOff, { "aria-hidden": "true", className: "h-6 w-6" }),
7293
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: T.videoUnavailable })
7294
+ ]
7295
+ }
7296
+ )
7297
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
7298
+ "video",
7299
+ {
7300
+ ref: videoRef,
7301
+ "data-boff-explore": "video-player",
7302
+ src: videoUrl,
7303
+ poster: posterUrl,
7304
+ controls: true,
7305
+ controlsList: support === "element" ? "nofullscreen" : void 0,
7306
+ playsInline: true,
7307
+ autoPlay: true,
7308
+ preload: "metadata",
7309
+ onError: () => setFailed(true),
7310
+ className: "block max-h-[80vh] w-full bg-muted object-contain"
7311
+ }
7312
+ ) })
7313
+ ]
7314
+ }
7315
+ )
7316
+ }
7317
+ )
7318
+ ]
7319
+ }
7320
+ ),
7321
+ document.body
7322
+ );
7323
+ }
7026
7324
  var EXPLORE_ANSWER_CSS = `
7027
7325
  /* Nothing inside an answer may widen the page. Model output carries long URLs, code and
7028
7326
  tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
@@ -7055,6 +7353,14 @@ var EXPLORE_ANSWER_CSS = `
7055
7353
  max-width: 100%;
7056
7354
  height: auto;
7057
7355
  }
7356
+ /* A product-video poster fills the card's reserved 16:9 frame and is cropped to it. The
7357
+ blanket \`img { height: auto }\` above \u2014 and ProseStyles' own \u2014 outrank a height utility,
7358
+ and would size it by its own aspect ratio instead, leaving a band of the frame showing
7359
+ whenever a poster is not exactly 16:9. */
7360
+ .boff-explore-body .boff-explore-video-poster {
7361
+ height: 100%;
7362
+ max-width: none;
7363
+ }
7058
7364
 
7059
7365
  /* ---- Comparison and pricing tables ----------------------------------------------------
7060
7366
  The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
@@ -7185,28 +7491,81 @@ function inlineImageOrigins(products) {
7185
7491
  }
7186
7492
  return origins;
7187
7493
  }
7188
- function isInlineImageAllowed(src, origins) {
7189
- if (typeof src !== "string" || src === "") return false;
7494
+ var VIDEO_FILE_PATH = /\.(?:mp4|webm|m4v|mov|vtt)$/i;
7495
+ function catalogUrlOf(value, origins) {
7496
+ if (typeof value !== "string" || value === "") return null;
7190
7497
  let url;
7191
7498
  try {
7192
- url = new URL(src);
7499
+ url = new URL(value);
7500
+ } catch {
7501
+ return null;
7502
+ }
7503
+ if (url.protocol !== "https:" || url.username || url.password) return null;
7504
+ return origins.has(url.origin) ? url : null;
7505
+ }
7506
+ function isInlineImageAllowed(src, origins) {
7507
+ const url = catalogUrlOf(src, origins);
7508
+ return url !== null && !VIDEO_FILE_PATH.test(url.pathname);
7509
+ }
7510
+ var INLINE_VIDEO_PATH = /\.(?:mp4|webm)$/i;
7511
+ function isInlineVideoAllowed(href, origins) {
7512
+ const url = catalogUrlOf(href, origins);
7513
+ return url !== null && INLINE_VIDEO_PATH.test(url.pathname);
7514
+ }
7515
+ function apexOriginOf(url) {
7516
+ const apex = url.hostname.replace(/^www\./, "");
7517
+ const port = url.port ? `:${url.port}` : "";
7518
+ return `https://${apex}${port}`;
7519
+ }
7520
+ function productSlugsByOrigin(products) {
7521
+ const slugs = /* @__PURE__ */ new Map();
7522
+ for (const product of products) {
7523
+ let url;
7524
+ try {
7525
+ url = new URL(product.website);
7526
+ } catch {
7527
+ continue;
7528
+ }
7529
+ if (url.protocol !== "https:") continue;
7530
+ const key = apexOriginOf(url);
7531
+ if (!slugs.has(key)) slugs.set(key, product.slug);
7532
+ }
7533
+ return slugs;
7534
+ }
7535
+ function videoProductOf(href, slugs) {
7536
+ const url = new URL(href);
7537
+ return slugs.get(apexOriginOf(url)) ?? url.hostname.replace(/^www\./, "");
7538
+ }
7539
+ function isVideoFileTarget(href) {
7540
+ if (typeof href !== "string") return false;
7541
+ try {
7542
+ return VIDEO_FILE_PATH.test(
7543
+ new URL(href, "https://answer.invalid").pathname
7544
+ );
7193
7545
  } catch {
7194
7546
  return false;
7195
7547
  }
7196
- if (url.protocol !== "https:" || url.username || url.password) return false;
7197
- return origins.has(url.origin);
7198
7548
  }
7199
7549
  var CODE_FENCE = /^\s*(?:```|~~~)/;
7200
7550
  var TABLE_ROW_LINE = /^\s{0,3}\|/;
7201
7551
  var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
7552
+ var LINKED_IMAGE = /\[!\[([^[\]]*)\]\([^)\s]*(?:\s+"[^"]*")?\)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
7553
+ var IMAGE = /!\[[^[\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g;
7554
+ var LINK = /\[([^[\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
7202
7555
  function readableLine(raw, mode) {
7203
7556
  let line = raw;
7204
- line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
7205
- line = mode === "speech" ? (
7206
- // A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
7207
- // prompt asks for two to five links in every answer. The label IS the words.
7208
- line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
7209
- ) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
7557
+ if (line.includes("](")) {
7558
+ line = line.replace(LINKED_IMAGE, (_match, alt, href) => {
7559
+ if (mode === "speech") return "";
7560
+ return alt.trim() === "" ? href : `${alt.trim()} (${href})`;
7561
+ });
7562
+ line = line.replace(IMAGE, "");
7563
+ line = mode === "speech" ? (
7564
+ // A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
7565
+ // prompt asks for two to five links in every answer. The label IS the words.
7566
+ line.replace(LINK, "$1")
7567
+ ) : line.replace(LINK, "$1 ($2)");
7568
+ }
7210
7569
  line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
7211
7570
  line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
7212
7571
  line = line.replace(/^\s{0,3}>\s?/, "");
@@ -7297,6 +7656,21 @@ function answerSpeechText(markdown) {
7297
7656
  var AnswerScopeContext = React.createContext(null);
7298
7657
  var InsideLinkContext = React.createContext(false);
7299
7658
  function AnswerLink(props) {
7659
+ const scope = React.useContext(AnswerScopeContext);
7660
+ const role = videoRoleOf(props.node);
7661
+ if (role !== void 0 && scope !== null) {
7662
+ const video = role === "card" && scope.enabled ? productVideoOf(props.node) : null;
7663
+ if (video === null) return null;
7664
+ return /* @__PURE__ */ jsxRuntime.jsx(
7665
+ InlineVideoCard,
7666
+ {
7667
+ href: video.href,
7668
+ poster: video.poster,
7669
+ alt: video.alt,
7670
+ scope
7671
+ }
7672
+ );
7673
+ }
7300
7674
  return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
7301
7675
  }
7302
7676
  var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
@@ -7408,16 +7782,173 @@ function AnswerTableRow({
7408
7782
  });
7409
7783
  return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
7410
7784
  }
7785
+ function linkedImageOf(node) {
7786
+ if (node?.tagName !== "a") return null;
7787
+ const content = (node.children ?? []).filter(
7788
+ (child) => child.tagName !== void 0 || (child.value ?? "").trim() !== ""
7789
+ );
7790
+ const image = content.length === 1 ? content[0] : void 0;
7791
+ if (image?.tagName !== "img") return null;
7792
+ const alt = image.properties?.alt;
7793
+ return {
7794
+ href: node.properties?.href,
7795
+ src: image.properties?.src,
7796
+ alt: typeof alt === "string" ? alt.trim() : ""
7797
+ };
7798
+ }
7799
+ function isCardableVideo(embed, origins) {
7800
+ if (!isInlineVideoAllowed(embed.href, origins) || !isInlineImageAllowed(embed.src, origins)) {
7801
+ return false;
7802
+ }
7803
+ return apexOriginOf(new URL(embed.href)) === apexOriginOf(new URL(embed.src));
7804
+ }
7805
+ var NO_VIDEO_CONTAINERS = /* @__PURE__ */ new Set(["li", "table"]);
7806
+ function rehypeProductVideo(options) {
7807
+ return (tree) => {
7808
+ let claimed = false;
7809
+ const visit = (node, parent, contained) => {
7810
+ const embed = linkedImageOf(node);
7811
+ if (embed !== null && isVideoFileTarget(embed.href)) {
7812
+ const card = !claimed && !contained && parent?.tagName === "p" && isCardableVideo(embed, options.origins);
7813
+ if (card) claimed = true;
7814
+ const data = typeof node.data === "object" ? node.data : null;
7815
+ node.data = { ...data, boffVideo: card ? "card" : "drop" };
7816
+ return;
7817
+ }
7818
+ const inside = contained || node.tagName !== void 0 && NO_VIDEO_CONTAINERS.has(node.tagName);
7819
+ for (const child of node.children ?? []) visit(child, node, inside);
7820
+ };
7821
+ visit(tree, null, false);
7822
+ };
7823
+ }
7824
+ function videoRoleOf(node) {
7825
+ const data = node?.data;
7826
+ if (typeof data !== "object" || data === null || !("boffVideo" in data)) {
7827
+ return void 0;
7828
+ }
7829
+ return data.boffVideo === "card" || data.boffVideo === "drop" ? data.boffVideo : void 0;
7830
+ }
7831
+ function productVideoOf(node) {
7832
+ const embed = linkedImageOf(node);
7833
+ if (embed === null || typeof embed.href !== "string" || typeof embed.src !== "string") {
7834
+ return null;
7835
+ }
7836
+ return { href: embed.href, poster: embed.src, alt: embed.alt };
7837
+ }
7838
+ var CANONICAL_VIDEO_ALT = /^(.+?)\s+product video$/i;
7839
+ function videoTitleOf(alt, T) {
7840
+ const product = CANONICAL_VIDEO_ALT.exec(alt)?.[1]?.trim();
7841
+ if (product) return fill(T.productVideoTitle, { product });
7842
+ return alt === "" ? T.productVideo : alt;
7843
+ }
7844
+ 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";
7845
+ function InlineVideoCard({
7846
+ href,
7847
+ poster,
7848
+ alt,
7849
+ scope
7850
+ }) {
7851
+ const { strings: T, failed, onFailed, productSlugs } = scope;
7852
+ const [dialogDir, setDialogDir] = React.useState(null);
7853
+ const buttonRef = React.useRef(null);
7854
+ const returnFocusRef = React.useRef(false);
7855
+ const open = dialogDir !== null;
7856
+ React.useEffect(() => {
7857
+ if (open || !returnFocusRef.current) return;
7858
+ returnFocusRef.current = false;
7859
+ const button = buttonRef.current;
7860
+ if (button?.isConnected) button.focus();
7861
+ }, [open]);
7862
+ const openPlayer = React.useCallback((event) => {
7863
+ const dir = event.currentTarget.closest("[dir]")?.getAttribute("dir");
7864
+ returnFocusRef.current = true;
7865
+ setDialogDir(dir === "rtl" ? "rtl" : "ltr");
7866
+ }, []);
7867
+ const closePlayer = React.useCallback(() => setDialogDir(null), []);
7868
+ const name = videoTitleOf(alt, T);
7869
+ const posterFailed = failed.has(poster);
7870
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7871
+ "span",
7872
+ {
7873
+ "data-boff-explore": "inline-video",
7874
+ "data-product": videoProductOf(href, productSlugs),
7875
+ className: "my-3 block",
7876
+ children: [
7877
+ /* @__PURE__ */ jsxRuntime.jsxs(
7878
+ "button",
7879
+ {
7880
+ ref: buttonRef,
7881
+ type: "button",
7882
+ "aria-haspopup": "dialog",
7883
+ "aria-label": fill(T.playVideo, { title: name }),
7884
+ onClick: openPlayer,
7885
+ className: INLINE_VIDEO_FRAME,
7886
+ children: [
7887
+ posterFailed ? /* @__PURE__ */ jsxRuntime.jsx(
7888
+ "span",
7889
+ {
7890
+ "aria-hidden": "true",
7891
+ className: "absolute inset-0 flex items-center justify-center bg-muted text-muted-foreground",
7892
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-10 w-10" })
7893
+ }
7894
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
7895
+ "img",
7896
+ {
7897
+ src: poster,
7898
+ alt: "",
7899
+ loading: "lazy",
7900
+ decoding: "async",
7901
+ onError: () => onFailed(poster),
7902
+ className: "boff-explore-video-poster absolute inset-0 h-full w-full object-cover"
7903
+ }
7904
+ ),
7905
+ /* @__PURE__ */ jsxRuntime.jsx(
7906
+ "span",
7907
+ {
7908
+ "aria-hidden": "true",
7909
+ className: "pointer-events-none absolute inset-0 flex items-center justify-center",
7910
+ children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(lucideReact.Play, { className: "h-6 w-6 fill-current" }) })
7911
+ }
7912
+ ),
7913
+ /* @__PURE__ */ jsxRuntime.jsx(
7914
+ "span",
7915
+ {
7916
+ "aria-hidden": "true",
7917
+ 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",
7918
+ children: T.productVideo
7919
+ }
7920
+ )
7921
+ ]
7922
+ }
7923
+ ),
7924
+ open ? /* @__PURE__ */ jsxRuntime.jsx(
7925
+ ExploreVideoDialog,
7926
+ {
7927
+ videoUrl: href,
7928
+ posterUrl: poster,
7929
+ title: name,
7930
+ strings: T,
7931
+ dir: dialogDir,
7932
+ onClose: closePlayer
7933
+ }
7934
+ ) : null
7935
+ ]
7936
+ }
7937
+ );
7938
+ }
7411
7939
  var ANSWER_MARKDOWN_COMPONENTS = {
7412
7940
  a: AnswerLink,
7413
7941
  img: InlineConceptImage,
7414
7942
  table: AnswerTable,
7415
7943
  tr: AnswerTableRow
7416
7944
  };
7945
+ var NO_PRODUCT_SLUGS = /* @__PURE__ */ new Map();
7417
7946
  function ExploreAnswerBody({
7418
7947
  children,
7419
7948
  strings,
7420
7949
  imageOrigins,
7950
+ videoOrigins = imageOrigins,
7951
+ productSlugs = NO_PRODUCT_SLUGS,
7421
7952
  enabled = true,
7422
7953
  components,
7423
7954
  className
@@ -7433,16 +7964,21 @@ function ExploreAnswerBody({
7433
7964
  strings,
7434
7965
  enabled,
7435
7966
  origins: imageOrigins,
7967
+ productSlugs,
7436
7968
  failed,
7437
7969
  onFailed
7438
7970
  }),
7439
- [strings, enabled, imageOrigins, failed, onFailed]
7971
+ [strings, enabled, imageOrigins, productSlugs, failed, onFailed]
7972
+ );
7973
+ const rehypePlugins = React.useMemo(
7974
+ () => [[rehypeProductVideo, { origins: videoOrigins }]],
7975
+ [videoOrigins]
7440
7976
  );
7441
7977
  const overrides = React.useMemo(
7442
7978
  () => components ? { ...ANSWER_MARKDOWN_COMPONENTS, ...components } : ANSWER_MARKDOWN_COMPONENTS,
7443
7979
  [components]
7444
7980
  );
7445
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsxRuntime.jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { components: overrides, children }) }) });
7981
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsxRuntime.jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { components: overrides, rehypePlugins, children }) }) });
7446
7982
  }
7447
7983
  var PROSE_CSS = `
7448
7984
  .boff-prose {
@@ -7620,60 +8156,239 @@ var PROSE_CSS = `
7620
8156
  function ProseStyles() {
7621
8157
  return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: PROSE_CSS } });
7622
8158
  }
7623
- var META_DESCRIPTION_LIMIT2 = 160;
7624
- var TITLE_LIMIT = 100;
7625
- var RELATED_LIMIT2 = 4;
7626
- function normaliseSiteUrl2(siteUrl) {
7627
- return siteUrl.trim().replace(/\/+$/, "");
7628
- }
7629
- function absoluteUrl2(base, path) {
7630
- if (/^https?:\/\//i.test(path)) return path;
7631
- return `${base}${path.startsWith("/") ? path : `/${path}`}`;
7632
- }
7633
- function sortByOrder2(answers) {
7634
- return [...answers].sort(
7635
- (a, b) => a.order - b.order || a.slug.localeCompare(b.slug)
7636
- );
7637
- }
7638
- function clampText2(text, limit) {
7639
- if (text.length <= limit) return text;
7640
- const cut = text.slice(0, limit - 1);
7641
- const lastSpace = cut.lastIndexOf(" ");
7642
- return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[\s,;:—–-]+$/, "")}\u2026`;
7643
- }
7644
- function questionCount(T, count) {
7645
- return count === 1 ? T.questionCountOne : fill(T.questionCountMany, { count: String(count) });
7646
- }
7647
- function sourceCount(T, count) {
7648
- return count === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(count) });
7649
- }
7650
- function slugifyAnchor2(value) {
7651
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7652
- }
7653
- function publishableSources(sources) {
7654
- const seen = /* @__PURE__ */ new Set();
7655
- const kept = [];
7656
- for (const source of sources) {
7657
- if (!isPublishedSourceUrl(source.url) || seen.has(source.url)) continue;
7658
- seen.add(source.url);
7659
- kept.push(source);
7660
- }
7661
- return kept;
7662
- }
7663
- function groupByTopic(ordered, topics) {
7664
- const byName = /* @__PURE__ */ new Map();
7665
- for (const answer of ordered) {
7666
- const list = byName.get(answer.topic);
7667
- if (list) list.push(answer);
7668
- else byName.set(answer.topic, [answer]);
7669
- }
7670
- const described = [];
7671
- for (const topic of topics ?? []) {
7672
- const list = byName.get(topic.name);
7673
- if (!list) continue;
7674
- described.push({
7675
- name: topic.name,
7676
- description: topic.description,
8159
+
8160
+ // src/data/products.ts
8161
+ var ECOSYSTEM_PRODUCTS = [
8162
+ {
8163
+ slug: "workspaces",
8164
+ name: "Workspaces",
8165
+ websiteUrl: "https://burdenoff.com"
8166
+ },
8167
+ {
8168
+ slug: "vibecontrols",
8169
+ name: "VibeControls",
8170
+ websiteUrl: "https://vibecontrols.com"
8171
+ },
8172
+ // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore
8173
+ // catalog and the site's own sitemap use this origin, and so must the fallback catalog
8174
+ // built from this list, or /explore rejects FluidGrids concept images until it loads.
8175
+ {
8176
+ slug: "fluidgrids",
8177
+ name: "FluidGrids",
8178
+ websiteUrl: "https://www.fluidgrids.ai"
8179
+ },
8180
+ // BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain
8181
+ // still serves the same site but is no longer the name we link to).
8182
+ { slug: "botlit", name: "BotManor", websiteUrl: "https://www.botmanor.com" },
8183
+ {
8184
+ slug: "bigconsole",
8185
+ name: "BigConsole",
8186
+ websiteUrl: "https://bigconsole.com"
8187
+ },
8188
+ {
8189
+ slug: "semanticfed",
8190
+ name: "SemanticFed",
8191
+ websiteUrl: "https://semanticfed.com"
8192
+ },
8193
+ {
8194
+ slug: "intelwatchtower",
8195
+ name: "IntelWatchtower",
8196
+ websiteUrl: "https://intelwatchtower.com"
8197
+ },
8198
+ { slug: "buildmyiq", name: "BuildMyIQ", websiteUrl: "https://buildmyiq.com" },
8199
+ {
8200
+ slug: "assethandler",
8201
+ name: "AssetHandler",
8202
+ websiteUrl: "https://assethandler.com"
8203
+ },
8204
+ {
8205
+ slug: "crewfoundry",
8206
+ name: "CrewFoundry",
8207
+ websiteUrl: "https://crewfoundry.com"
8208
+ },
8209
+ {
8210
+ slug: "planmagnet",
8211
+ name: "PlanMagnet",
8212
+ websiteUrl: "https://planmagnet.com"
8213
+ },
8214
+ {
8215
+ slug: "eventfullymanaged",
8216
+ name: "EventfullyManaged",
8217
+ websiteUrl: "https://eventfullymanaged.com"
8218
+ },
8219
+ {
8220
+ slug: "headshotmarketing",
8221
+ name: "HeadshotMarketing",
8222
+ websiteUrl: "https://headshotmarketing.com"
8223
+ },
8224
+ {
8225
+ slug: "technospam",
8226
+ name: "TechnoSpam",
8227
+ websiteUrl: "https://technospam.com"
8228
+ },
8229
+ { slug: "kadaikodi", name: "Kadaikodi", websiteUrl: "https://kadaikodi.com" },
8230
+ {
8231
+ slug: "movethewheels",
8232
+ name: "MoveTheWheels",
8233
+ websiteUrl: "https://movethewheels.com"
8234
+ },
8235
+ {
8236
+ slug: "healthybowl",
8237
+ name: "Healthy Bowl",
8238
+ websiteUrl: "https://gethealthybowl.com"
8239
+ },
8240
+ { slug: "ggnomad", name: "GGNomad", websiteUrl: "https://ggnomad.com" },
8241
+ {
8242
+ slug: "timecampus",
8243
+ name: "TimeCampus",
8244
+ websiteUrl: "https://timecampus.com"
8245
+ },
8246
+ {
8247
+ slug: "manufacturedops",
8248
+ name: "ManufacturedOps",
8249
+ websiteUrl: "https://manufacturedops.com"
8250
+ },
8251
+ {
8252
+ slug: "theglobalfuel",
8253
+ name: "TheGlobalFuel",
8254
+ websiteUrl: "https://theglobalfuel.com"
8255
+ },
8256
+ {
8257
+ slug: "housingvista",
8258
+ name: "HousingVista",
8259
+ websiteUrl: "https://housingvista.com"
8260
+ },
8261
+ {
8262
+ slug: "proserviceworld",
8263
+ name: "ProServiceWorld",
8264
+ websiteUrl: "https://proserviceworld.com"
8265
+ },
8266
+ {
8267
+ slug: "mybodyshield",
8268
+ name: "MyBodyShield",
8269
+ websiteUrl: "https://mybodyshield.com"
8270
+ },
8271
+ {
8272
+ slug: "artistrybase",
8273
+ name: "ArtistryBase",
8274
+ websiteUrl: "https://artistrybase.com"
8275
+ },
8276
+ {
8277
+ slug: "theculturepost",
8278
+ name: "TheCulturePost",
8279
+ websiteUrl: "https://theculturepost.com"
8280
+ },
8281
+ { slug: "collabkin", name: "CollabKin", websiteUrl: "https://collabkin.com" },
8282
+ {
8283
+ slug: "comradecircle",
8284
+ name: "ComradeCircle",
8285
+ websiteUrl: "https://comradecircle.com"
8286
+ },
8287
+ {
8288
+ slug: "brainyrich",
8289
+ name: "BrainyRich",
8290
+ websiteUrl: "https://brainyrich.com"
8291
+ },
8292
+ {
8293
+ slug: "cosmicintersection",
8294
+ name: "CosmicIntersection",
8295
+ websiteUrl: "https://cosmicintersection.com"
8296
+ },
8297
+ {
8298
+ slug: "govcitizenhub",
8299
+ name: "GovCitizenHub",
8300
+ websiteUrl: "https://govcitizenhub.com"
8301
+ },
8302
+ {
8303
+ slug: "adaptercloud",
8304
+ name: "AdapterCloud",
8305
+ websiteUrl: "https://adaptercloud.com"
8306
+ },
8307
+ {
8308
+ slug: "subscriberbot",
8309
+ name: "Subscriber Bot",
8310
+ websiteUrl: "https://subscriberbot.com"
8311
+ },
8312
+ {
8313
+ slug: "hookmovies",
8314
+ name: "HookMovies",
8315
+ websiteUrl: "https://hookmovies.com"
8316
+ },
8317
+ {
8318
+ slug: "coolandlovely",
8319
+ name: "CoolAndLovely",
8320
+ websiteUrl: "https://coolandlovely.com"
8321
+ },
8322
+ {
8323
+ slug: "atheletebridge",
8324
+ name: "AthleteBridge",
8325
+ websiteUrl: "https://atheletebridge.com"
8326
+ },
8327
+ {
8328
+ slug: "labsofscience",
8329
+ name: "LabsOfScience",
8330
+ websiteUrl: "https://labsofscience.com"
8331
+ },
8332
+ {
8333
+ slug: "ecoimpacthub",
8334
+ name: "EcoImpactHub",
8335
+ websiteUrl: "https://ecoimpacthub.com"
8336
+ }
8337
+ ];
8338
+ var META_DESCRIPTION_LIMIT2 = 160;
8339
+ var TITLE_LIMIT = 100;
8340
+ var RELATED_LIMIT2 = 4;
8341
+ function normaliseSiteUrl2(siteUrl) {
8342
+ return siteUrl.trim().replace(/\/+$/, "");
8343
+ }
8344
+ function absoluteUrl2(base, path) {
8345
+ if (/^https?:\/\//i.test(path)) return path;
8346
+ return `${base}${path.startsWith("/") ? path : `/${path}`}`;
8347
+ }
8348
+ function sortByOrder2(answers) {
8349
+ return [...answers].sort(
8350
+ (a, b) => a.order - b.order || a.slug.localeCompare(b.slug)
8351
+ );
8352
+ }
8353
+ function clampText2(text, limit) {
8354
+ if (text.length <= limit) return text;
8355
+ const cut = text.slice(0, limit - 1);
8356
+ const lastSpace = cut.lastIndexOf(" ");
8357
+ return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[\s,;:—–-]+$/, "")}\u2026`;
8358
+ }
8359
+ function questionCount(T, count) {
8360
+ return count === 1 ? T.questionCountOne : fill(T.questionCountMany, { count: String(count) });
8361
+ }
8362
+ function sourceCount(T, count) {
8363
+ return count === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(count) });
8364
+ }
8365
+ function slugifyAnchor2(value) {
8366
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
8367
+ }
8368
+ function publishableSources(sources) {
8369
+ const seen = /* @__PURE__ */ new Set();
8370
+ const kept = [];
8371
+ for (const source of sources) {
8372
+ if (!isPublishedSourceUrl(source.url) || seen.has(source.url)) continue;
8373
+ seen.add(source.url);
8374
+ kept.push(source);
8375
+ }
8376
+ return kept;
8377
+ }
8378
+ function groupByTopic(ordered, topics) {
8379
+ const byName = /* @__PURE__ */ new Map();
8380
+ for (const answer of ordered) {
8381
+ const list = byName.get(answer.topic);
8382
+ if (list) list.push(answer);
8383
+ else byName.set(answer.topic, [answer]);
8384
+ }
8385
+ const described = [];
8386
+ for (const topic of topics ?? []) {
8387
+ const list = byName.get(topic.name);
8388
+ if (!list) continue;
8389
+ described.push({
8390
+ name: topic.name,
8391
+ description: topic.description,
7677
8392
  answers: list
7678
8393
  });
7679
8394
  byName.delete(topic.name);
@@ -7725,6 +8440,12 @@ var ANSWER_HEADING_SHIFT = {
7725
8440
  h6: demoteHeading("h6")
7726
8441
  };
7727
8442
  var ENGLISH_TEXT = { lang: "en" };
8443
+ var ECOSYSTEM_SITES = ECOSYSTEM_PRODUCTS.map((product) => ({
8444
+ slug: product.slug,
8445
+ website: product.websiteUrl
8446
+ }));
8447
+ var ECOSYSTEM_PRODUCT_SLUGS = productSlugsByOrigin(ECOSYSTEM_SITES);
8448
+ var ECOSYSTEM_ORIGINS = inlineImageOrigins(ECOSYSTEM_SITES);
7728
8449
  function DefaultAnswerLink({
7729
8450
  href,
7730
8451
  className,
@@ -8223,6 +8944,10 @@ function AnswerDetail({
8223
8944
  ]),
8224
8945
  [base, sources, imageOrigins]
8225
8946
  );
8947
+ const answerVideoOrigins = React.useMemo(
8948
+ () => /* @__PURE__ */ new Set([...answerImageOrigins, ...ECOSYSTEM_ORIGINS]),
8949
+ [answerImageOrigins]
8950
+ );
8226
8951
  const canonical = absoluteUrl2(base, answerPath(answer.slug));
8227
8952
  const answerText = React.useMemo(
8228
8953
  () => answerSpeechText(answer.answer),
@@ -8384,6 +9109,8 @@ function AnswerDetail({
8384
9109
  {
8385
9110
  strings: T,
8386
9111
  imageOrigins: answerImageOrigins,
9112
+ videoOrigins: answerVideoOrigins,
9113
+ productSlugs: ECOSYSTEM_PRODUCT_SLUGS,
8387
9114
  components: ANSWER_HEADING_SHIFT,
8388
9115
  className: "mt-4",
8389
9116
  children: answer.answer
@@ -8745,185 +9472,6 @@ var EXPLORE_SHARED_CONVERSATION_QUERY = (
8745
9472
  `
8746
9473
  );
8747
9474
 
8748
- // src/data/products.ts
8749
- var ECOSYSTEM_PRODUCTS = [
8750
- {
8751
- slug: "workspaces",
8752
- name: "Workspaces",
8753
- websiteUrl: "https://burdenoff.com"
8754
- },
8755
- {
8756
- slug: "vibecontrols",
8757
- name: "VibeControls",
8758
- websiteUrl: "https://vibecontrols.com"
8759
- },
8760
- // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore
8761
- // catalog and the site's own sitemap use this origin, and so must the fallback catalog
8762
- // built from this list, or /explore rejects FluidGrids concept images until it loads.
8763
- {
8764
- slug: "fluidgrids",
8765
- name: "FluidGrids",
8766
- websiteUrl: "https://www.fluidgrids.ai"
8767
- },
8768
- // BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain
8769
- // still serves the same site but is no longer the name we link to).
8770
- { slug: "botlit", name: "BotManor", websiteUrl: "https://www.botmanor.com" },
8771
- {
8772
- slug: "bigconsole",
8773
- name: "BigConsole",
8774
- websiteUrl: "https://bigconsole.com"
8775
- },
8776
- {
8777
- slug: "semanticfed",
8778
- name: "SemanticFed",
8779
- websiteUrl: "https://semanticfed.com"
8780
- },
8781
- {
8782
- slug: "intelwatchtower",
8783
- name: "IntelWatchtower",
8784
- websiteUrl: "https://intelwatchtower.com"
8785
- },
8786
- { slug: "buildmyiq", name: "BuildMyIQ", websiteUrl: "https://buildmyiq.com" },
8787
- {
8788
- slug: "assethandler",
8789
- name: "AssetHandler",
8790
- websiteUrl: "https://assethandler.com"
8791
- },
8792
- {
8793
- slug: "crewfoundry",
8794
- name: "CrewFoundry",
8795
- websiteUrl: "https://crewfoundry.com"
8796
- },
8797
- {
8798
- slug: "planmagnet",
8799
- name: "PlanMagnet",
8800
- websiteUrl: "https://planmagnet.com"
8801
- },
8802
- {
8803
- slug: "eventfullymanaged",
8804
- name: "EventfullyManaged",
8805
- websiteUrl: "https://eventfullymanaged.com"
8806
- },
8807
- {
8808
- slug: "headshotmarketing",
8809
- name: "HeadshotMarketing",
8810
- websiteUrl: "https://headshotmarketing.com"
8811
- },
8812
- {
8813
- slug: "technospam",
8814
- name: "TechnoSpam",
8815
- websiteUrl: "https://technospam.com"
8816
- },
8817
- { slug: "kadaikodi", name: "Kadaikodi", websiteUrl: "https://kadaikodi.com" },
8818
- {
8819
- slug: "movethewheels",
8820
- name: "MoveTheWheels",
8821
- websiteUrl: "https://movethewheels.com"
8822
- },
8823
- {
8824
- slug: "healthybowl",
8825
- name: "Healthy Bowl",
8826
- websiteUrl: "https://gethealthybowl.com"
8827
- },
8828
- { slug: "ggnomad", name: "GGNomad", websiteUrl: "https://ggnomad.com" },
8829
- {
8830
- slug: "timecampus",
8831
- name: "TimeCampus",
8832
- websiteUrl: "https://timecampus.com"
8833
- },
8834
- {
8835
- slug: "manufacturedops",
8836
- name: "ManufacturedOps",
8837
- websiteUrl: "https://manufacturedops.com"
8838
- },
8839
- {
8840
- slug: "theglobalfuel",
8841
- name: "TheGlobalFuel",
8842
- websiteUrl: "https://theglobalfuel.com"
8843
- },
8844
- {
8845
- slug: "housingvista",
8846
- name: "HousingVista",
8847
- websiteUrl: "https://housingvista.com"
8848
- },
8849
- {
8850
- slug: "proserviceworld",
8851
- name: "ProServiceWorld",
8852
- websiteUrl: "https://proserviceworld.com"
8853
- },
8854
- {
8855
- slug: "mybodyshield",
8856
- name: "MyBodyShield",
8857
- websiteUrl: "https://mybodyshield.com"
8858
- },
8859
- {
8860
- slug: "artistrybase",
8861
- name: "ArtistryBase",
8862
- websiteUrl: "https://artistrybase.com"
8863
- },
8864
- {
8865
- slug: "theculturepost",
8866
- name: "TheCulturePost",
8867
- websiteUrl: "https://theculturepost.com"
8868
- },
8869
- { slug: "collabkin", name: "CollabKin", websiteUrl: "https://collabkin.com" },
8870
- {
8871
- slug: "comradecircle",
8872
- name: "ComradeCircle",
8873
- websiteUrl: "https://comradecircle.com"
8874
- },
8875
- {
8876
- slug: "brainyrich",
8877
- name: "BrainyRich",
8878
- websiteUrl: "https://brainyrich.com"
8879
- },
8880
- {
8881
- slug: "cosmicintersection",
8882
- name: "CosmicIntersection",
8883
- websiteUrl: "https://cosmicintersection.com"
8884
- },
8885
- {
8886
- slug: "govcitizenhub",
8887
- name: "GovCitizenHub",
8888
- websiteUrl: "https://govcitizenhub.com"
8889
- },
8890
- {
8891
- slug: "adaptercloud",
8892
- name: "AdapterCloud",
8893
- websiteUrl: "https://adaptercloud.com"
8894
- },
8895
- {
8896
- slug: "subscriberbot",
8897
- name: "Subscriber Bot",
8898
- websiteUrl: "https://subscriberbot.com"
8899
- },
8900
- {
8901
- slug: "hookmovies",
8902
- name: "HookMovies",
8903
- websiteUrl: "https://hookmovies.com"
8904
- },
8905
- {
8906
- slug: "coolandlovely",
8907
- name: "CoolAndLovely",
8908
- websiteUrl: "https://coolandlovely.com"
8909
- },
8910
- {
8911
- slug: "atheletebridge",
8912
- name: "AthleteBridge",
8913
- websiteUrl: "https://atheletebridge.com"
8914
- },
8915
- {
8916
- slug: "labsofscience",
8917
- name: "LabsOfScience",
8918
- websiteUrl: "https://labsofscience.com"
8919
- },
8920
- {
8921
- slug: "ecoimpacthub",
8922
- name: "EcoImpactHub",
8923
- websiteUrl: "https://ecoimpacthub.com"
8924
- }
8925
- ];
8926
-
8927
9475
  // src/utils/recaptcha-v3.ts
8928
9476
  var SCRIPT_ID = "boff-recaptcha-v3";
8929
9477
  var SCRIPT_SRC = "https://www.google.com/recaptcha/api.js?render=";
@@ -10041,7 +10589,7 @@ function isHighSurrogate(code) {
10041
10589
  }
10042
10590
  function safeInlineEnd(line) {
10043
10591
  let codeSpan = null;
10044
- let link = null;
10592
+ const links = [];
10045
10593
  const emphasis = [];
10046
10594
  let at = 0;
10047
10595
  while (at < line.length) {
@@ -10050,6 +10598,13 @@ function safeInlineEnd(line) {
10050
10598
  at += 2;
10051
10599
  continue;
10052
10600
  }
10601
+ const innermost = links[links.length - 1];
10602
+ if (innermost !== void 0 && innermost.kind !== "label") {
10603
+ if (char === "(") links.push({ kind: "paren", start: innermost.start });
10604
+ else if (char === ")") links.pop();
10605
+ at += 1;
10606
+ continue;
10607
+ }
10053
10608
  if (char === "`") {
10054
10609
  let run = 1;
10055
10610
  while (line[at + run] === "`") run += 1;
@@ -10062,17 +10617,17 @@ function safeInlineEnd(line) {
10062
10617
  continue;
10063
10618
  }
10064
10619
  if (char === "[") {
10065
- if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
10620
+ links.push({ kind: "label", start: line[at - 1] === "!" ? at - 1 : at });
10066
10621
  at += 1;
10067
10622
  continue;
10068
10623
  }
10069
- if (char === "]") {
10070
- if (link !== null && line[at + 1] !== "(") link = null;
10071
- at += 1;
10072
- continue;
10073
- }
10074
- if (char === ")") {
10075
- link = null;
10624
+ if (char === "]" && innermost !== void 0) {
10625
+ links.pop();
10626
+ if (line[at + 1] === "(") {
10627
+ links.push({ kind: "destination", start: innermost.start });
10628
+ at += 2;
10629
+ continue;
10630
+ }
10076
10631
  at += 1;
10077
10632
  continue;
10078
10633
  }
@@ -10099,9 +10654,11 @@ function safeInlineEnd(line) {
10099
10654
  }
10100
10655
  at += 1;
10101
10656
  }
10102
- const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
10103
- (value) => value !== null
10104
- );
10657
+ const open = [
10658
+ codeSpan,
10659
+ links[0]?.start ?? null,
10660
+ emphasis[0]?.start ?? null
10661
+ ].filter((value) => value !== null);
10105
10662
  return open.length === 0 ? line.length : Math.min(...open);
10106
10663
  }
10107
10664
  function revealBoundary(text, limit) {
@@ -11963,6 +12520,7 @@ function AssistantBubble({
11963
12520
  message,
11964
12521
  activity,
11965
12522
  imageOrigins,
12523
+ productSlugs,
11966
12524
  productNames,
11967
12525
  speechLang,
11968
12526
  onNavigate,
@@ -12077,6 +12635,7 @@ function AssistantBubble({
12077
12635
  {
12078
12636
  strings: T,
12079
12637
  imageOrigins,
12638
+ productSlugs,
12080
12639
  enabled: completed,
12081
12640
  className: "boff-prose-sm text-card-foreground",
12082
12641
  children: visible
@@ -12834,6 +13393,10 @@ function ExplorePage({
12834
13393
  () => productNamesByOrigin(catalog.products),
12835
13394
  [catalog.products]
12836
13395
  );
13396
+ const productSlugs = React.useMemo(
13397
+ () => productSlugsByOrigin(catalog.products),
13398
+ [catalog.products]
13399
+ );
12837
13400
  React.useEffect(() => {
12838
13401
  const el = textareaRef.current;
12839
13402
  if (!el) return;
@@ -13396,6 +13959,7 @@ function ExplorePage({
13396
13959
  message,
13397
13960
  activity,
13398
13961
  imageOrigins,
13962
+ productSlugs,
13399
13963
  productNames,
13400
13964
  speechLang,
13401
13965
  onNavigate,
@@ -18486,11 +19050,13 @@ exports.getRecaptchaV3Token = getRecaptchaV3Token;
18486
19050
  exports.inlineImageOrigins = inlineImageOrigins;
18487
19051
  exports.isFooterlessPath = isFooterlessPath;
18488
19052
  exports.isInlineImageAllowed = isInlineImageAllowed;
19053
+ exports.isInlineVideoAllowed = isInlineVideoAllowed;
18489
19054
  exports.isPublishedSourceUrl = isPublishedSourceUrl;
18490
19055
  exports.isValidUrl = isValidUrl;
18491
19056
  exports.loadRecaptchaV3 = loadRecaptchaV3;
18492
19057
  exports.looksLikeDeliverableEmail = looksLikeDeliverableEmail;
18493
19058
  exports.normalizeRoutePath = normalizeRoutePath;
19059
+ exports.productSlugsByOrigin = productSlugsByOrigin;
18494
19060
  exports.shouldRenderSiteFooter = shouldRenderSiteFooter;
18495
19061
  exports.speechLocaleOf = speechLocaleOf;
18496
19062
  exports.stopSpeaking = stopSpeaking;