@burdenoff/website-sdk 2026.925.1 → 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 (39) hide show
  1. package/dist/client/index.js +2 -0
  2. package/dist/client/index.js.map +1 -1
  3. package/dist/client/index.mjs +2 -0
  4. package/dist/client/index.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 +1049 -502
  8. package/dist/components/explore.js.map +1 -1
  9. package/dist/components/explore.mjs +1051 -505
  10. package/dist/components/explore.mjs.map +1 -1
  11. package/dist/components/partners.js +3 -2
  12. package/dist/components/partners.js.map +1 -1
  13. package/dist/components/partners.mjs +3 -2
  14. package/dist/components/partners.mjs.map +1 -1
  15. package/dist/content/index.d.mts +1 -1
  16. package/dist/content/index.d.ts +1 -1
  17. package/dist/content/index.js +3 -2
  18. package/dist/content/index.js.map +1 -1
  19. package/dist/content/index.mjs +3 -2
  20. package/dist/content/index.mjs.map +1 -1
  21. package/dist/{explore-C3SEP5K6.d.ts → explore-Cam_ElSJ.d.ts} +71 -13
  22. package/dist/{explore-Crc41y0j.d.mts → explore-hTQm-_SF.d.mts} +71 -13
  23. package/dist/hooks/index.js +24 -14
  24. package/dist/hooks/index.js.map +1 -1
  25. package/dist/hooks/index.mjs +24 -14
  26. package/dist/hooks/index.mjs.map +1 -1
  27. package/dist/{index-BMWQNZDX.d.mts → index-gm3pBCJT.d.mts} +8 -2
  28. package/dist/{index-BqHF-G-S.d.ts → index-ySbOLSD5.d.ts} +8 -2
  29. package/dist/index.d.mts +30 -9
  30. package/dist/index.d.ts +30 -9
  31. package/dist/index.js +829 -260
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +829 -262
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/store/index.js +3 -2
  36. package/dist/store/index.js.map +1 -1
  37. package/dist/store/index.mjs +3 -2
  38. package/dist/store/index.mjs.map +1 -1
  39. 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
 
@@ -280,7 +281,9 @@ var PRODUCT_DOMAINS = [
280
281
  "algoshred.com",
281
282
  "vibecontrols.com",
282
283
  "fluidgrids.com",
284
+ "botmanor.com",
283
285
  "botlit.ai",
286
+ // legacy BotManor domain, still served
284
287
  "bigconsole.com",
285
288
  "gethealthybowl.com",
286
289
  "headshotmarketing.com",
@@ -1136,13 +1139,14 @@ function Markdown({
1136
1139
  children,
1137
1140
  className,
1138
1141
  inline,
1139
- components
1142
+ components,
1143
+ rehypePlugins
1140
1144
  }) {
1141
1145
  const content = /* @__PURE__ */ jsxRuntime.jsx(
1142
1146
  ReactMarkdown__default.default,
1143
1147
  {
1144
1148
  remarkPlugins: [remarkGfm__default.default],
1145
- rehypePlugins: [rehypeSanitize__default.default],
1149
+ rehypePlugins: [rehypeSanitize__default.default, ...rehypePlugins ?? []],
1146
1150
  components: { ...defaultComponents, ...components },
1147
1151
  ...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
1148
1152
  children
@@ -6163,6 +6167,13 @@ var EN = {
6163
6167
  readAloud: "Read aloud",
6164
6168
  stopReading: "Stop reading",
6165
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.",
6166
6177
  groundedIn: "What this answer is based on",
6167
6178
  sourceCountOne: "1 source",
6168
6179
  sourceCountMany: "{count} sources",
@@ -6264,6 +6275,13 @@ var ZH = {
6264
6275
  readAloud: "\u6717\u8BFB",
6265
6276
  stopReading: "\u505C\u6B62\u6717\u8BFB",
6266
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",
6267
6285
  groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
6268
6286
  sourceCountOne: "1 \u4E2A\u6765\u6E90",
6269
6287
  sourceCountMany: "{count} \u4E2A\u6765\u6E90",
@@ -6361,6 +6379,13 @@ var HI = {
6361
6379
  readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
6362
6380
  stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
6363
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",
6364
6389
  groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
6365
6390
  sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
6366
6391
  sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
@@ -6462,6 +6487,13 @@ var ES = {
6462
6487
  readAloud: "Leer en voz alta",
6463
6488
  stopReading: "Dejar de leer",
6464
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.",
6465
6497
  groundedIn: "En qu\xE9 se basa esta respuesta",
6466
6498
  sourceCountOne: "1 fuente",
6467
6499
  sourceCountMany: "{count} fuentes",
@@ -6563,6 +6595,13 @@ var AR = {
6563
6595
  readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
6564
6596
  stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
6565
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.",
6566
6605
  groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
6567
6606
  sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
6568
6607
  sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
@@ -6664,6 +6703,13 @@ var TA = {
6664
6703
  readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
6665
6704
  stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
6666
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.",
6667
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",
6668
6714
  sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
6669
6715
  sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
@@ -6953,7 +6999,14 @@ function answerStrings(locale) {
6953
6999
  ...chrome,
6954
7000
  answerTable: explore.answerTable,
6955
7001
  conceptIllustration: explore.conceptIllustration,
6956
- 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
6957
7010
  };
6958
7011
  }
6959
7012
  var DATE_TAGS = {
@@ -7021,6 +7074,253 @@ function answerSourceHost(url) {
7021
7074
  return "";
7022
7075
  }
7023
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
+ }
7024
7324
  var EXPLORE_ANSWER_CSS = `
7025
7325
  /* Nothing inside an answer may widen the page. Model output carries long URLs, code and
7026
7326
  tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
@@ -7053,6 +7353,14 @@ var EXPLORE_ANSWER_CSS = `
7053
7353
  max-width: 100%;
7054
7354
  height: auto;
7055
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
+ }
7056
7364
 
7057
7365
  /* ---- Comparison and pricing tables ----------------------------------------------------
7058
7366
  The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
@@ -7183,28 +7491,81 @@ function inlineImageOrigins(products) {
7183
7491
  }
7184
7492
  return origins;
7185
7493
  }
7186
- function isInlineImageAllowed(src, origins) {
7187
- 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;
7188
7497
  let url;
7189
7498
  try {
7190
- 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
+ );
7191
7545
  } catch {
7192
7546
  return false;
7193
7547
  }
7194
- if (url.protocol !== "https:" || url.username || url.password) return false;
7195
- return origins.has(url.origin);
7196
7548
  }
7197
7549
  var CODE_FENCE = /^\s*(?:```|~~~)/;
7198
7550
  var TABLE_ROW_LINE = /^\s{0,3}\|/;
7199
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;
7200
7555
  function readableLine(raw, mode) {
7201
7556
  let line = raw;
7202
- line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
7203
- line = mode === "speech" ? (
7204
- // A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
7205
- // prompt asks for two to five links in every answer. The label IS the words.
7206
- line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
7207
- ) : 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
+ }
7208
7569
  line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
7209
7570
  line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
7210
7571
  line = line.replace(/^\s{0,3}>\s?/, "");
@@ -7295,6 +7656,21 @@ function answerSpeechText(markdown) {
7295
7656
  var AnswerScopeContext = React.createContext(null);
7296
7657
  var InsideLinkContext = React.createContext(false);
7297
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
+ }
7298
7674
  return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
7299
7675
  }
7300
7676
  var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
@@ -7406,16 +7782,173 @@ function AnswerTableRow({
7406
7782
  });
7407
7783
  return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
7408
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
+ }
7409
7939
  var ANSWER_MARKDOWN_COMPONENTS = {
7410
7940
  a: AnswerLink,
7411
7941
  img: InlineConceptImage,
7412
7942
  table: AnswerTable,
7413
7943
  tr: AnswerTableRow
7414
7944
  };
7945
+ var NO_PRODUCT_SLUGS = /* @__PURE__ */ new Map();
7415
7946
  function ExploreAnswerBody({
7416
7947
  children,
7417
7948
  strings,
7418
7949
  imageOrigins,
7950
+ videoOrigins = imageOrigins,
7951
+ productSlugs = NO_PRODUCT_SLUGS,
7419
7952
  enabled = true,
7420
7953
  components,
7421
7954
  className
@@ -7431,16 +7964,21 @@ function ExploreAnswerBody({
7431
7964
  strings,
7432
7965
  enabled,
7433
7966
  origins: imageOrigins,
7967
+ productSlugs,
7434
7968
  failed,
7435
7969
  onFailed
7436
7970
  }),
7437
- [strings, enabled, imageOrigins, failed, onFailed]
7971
+ [strings, enabled, imageOrigins, productSlugs, failed, onFailed]
7972
+ );
7973
+ const rehypePlugins = React.useMemo(
7974
+ () => [[rehypeProductVideo, { origins: videoOrigins }]],
7975
+ [videoOrigins]
7438
7976
  );
7439
7977
  const overrides = React.useMemo(
7440
7978
  () => components ? { ...ANSWER_MARKDOWN_COMPONENTS, ...components } : ANSWER_MARKDOWN_COMPONENTS,
7441
7979
  [components]
7442
7980
  );
7443
- 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 }) }) });
7444
7982
  }
7445
7983
  var PROSE_CSS = `
7446
7984
  .boff-prose {
@@ -7618,60 +8156,239 @@ var PROSE_CSS = `
7618
8156
  function ProseStyles() {
7619
8157
  return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: PROSE_CSS } });
7620
8158
  }
7621
- var META_DESCRIPTION_LIMIT2 = 160;
7622
- var TITLE_LIMIT = 100;
7623
- var RELATED_LIMIT2 = 4;
7624
- function normaliseSiteUrl2(siteUrl) {
7625
- return siteUrl.trim().replace(/\/+$/, "");
7626
- }
7627
- function absoluteUrl2(base, path) {
7628
- if (/^https?:\/\//i.test(path)) return path;
7629
- return `${base}${path.startsWith("/") ? path : `/${path}`}`;
7630
- }
7631
- function sortByOrder2(answers) {
7632
- return [...answers].sort(
7633
- (a, b) => a.order - b.order || a.slug.localeCompare(b.slug)
7634
- );
7635
- }
7636
- function clampText2(text, limit) {
7637
- if (text.length <= limit) return text;
7638
- const cut = text.slice(0, limit - 1);
7639
- const lastSpace = cut.lastIndexOf(" ");
7640
- return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[\s,;:—–-]+$/, "")}\u2026`;
7641
- }
7642
- function questionCount(T, count) {
7643
- return count === 1 ? T.questionCountOne : fill(T.questionCountMany, { count: String(count) });
7644
- }
7645
- function sourceCount(T, count) {
7646
- return count === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(count) });
7647
- }
7648
- function slugifyAnchor2(value) {
7649
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7650
- }
7651
- function publishableSources(sources) {
7652
- const seen = /* @__PURE__ */ new Set();
7653
- const kept = [];
7654
- for (const source of sources) {
7655
- if (!isPublishedSourceUrl(source.url) || seen.has(source.url)) continue;
7656
- seen.add(source.url);
7657
- kept.push(source);
7658
- }
7659
- return kept;
7660
- }
7661
- function groupByTopic(ordered, topics) {
7662
- const byName = /* @__PURE__ */ new Map();
7663
- for (const answer of ordered) {
7664
- const list = byName.get(answer.topic);
7665
- if (list) list.push(answer);
7666
- else byName.set(answer.topic, [answer]);
7667
- }
7668
- const described = [];
7669
- for (const topic of topics ?? []) {
7670
- const list = byName.get(topic.name);
7671
- if (!list) continue;
7672
- described.push({
7673
- name: topic.name,
7674
- 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,
7675
8392
  answers: list
7676
8393
  });
7677
8394
  byName.delete(topic.name);
@@ -7723,6 +8440,12 @@ var ANSWER_HEADING_SHIFT = {
7723
8440
  h6: demoteHeading("h6")
7724
8441
  };
7725
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);
7726
8449
  function DefaultAnswerLink({
7727
8450
  href,
7728
8451
  className,
@@ -8221,6 +8944,10 @@ function AnswerDetail({
8221
8944
  ]),
8222
8945
  [base, sources, imageOrigins]
8223
8946
  );
8947
+ const answerVideoOrigins = React.useMemo(
8948
+ () => /* @__PURE__ */ new Set([...answerImageOrigins, ...ECOSYSTEM_ORIGINS]),
8949
+ [answerImageOrigins]
8950
+ );
8224
8951
  const canonical = absoluteUrl2(base, answerPath(answer.slug));
8225
8952
  const answerText = React.useMemo(
8226
8953
  () => answerSpeechText(answer.answer),
@@ -8382,6 +9109,8 @@ function AnswerDetail({
8382
9109
  {
8383
9110
  strings: T,
8384
9111
  imageOrigins: answerImageOrigins,
9112
+ videoOrigins: answerVideoOrigins,
9113
+ productSlugs: ECOSYSTEM_PRODUCT_SLUGS,
8385
9114
  components: ANSWER_HEADING_SHIFT,
8386
9115
  className: "mt-4",
8387
9116
  children: answer.answer
@@ -8743,184 +9472,6 @@ var EXPLORE_SHARED_CONVERSATION_QUERY = (
8743
9472
  `
8744
9473
  );
8745
9474
 
8746
- // src/data/products.ts
8747
- var ECOSYSTEM_PRODUCTS = [
8748
- {
8749
- slug: "workspaces",
8750
- name: "Workspaces",
8751
- websiteUrl: "https://burdenoff.com"
8752
- },
8753
- {
8754
- slug: "vibecontrols",
8755
- name: "VibeControls",
8756
- websiteUrl: "https://vibecontrols.com"
8757
- },
8758
- // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore
8759
- // catalog and the site's own sitemap use this origin, and so must the fallback catalog
8760
- // built from this list, or /explore rejects FluidGrids concept images until it loads.
8761
- {
8762
- slug: "fluidgrids",
8763
- name: "FluidGrids",
8764
- websiteUrl: "https://www.fluidgrids.ai"
8765
- },
8766
- // BotManor ships on botlit.ai — botlit.com does not resolve.
8767
- { slug: "botlit", name: "BotManor", websiteUrl: "https://www.botlit.ai" },
8768
- {
8769
- slug: "bigconsole",
8770
- name: "BigConsole",
8771
- websiteUrl: "https://bigconsole.com"
8772
- },
8773
- {
8774
- slug: "semanticfed",
8775
- name: "SemanticFed",
8776
- websiteUrl: "https://semanticfed.com"
8777
- },
8778
- {
8779
- slug: "intelwatchtower",
8780
- name: "IntelWatchtower",
8781
- websiteUrl: "https://intelwatchtower.com"
8782
- },
8783
- { slug: "buildmyiq", name: "BuildMyIQ", websiteUrl: "https://buildmyiq.com" },
8784
- {
8785
- slug: "assethandler",
8786
- name: "AssetHandler",
8787
- websiteUrl: "https://assethandler.com"
8788
- },
8789
- {
8790
- slug: "crewfoundry",
8791
- name: "CrewFoundry",
8792
- websiteUrl: "https://crewfoundry.com"
8793
- },
8794
- {
8795
- slug: "planmagnet",
8796
- name: "PlanMagnet",
8797
- websiteUrl: "https://planmagnet.com"
8798
- },
8799
- {
8800
- slug: "eventfullymanaged",
8801
- name: "EventfullyManaged",
8802
- websiteUrl: "https://eventfullymanaged.com"
8803
- },
8804
- {
8805
- slug: "headshotmarketing",
8806
- name: "HeadshotMarketing",
8807
- websiteUrl: "https://headshotmarketing.com"
8808
- },
8809
- {
8810
- slug: "technospam",
8811
- name: "TechnoSpam",
8812
- websiteUrl: "https://technospam.com"
8813
- },
8814
- { slug: "kadaikodi", name: "Kadaikodi", websiteUrl: "https://kadaikodi.com" },
8815
- {
8816
- slug: "movethewheels",
8817
- name: "MoveTheWheels",
8818
- websiteUrl: "https://movethewheels.com"
8819
- },
8820
- {
8821
- slug: "healthybowl",
8822
- name: "Healthy Bowl",
8823
- websiteUrl: "https://gethealthybowl.com"
8824
- },
8825
- { slug: "ggnomad", name: "GGNomad", websiteUrl: "https://ggnomad.com" },
8826
- {
8827
- slug: "timecampus",
8828
- name: "TimeCampus",
8829
- websiteUrl: "https://timecampus.com"
8830
- },
8831
- {
8832
- slug: "manufacturedops",
8833
- name: "ManufacturedOps",
8834
- websiteUrl: "https://manufacturedops.com"
8835
- },
8836
- {
8837
- slug: "theglobalfuel",
8838
- name: "TheGlobalFuel",
8839
- websiteUrl: "https://theglobalfuel.com"
8840
- },
8841
- {
8842
- slug: "housingvista",
8843
- name: "HousingVista",
8844
- websiteUrl: "https://housingvista.com"
8845
- },
8846
- {
8847
- slug: "proserviceworld",
8848
- name: "ProServiceWorld",
8849
- websiteUrl: "https://proserviceworld.com"
8850
- },
8851
- {
8852
- slug: "mybodyshield",
8853
- name: "MyBodyShield",
8854
- websiteUrl: "https://mybodyshield.com"
8855
- },
8856
- {
8857
- slug: "artistrybase",
8858
- name: "ArtistryBase",
8859
- websiteUrl: "https://artistrybase.com"
8860
- },
8861
- {
8862
- slug: "theculturepost",
8863
- name: "TheCulturePost",
8864
- websiteUrl: "https://theculturepost.com"
8865
- },
8866
- { slug: "collabkin", name: "CollabKin", websiteUrl: "https://collabkin.com" },
8867
- {
8868
- slug: "comradecircle",
8869
- name: "ComradeCircle",
8870
- websiteUrl: "https://comradecircle.com"
8871
- },
8872
- {
8873
- slug: "brainyrich",
8874
- name: "BrainyRich",
8875
- websiteUrl: "https://brainyrich.com"
8876
- },
8877
- {
8878
- slug: "cosmicintersection",
8879
- name: "CosmicIntersection",
8880
- websiteUrl: "https://cosmicintersection.com"
8881
- },
8882
- {
8883
- slug: "govcitizenhub",
8884
- name: "GovCitizenHub",
8885
- websiteUrl: "https://govcitizenhub.com"
8886
- },
8887
- {
8888
- slug: "adaptercloud",
8889
- name: "AdapterCloud",
8890
- websiteUrl: "https://adaptercloud.com"
8891
- },
8892
- {
8893
- slug: "subscriberbot",
8894
- name: "Subscriber Bot",
8895
- websiteUrl: "https://subscriberbot.com"
8896
- },
8897
- {
8898
- slug: "hookmovies",
8899
- name: "HookMovies",
8900
- websiteUrl: "https://hookmovies.com"
8901
- },
8902
- {
8903
- slug: "coolandlovely",
8904
- name: "CoolAndLovely",
8905
- websiteUrl: "https://coolandlovely.com"
8906
- },
8907
- {
8908
- slug: "atheletebridge",
8909
- name: "AthleteBridge",
8910
- websiteUrl: "https://atheletebridge.com"
8911
- },
8912
- {
8913
- slug: "labsofscience",
8914
- name: "LabsOfScience",
8915
- websiteUrl: "https://labsofscience.com"
8916
- },
8917
- {
8918
- slug: "ecoimpacthub",
8919
- name: "EcoImpactHub",
8920
- websiteUrl: "https://ecoimpacthub.com"
8921
- }
8922
- ];
8923
-
8924
9475
  // src/utils/recaptcha-v3.ts
8925
9476
  var SCRIPT_ID = "boff-recaptcha-v3";
8926
9477
  var SCRIPT_SRC = "https://www.google.com/recaptcha/api.js?render=";
@@ -10038,7 +10589,7 @@ function isHighSurrogate(code) {
10038
10589
  }
10039
10590
  function safeInlineEnd(line) {
10040
10591
  let codeSpan = null;
10041
- let link = null;
10592
+ const links = [];
10042
10593
  const emphasis = [];
10043
10594
  let at = 0;
10044
10595
  while (at < line.length) {
@@ -10047,6 +10598,13 @@ function safeInlineEnd(line) {
10047
10598
  at += 2;
10048
10599
  continue;
10049
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
+ }
10050
10608
  if (char === "`") {
10051
10609
  let run = 1;
10052
10610
  while (line[at + run] === "`") run += 1;
@@ -10059,17 +10617,17 @@ function safeInlineEnd(line) {
10059
10617
  continue;
10060
10618
  }
10061
10619
  if (char === "[") {
10062
- if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
10620
+ links.push({ kind: "label", start: line[at - 1] === "!" ? at - 1 : at });
10063
10621
  at += 1;
10064
10622
  continue;
10065
10623
  }
10066
- if (char === "]") {
10067
- if (link !== null && line[at + 1] !== "(") link = null;
10068
- at += 1;
10069
- continue;
10070
- }
10071
- if (char === ")") {
10072
- 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
+ }
10073
10631
  at += 1;
10074
10632
  continue;
10075
10633
  }
@@ -10096,9 +10654,11 @@ function safeInlineEnd(line) {
10096
10654
  }
10097
10655
  at += 1;
10098
10656
  }
10099
- const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
10100
- (value) => value !== null
10101
- );
10657
+ const open = [
10658
+ codeSpan,
10659
+ links[0]?.start ?? null,
10660
+ emphasis[0]?.start ?? null
10661
+ ].filter((value) => value !== null);
10102
10662
  return open.length === 0 ? line.length : Math.min(...open);
10103
10663
  }
10104
10664
  function revealBoundary(text, limit) {
@@ -11960,6 +12520,7 @@ function AssistantBubble({
11960
12520
  message,
11961
12521
  activity,
11962
12522
  imageOrigins,
12523
+ productSlugs,
11963
12524
  productNames,
11964
12525
  speechLang,
11965
12526
  onNavigate,
@@ -12074,6 +12635,7 @@ function AssistantBubble({
12074
12635
  {
12075
12636
  strings: T,
12076
12637
  imageOrigins,
12638
+ productSlugs,
12077
12639
  enabled: completed,
12078
12640
  className: "boff-prose-sm text-card-foreground",
12079
12641
  children: visible
@@ -12831,6 +13393,10 @@ function ExplorePage({
12831
13393
  () => productNamesByOrigin(catalog.products),
12832
13394
  [catalog.products]
12833
13395
  );
13396
+ const productSlugs = React.useMemo(
13397
+ () => productSlugsByOrigin(catalog.products),
13398
+ [catalog.products]
13399
+ );
12834
13400
  React.useEffect(() => {
12835
13401
  const el = textareaRef.current;
12836
13402
  if (!el) return;
@@ -13393,6 +13959,7 @@ function ExplorePage({
13393
13959
  message,
13394
13960
  activity,
13395
13961
  imageOrigins,
13962
+ productSlugs,
13396
13963
  productNames,
13397
13964
  speechLang,
13398
13965
  onNavigate,
@@ -18483,11 +19050,13 @@ exports.getRecaptchaV3Token = getRecaptchaV3Token;
18483
19050
  exports.inlineImageOrigins = inlineImageOrigins;
18484
19051
  exports.isFooterlessPath = isFooterlessPath;
18485
19052
  exports.isInlineImageAllowed = isInlineImageAllowed;
19053
+ exports.isInlineVideoAllowed = isInlineVideoAllowed;
18486
19054
  exports.isPublishedSourceUrl = isPublishedSourceUrl;
18487
19055
  exports.isValidUrl = isValidUrl;
18488
19056
  exports.loadRecaptchaV3 = loadRecaptchaV3;
18489
19057
  exports.looksLikeDeliverableEmail = looksLikeDeliverableEmail;
18490
19058
  exports.normalizeRoutePath = normalizeRoutePath;
19059
+ exports.productSlugsByOrigin = productSlugsByOrigin;
18491
19060
  exports.shouldRenderSiteFooter = shouldRenderSiteFooter;
18492
19061
  exports.speechLocaleOf = speechLocaleOf;
18493
19062
  exports.stopSpeaking = stopSpeaking;