@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.
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +2 -0
- package/dist/client/index.mjs.map +1 -1
- package/dist/components/explore.d.mts +1 -1
- package/dist/components/explore.d.ts +1 -1
- package/dist/components/explore.js +1049 -502
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +1051 -505
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/partners.js +3 -2
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +3 -2
- package/dist/components/partners.mjs.map +1 -1
- package/dist/content/index.d.mts +1 -1
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.js +3 -2
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +3 -2
- package/dist/content/index.mjs.map +1 -1
- package/dist/{explore-C3SEP5K6.d.ts → explore-Cam_ElSJ.d.ts} +71 -13
- package/dist/{explore-Crc41y0j.d.mts → explore-hTQm-_SF.d.mts} +71 -13
- package/dist/hooks/index.js +24 -14
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +24 -14
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/{index-BMWQNZDX.d.mts → index-gm3pBCJT.d.mts} +8 -2
- package/dist/{index-BqHF-G-S.d.ts → index-ySbOLSD5.d.ts} +8 -2
- package/dist/index.d.mts +30 -9
- package/dist/index.d.ts +30 -9
- package/dist/index.js +829 -260
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +829 -262
- package/dist/index.mjs.map +1 -1
- package/dist/store/index.js +3 -2
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +3 -2
- package/dist/store/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { createContext, useMemo, useState, useRef, useEffect, useCallback, useContext, Children, isValidElement, cloneElement } from 'react';
|
|
3
|
-
import { Sparkles, RotateCcw, Link2, History, Trash2, Paperclip, X, Mic, Square, ArrowUp, BookOpenText, Layers, TriangleAlert, ChevronLeft, ChevronRight, ExternalLink, Check, Copy, ThumbsUp, ThumbsDown, Volume2 } from 'lucide-react';
|
|
2
|
+
import { createContext, useMemo, useState, useRef, useEffect, useCallback, useContext, Children, isValidElement, cloneElement, useId } from 'react';
|
|
3
|
+
import { Sparkles, RotateCcw, Link2, History, Trash2, Paperclip, X, Mic, Square, ArrowUp, BookOpenText, Layers, TriangleAlert, ChevronLeft, ChevronRight, ExternalLink, Check, Copy, ThumbsUp, ThumbsDown, Volume2, Film, Play, Minimize, Maximize, VideoOff } from 'lucide-react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
6
|
import ReactMarkdown from 'react-markdown';
|
|
5
7
|
import rehypeSanitize from 'rehype-sanitize';
|
|
6
8
|
import remarkGfm from 'remark-gfm';
|
|
7
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
9
|
import { clsx } from 'clsx';
|
|
9
10
|
import { twMerge } from 'tailwind-merge';
|
|
10
11
|
import { Helmet } from 'react-helmet-async';
|
|
@@ -14,478 +15,12 @@ import { cva } from 'class-variance-authority';
|
|
|
14
15
|
var __defProp = Object.defineProperty;
|
|
15
16
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
16
17
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
17
|
-
function MarkdownLink({
|
|
18
|
-
href,
|
|
19
|
-
children,
|
|
20
|
-
// react-markdown passes the hast node to every override; it is not a DOM attribute.
|
|
21
|
-
node: _node,
|
|
22
|
-
...props
|
|
23
|
-
}) {
|
|
24
|
-
const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
|
|
25
|
-
return /* @__PURE__ */ jsx(
|
|
26
|
-
"a",
|
|
27
|
-
{
|
|
28
|
-
href,
|
|
29
|
-
...props,
|
|
30
|
-
...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
|
|
31
|
-
children
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
var defaultComponents = {
|
|
36
|
-
a: MarkdownLink
|
|
37
|
-
};
|
|
38
|
-
function Markdown({
|
|
39
|
-
children,
|
|
40
|
-
className,
|
|
41
|
-
inline,
|
|
42
|
-
components
|
|
43
|
-
}) {
|
|
44
|
-
const content = /* @__PURE__ */ jsx(
|
|
45
|
-
ReactMarkdown,
|
|
46
|
-
{
|
|
47
|
-
remarkPlugins: [remarkGfm],
|
|
48
|
-
rehypePlugins: [rehypeSanitize],
|
|
49
|
-
components: { ...defaultComponents, ...components },
|
|
50
|
-
...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
|
|
51
|
-
children
|
|
52
|
-
}
|
|
53
|
-
);
|
|
54
|
-
if (inline) {
|
|
55
|
-
return /* @__PURE__ */ jsx("span", { className, children: content });
|
|
56
|
-
}
|
|
57
|
-
return /* @__PURE__ */ jsx("div", { className, children: content });
|
|
58
|
-
}
|
|
59
|
-
function cn(...inputs) {
|
|
60
|
-
return twMerge(clsx(inputs));
|
|
61
|
-
}
|
|
62
|
-
var EXPLORE_ANSWER_CSS = `
|
|
63
|
-
/* Nothing inside an answer may widen the page. Model output carries long URLs, code and
|
|
64
|
-
tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
|
|
65
|
-
WHOLE document horizontally scrollable, not just the bubble. */
|
|
66
|
-
.boff-explore-body,
|
|
67
|
-
.boff-explore-body p,
|
|
68
|
-
.boff-explore-body li,
|
|
69
|
-
.boff-explore-body a,
|
|
70
|
-
.boff-explore-body h1,
|
|
71
|
-
.boff-explore-body h2,
|
|
72
|
-
.boff-explore-body h3,
|
|
73
|
-
/* h4-h6 as well: a published answer page pushes the model's own headings down a level or
|
|
74
|
-
two so they nest under the page's, and a long unbroken word is no narrower for it. */
|
|
75
|
-
.boff-explore-body h4,
|
|
76
|
-
.boff-explore-body h5,
|
|
77
|
-
.boff-explore-body h6 {
|
|
78
|
-
overflow-wrap: anywhere;
|
|
79
|
-
word-break: break-word;
|
|
80
|
-
}
|
|
81
|
-
.boff-explore-body pre {
|
|
82
|
-
overflow-x: auto;
|
|
83
|
-
max-width: 100%;
|
|
84
|
-
}
|
|
85
|
-
.boff-explore-body table {
|
|
86
|
-
display: block;
|
|
87
|
-
overflow-x: auto;
|
|
88
|
-
max-width: 100%;
|
|
89
|
-
}
|
|
90
|
-
.boff-explore-body img {
|
|
91
|
-
max-width: 100%;
|
|
92
|
-
height: auto;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/* ---- Comparison and pricing tables ----------------------------------------------------
|
|
96
|
-
The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
|
|
97
|
-
the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
|
|
98
|
-
the page must not scroll sideways because of it, and it has to be readable on a 320px
|
|
99
|
-
phone, which a four-column grid of 1px borders is not.
|
|
100
|
-
|
|
101
|
-
Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
|
|
102
|
-
\`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
|
|
103
|
-
cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
|
|
104
|
-
of table from chaining to the page \u2014 the same rule the transcript needed. */
|
|
105
|
-
.boff-explore-body .boff-explore-table {
|
|
106
|
-
max-width: 100%;
|
|
107
|
-
overflow-x: auto;
|
|
108
|
-
overscroll-behavior-x: contain;
|
|
109
|
-
margin: 0.75rem 0;
|
|
110
|
-
border: 1px solid hsl(var(--border));
|
|
111
|
-
border-radius: 0.75rem;
|
|
112
|
-
background-color: hsl(var(--card));
|
|
113
|
-
}
|
|
114
|
-
.boff-explore-body .boff-explore-table:focus-visible {
|
|
115
|
-
outline: 2px solid hsl(var(--ring));
|
|
116
|
-
outline-offset: 2px;
|
|
117
|
-
}
|
|
118
|
-
.boff-explore-body .boff-explore-table table {
|
|
119
|
-
/* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
|
|
120
|
-
display: table;
|
|
121
|
-
width: 100%;
|
|
122
|
-
max-width: none;
|
|
123
|
-
margin: 0;
|
|
124
|
-
border-collapse: collapse;
|
|
125
|
-
font-size: 0.8125rem;
|
|
126
|
-
}
|
|
127
|
-
.boff-explore-body .boff-explore-table th,
|
|
128
|
-
.boff-explore-body .boff-explore-table td {
|
|
129
|
-
border: 0;
|
|
130
|
-
border-top: 1px solid hsl(var(--border));
|
|
131
|
-
padding: 0.5rem 0.75rem;
|
|
132
|
-
/* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
|
|
133
|
-
table there reads against the grain of every other line on the page. */
|
|
134
|
-
text-align: start;
|
|
135
|
-
vertical-align: top;
|
|
136
|
-
overflow-wrap: anywhere;
|
|
137
|
-
/* Prices in a column line up under each other instead of drifting by digit width. */
|
|
138
|
-
font-variant-numeric: tabular-nums;
|
|
139
|
-
min-width: 6rem;
|
|
140
|
-
}
|
|
141
|
-
.boff-explore-body .boff-explore-table thead th {
|
|
142
|
-
border-top: 0;
|
|
143
|
-
background-color: hsl(var(--muted));
|
|
144
|
-
color: hsl(var(--foreground));
|
|
145
|
-
font-weight: 600;
|
|
146
|
-
white-space: nowrap;
|
|
147
|
-
}
|
|
148
|
-
/* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
|
|
149
|
-
the row's heading is what makes a price list scannable rather than a grid of strings. */
|
|
150
|
-
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
151
|
-
font-weight: 600;
|
|
152
|
-
color: hsl(var(--card-foreground));
|
|
153
|
-
}
|
|
154
18
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
.boff-explore-body .boff-explore-table {
|
|
161
|
-
overflow-x: visible;
|
|
162
|
-
}
|
|
163
|
-
.boff-explore-body .boff-explore-table table,
|
|
164
|
-
.boff-explore-body .boff-explore-table tbody,
|
|
165
|
-
.boff-explore-body .boff-explore-table tr,
|
|
166
|
-
.boff-explore-body .boff-explore-table td {
|
|
167
|
-
display: block;
|
|
168
|
-
width: 100%;
|
|
169
|
-
min-width: 0;
|
|
170
|
-
}
|
|
171
|
-
.boff-explore-body .boff-explore-table thead {
|
|
172
|
-
display: none;
|
|
173
|
-
}
|
|
174
|
-
.boff-explore-body .boff-explore-table tr {
|
|
175
|
-
border-top: 1px solid hsl(var(--border));
|
|
176
|
-
padding: 0.5rem 0;
|
|
177
|
-
}
|
|
178
|
-
.boff-explore-body .boff-explore-table tr:first-child {
|
|
179
|
-
border-top: 0;
|
|
180
|
-
}
|
|
181
|
-
.boff-explore-body .boff-explore-table td {
|
|
182
|
-
border: 0;
|
|
183
|
-
padding: 0.125rem 0.75rem;
|
|
184
|
-
}
|
|
185
|
-
.boff-explore-body .boff-explore-table td[data-label]::before {
|
|
186
|
-
content: attr(data-label);
|
|
187
|
-
display: block;
|
|
188
|
-
font-size: 0.6875rem;
|
|
189
|
-
font-weight: 600;
|
|
190
|
-
text-transform: uppercase;
|
|
191
|
-
letter-spacing: 0.04em;
|
|
192
|
-
color: hsl(var(--muted-foreground));
|
|
193
|
-
}
|
|
194
|
-
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
195
|
-
font-size: 0.9375rem;
|
|
196
|
-
padding-bottom: 0.25rem;
|
|
197
|
-
}
|
|
198
|
-
.boff-explore-body .boff-explore-table tbody td:first-child::before {
|
|
199
|
-
/* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
|
|
200
|
-
display: none;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
`;
|
|
204
|
-
function ExploreAnswerStyles() {
|
|
205
|
-
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_ANSWER_CSS } });
|
|
206
|
-
}
|
|
207
|
-
function inlineImageOrigins(products) {
|
|
208
|
-
const origins = /* @__PURE__ */ new Set();
|
|
209
|
-
for (const product of products) {
|
|
210
|
-
let url;
|
|
211
|
-
try {
|
|
212
|
-
url = new URL(product.website);
|
|
213
|
-
} catch {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (url.protocol !== "https:") continue;
|
|
217
|
-
const apex = url.hostname.replace(/^www\./, "");
|
|
218
|
-
const port = url.port ? `:${url.port}` : "";
|
|
219
|
-
origins.add(`https://${apex}${port}`);
|
|
220
|
-
origins.add(`https://www.${apex}${port}`);
|
|
221
|
-
}
|
|
222
|
-
return origins;
|
|
223
|
-
}
|
|
224
|
-
function isInlineImageAllowed(src, origins) {
|
|
225
|
-
if (typeof src !== "string" || src === "") return false;
|
|
226
|
-
let url;
|
|
227
|
-
try {
|
|
228
|
-
url = new URL(src);
|
|
229
|
-
} catch {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
if (url.protocol !== "https:" || url.username || url.password) return false;
|
|
233
|
-
return origins.has(url.origin);
|
|
234
|
-
}
|
|
235
|
-
var CODE_FENCE = /^\s*(?:```|~~~)/;
|
|
236
|
-
var TABLE_ROW_LINE = /^\s{0,3}\|/;
|
|
237
|
-
var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
|
|
238
|
-
function readableLine(raw, mode) {
|
|
239
|
-
let line = raw;
|
|
240
|
-
line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
|
|
241
|
-
line = mode === "speech" ? (
|
|
242
|
-
// A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
|
|
243
|
-
// prompt asks for two to five links in every answer. The label IS the words.
|
|
244
|
-
line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
|
|
245
|
-
) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
|
|
246
|
-
line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
|
|
247
|
-
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
248
|
-
line = line.replace(/^\s{0,3}>\s?/, "");
|
|
249
|
-
if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
|
|
250
|
-
line = line.replace(/`([^`]+)`/g, "$1");
|
|
251
|
-
line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
252
|
-
line = line.replace(/__([^_]+)__/g, "$1");
|
|
253
|
-
line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
|
|
254
|
-
line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
|
|
255
|
-
return line.replace(/[ \t]+$/, "");
|
|
256
|
-
}
|
|
257
|
-
function tableCells(row) {
|
|
258
|
-
const trimmed = row.trim();
|
|
259
|
-
const cells = [];
|
|
260
|
-
let cell = "";
|
|
261
|
-
for (let at = 0; at < trimmed.length; at += 1) {
|
|
262
|
-
const char = trimmed[at] ?? "";
|
|
263
|
-
if (char === "\\") {
|
|
264
|
-
cell += char + (trimmed[at + 1] ?? "");
|
|
265
|
-
at += 1;
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
|
-
if (char === "|") {
|
|
269
|
-
cells.push(cell.trim());
|
|
270
|
-
cell = "";
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
cell += char;
|
|
274
|
-
}
|
|
275
|
-
cells.push(cell.trim());
|
|
276
|
-
if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
|
|
277
|
-
if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
|
|
278
|
-
return cells;
|
|
279
|
-
}
|
|
280
|
-
function asSentence(text) {
|
|
281
|
-
return /[.!?]$/.test(text) ? text : `${text}.`;
|
|
282
|
-
}
|
|
283
|
-
function spokenTable(rows) {
|
|
284
|
-
if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
|
|
285
|
-
const labels = tableCells(rows[0] ?? "");
|
|
286
|
-
const spoken = [];
|
|
287
|
-
for (const row of rows.slice(2)) {
|
|
288
|
-
const parts = [];
|
|
289
|
-
tableCells(row).forEach((value, column) => {
|
|
290
|
-
if (value === "") return;
|
|
291
|
-
const label = column === 0 ? "" : labels[column] ?? "";
|
|
292
|
-
parts.push(label === "" ? value : `${label}: ${value}`);
|
|
293
|
-
});
|
|
294
|
-
if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
|
|
295
|
-
}
|
|
296
|
-
return spoken;
|
|
297
|
-
}
|
|
298
|
-
function readableText(markdown, mode) {
|
|
299
|
-
const lines = [];
|
|
300
|
-
let inFence = false;
|
|
301
|
-
let table = [];
|
|
302
|
-
const flushTable = () => {
|
|
303
|
-
if (table.length === 0) return;
|
|
304
|
-
for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
|
|
305
|
-
table = [];
|
|
306
|
-
};
|
|
307
|
-
for (const raw of markdown.split("\n")) {
|
|
308
|
-
if (CODE_FENCE.test(raw)) {
|
|
309
|
-
flushTable();
|
|
310
|
-
inFence = !inFence;
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
if (inFence) {
|
|
314
|
-
lines.push(raw);
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
|
|
318
|
-
table.push(raw);
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
flushTable();
|
|
322
|
-
lines.push(readableLine(raw, mode));
|
|
323
|
-
}
|
|
324
|
-
flushTable();
|
|
325
|
-
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
326
|
-
}
|
|
327
|
-
function answerPlainText(markdown) {
|
|
328
|
-
return readableText(markdown, "clipboard");
|
|
329
|
-
}
|
|
330
|
-
function answerSpeechText(markdown) {
|
|
331
|
-
return readableText(markdown, "speech");
|
|
332
|
-
}
|
|
333
|
-
var AnswerScopeContext = createContext(null);
|
|
334
|
-
var InsideLinkContext = createContext(false);
|
|
335
|
-
function AnswerLink(props) {
|
|
336
|
-
return /* @__PURE__ */ jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx(MarkdownLink, { ...props }) });
|
|
337
|
-
}
|
|
338
|
-
var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
|
|
339
|
-
function InlineConceptImage({ src, alt }) {
|
|
340
|
-
const scope = useContext(AnswerScopeContext);
|
|
341
|
-
const insideLink = useContext(InsideLinkContext);
|
|
342
|
-
if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
345
|
-
const { strings: T, onFailed } = scope;
|
|
346
|
-
const picture = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
347
|
-
/* @__PURE__ */ jsx(
|
|
348
|
-
"img",
|
|
349
|
-
{
|
|
350
|
-
src,
|
|
351
|
-
alt: alt ?? "",
|
|
352
|
-
loading: "lazy",
|
|
353
|
-
decoding: "async",
|
|
354
|
-
onError: () => onFailed(src),
|
|
355
|
-
className: "block h-auto max-h-80 w-auto max-w-full object-contain"
|
|
356
|
-
}
|
|
357
|
-
),
|
|
358
|
-
/* @__PURE__ */ jsx(
|
|
359
|
-
"span",
|
|
360
|
-
{
|
|
361
|
-
"aria-hidden": "true",
|
|
362
|
-
className: "pointer-events-none absolute left-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",
|
|
363
|
-
children: T.conceptIllustration
|
|
364
|
-
}
|
|
365
|
-
)
|
|
366
|
-
] });
|
|
367
|
-
return /* @__PURE__ */ jsx("span", { "data-boff-explore": "inline-image", className: "my-3 block", children: insideLink ? /* @__PURE__ */ jsx("span", { className: INLINE_IMAGE_FRAME, children: picture }) : /* @__PURE__ */ jsxs(
|
|
368
|
-
"a",
|
|
369
|
-
{
|
|
370
|
-
href: src,
|
|
371
|
-
target: "_blank",
|
|
372
|
-
rel: "noopener noreferrer",
|
|
373
|
-
className: cn(
|
|
374
|
-
INLINE_IMAGE_FRAME,
|
|
375
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
376
|
-
),
|
|
377
|
-
children: [
|
|
378
|
-
picture,
|
|
379
|
-
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
380
|
-
"(",
|
|
381
|
-
T.opensInNewTab,
|
|
382
|
-
")"
|
|
383
|
-
] })
|
|
384
|
-
]
|
|
385
|
-
}
|
|
386
|
-
) });
|
|
387
|
-
}
|
|
388
|
-
function markdownText(node) {
|
|
389
|
-
if (!node) return "";
|
|
390
|
-
if (typeof node.value === "string") return node.value;
|
|
391
|
-
return (node.children ?? []).map(markdownText).join("");
|
|
392
|
-
}
|
|
393
|
-
function hasHeaderCells(node) {
|
|
394
|
-
return (node?.children ?? []).some((cell) => cell.tagName === "th");
|
|
395
|
-
}
|
|
396
|
-
function findHeaderRow(node) {
|
|
397
|
-
for (const child of node?.children ?? []) {
|
|
398
|
-
if (child.tagName === "tr" && hasHeaderCells(child)) return child;
|
|
399
|
-
const nested = findHeaderRow(child);
|
|
400
|
-
if (nested !== null) return nested;
|
|
401
|
-
}
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
|
-
function tableColumnLabels(node) {
|
|
405
|
-
const row = findHeaderRow(node);
|
|
406
|
-
if (row === null) return [];
|
|
407
|
-
return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
|
|
408
|
-
}
|
|
409
|
-
var TableColumnsContext = createContext([]);
|
|
410
|
-
function AnswerTable({ node, children }) {
|
|
411
|
-
const scope = useContext(AnswerScopeContext);
|
|
412
|
-
const table = /* @__PURE__ */ jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsx("table", { children }) });
|
|
413
|
-
if (!scope) return table;
|
|
414
|
-
return /* @__PURE__ */ jsx(
|
|
415
|
-
"div",
|
|
416
|
-
{
|
|
417
|
-
"data-boff-explore": "table",
|
|
418
|
-
role: "region",
|
|
419
|
-
"aria-label": scope.strings.answerTable,
|
|
420
|
-
tabIndex: 0,
|
|
421
|
-
className: "boff-explore-table",
|
|
422
|
-
children: table
|
|
423
|
-
}
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
function AnswerTableRow({
|
|
427
|
-
node,
|
|
428
|
-
children,
|
|
429
|
-
...props
|
|
430
|
-
}) {
|
|
431
|
-
const labels = useContext(TableColumnsContext);
|
|
432
|
-
if (labels.length === 0 || hasHeaderCells(node)) {
|
|
433
|
-
return /* @__PURE__ */ jsx("tr", { ...props, children });
|
|
434
|
-
}
|
|
435
|
-
let column = 0;
|
|
436
|
-
const cells = Children.map(children, (cell) => {
|
|
437
|
-
if (!isValidElement(cell)) return cell;
|
|
438
|
-
const label = labels[column] ?? "";
|
|
439
|
-
column += 1;
|
|
440
|
-
if (label === "") return cell;
|
|
441
|
-
return cloneElement(cell, {
|
|
442
|
-
"data-label": label
|
|
443
|
-
});
|
|
444
|
-
});
|
|
445
|
-
return /* @__PURE__ */ jsx("tr", { ...props, children: cells });
|
|
446
|
-
}
|
|
447
|
-
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
448
|
-
a: AnswerLink,
|
|
449
|
-
img: InlineConceptImage,
|
|
450
|
-
table: AnswerTable,
|
|
451
|
-
tr: AnswerTableRow
|
|
452
|
-
};
|
|
453
|
-
function ExploreAnswerBody({
|
|
454
|
-
children,
|
|
455
|
-
strings,
|
|
456
|
-
imageOrigins,
|
|
457
|
-
enabled = true,
|
|
458
|
-
components,
|
|
459
|
-
className
|
|
460
|
-
}) {
|
|
461
|
-
const [failed, setFailed] = useState(() => /* @__PURE__ */ new Set());
|
|
462
|
-
const onFailed = useCallback((src) => {
|
|
463
|
-
setFailed(
|
|
464
|
-
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
465
|
-
);
|
|
466
|
-
}, []);
|
|
467
|
-
const scope = useMemo(
|
|
468
|
-
() => ({
|
|
469
|
-
strings,
|
|
470
|
-
enabled,
|
|
471
|
-
origins: imageOrigins,
|
|
472
|
-
failed,
|
|
473
|
-
onFailed
|
|
474
|
-
}),
|
|
475
|
-
[strings, enabled, imageOrigins, failed, onFailed]
|
|
476
|
-
);
|
|
477
|
-
const overrides = useMemo(
|
|
478
|
-
() => components ? { ...ANSWER_MARKDOWN_COMPONENTS, ...components } : ANSWER_MARKDOWN_COMPONENTS,
|
|
479
|
-
[components]
|
|
480
|
-
);
|
|
481
|
-
return /* @__PURE__ */ jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsx(Markdown, { components: overrides, children }) }) });
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// src/components/explore-i18n.ts
|
|
485
|
-
var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
|
|
486
|
-
var RTL_LOCALES = /* @__PURE__ */ new Set(["ar"]);
|
|
487
|
-
function isRtlLocale(locale) {
|
|
488
|
-
return RTL_LOCALES.has(locale);
|
|
19
|
+
// src/components/explore-i18n.ts
|
|
20
|
+
var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
|
|
21
|
+
var RTL_LOCALES = /* @__PURE__ */ new Set(["ar"]);
|
|
22
|
+
function isRtlLocale(locale) {
|
|
23
|
+
return RTL_LOCALES.has(locale);
|
|
489
24
|
}
|
|
490
25
|
var EN = {
|
|
491
26
|
badge: "AI answers, grounded in our documentation",
|
|
@@ -568,6 +103,13 @@ var EN = {
|
|
|
568
103
|
readAloud: "Read aloud",
|
|
569
104
|
stopReading: "Stop reading",
|
|
570
105
|
answerTable: "Table",
|
|
106
|
+
productVideo: "Product video",
|
|
107
|
+
productVideoTitle: "{product} product video",
|
|
108
|
+
playVideo: "Play {title}",
|
|
109
|
+
closeVideo: "Close video",
|
|
110
|
+
enterFullScreen: "Full screen",
|
|
111
|
+
exitFullScreen: "Exit full screen",
|
|
112
|
+
videoUnavailable: "This video can't be played right now.",
|
|
571
113
|
groundedIn: "What this answer is based on",
|
|
572
114
|
sourceCountOne: "1 source",
|
|
573
115
|
sourceCountMany: "{count} sources",
|
|
@@ -669,6 +211,13 @@ var ZH = {
|
|
|
669
211
|
readAloud: "\u6717\u8BFB",
|
|
670
212
|
stopReading: "\u505C\u6B62\u6717\u8BFB",
|
|
671
213
|
answerTable: "\u8868\u683C",
|
|
214
|
+
productVideo: "\u4EA7\u54C1\u89C6\u9891",
|
|
215
|
+
productVideoTitle: "{product} \u4EA7\u54C1\u89C6\u9891",
|
|
216
|
+
playVideo: "\u64AD\u653E {title}",
|
|
217
|
+
closeVideo: "\u5173\u95ED\u89C6\u9891",
|
|
218
|
+
enterFullScreen: "\u5168\u5C4F",
|
|
219
|
+
exitFullScreen: "\u9000\u51FA\u5168\u5C4F",
|
|
220
|
+
videoUnavailable: "\u6682\u65F6\u65E0\u6CD5\u64AD\u653E\u6B64\u89C6\u9891\u3002",
|
|
672
221
|
groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
|
|
673
222
|
sourceCountOne: "1 \u4E2A\u6765\u6E90",
|
|
674
223
|
sourceCountMany: "{count} \u4E2A\u6765\u6E90",
|
|
@@ -766,6 +315,13 @@ var HI = {
|
|
|
766
315
|
readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
|
|
767
316
|
stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
768
317
|
answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
|
|
318
|
+
productVideo: "\u0909\u0924\u094D\u092A\u093E\u0926 \u0935\u0940\u0921\u093F\u092F\u094B",
|
|
319
|
+
productVideoTitle: "{product} \u0909\u0924\u094D\u092A\u093E\u0926 \u0935\u0940\u0921\u093F\u092F\u094B",
|
|
320
|
+
playVideo: "{title} \u091A\u0932\u093E\u090F\u0901",
|
|
321
|
+
closeVideo: "\u0935\u0940\u0921\u093F\u092F\u094B \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
322
|
+
enterFullScreen: "\u092A\u0942\u0930\u094D\u0923 \u0938\u094D\u0915\u094D\u0930\u0940\u0928",
|
|
323
|
+
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",
|
|
324
|
+
videoUnavailable: "\u092F\u0939 \u0935\u0940\u0921\u093F\u092F\u094B \u0905\u092D\u0940 \u0928\u0939\u0940\u0902 \u091A\u0932 \u0938\u0915\u0924\u093E\u0964",
|
|
769
325
|
groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
|
|
770
326
|
sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
|
|
771
327
|
sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
|
|
@@ -867,6 +423,13 @@ var ES = {
|
|
|
867
423
|
readAloud: "Leer en voz alta",
|
|
868
424
|
stopReading: "Dejar de leer",
|
|
869
425
|
answerTable: "Tabla",
|
|
426
|
+
productVideo: "V\xEDdeo del producto",
|
|
427
|
+
productVideoTitle: "V\xEDdeo de {product}",
|
|
428
|
+
playVideo: "Reproducir {title}",
|
|
429
|
+
closeVideo: "Cerrar v\xEDdeo",
|
|
430
|
+
enterFullScreen: "Pantalla completa",
|
|
431
|
+
exitFullScreen: "Salir de pantalla completa",
|
|
432
|
+
videoUnavailable: "Ahora mismo no se puede reproducir este v\xEDdeo.",
|
|
870
433
|
groundedIn: "En qu\xE9 se basa esta respuesta",
|
|
871
434
|
sourceCountOne: "1 fuente",
|
|
872
435
|
sourceCountMany: "{count} fuentes",
|
|
@@ -968,6 +531,13 @@ var AR = {
|
|
|
968
531
|
readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
|
|
969
532
|
stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
|
|
970
533
|
answerTable: "\u062C\u062F\u0648\u0644",
|
|
534
|
+
productVideo: "\u0641\u064A\u062F\u064A\u0648 \u0627\u0644\u0645\u0646\u062A\u062C",
|
|
535
|
+
productVideoTitle: "\u0641\u064A\u062F\u064A\u0648 {product}",
|
|
536
|
+
playVideo: "\u062A\u0634\u063A\u064A\u0644 {title}",
|
|
537
|
+
closeVideo: "\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0641\u064A\u062F\u064A\u0648",
|
|
538
|
+
enterFullScreen: "\u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629",
|
|
539
|
+
exitFullScreen: "\u0627\u0644\u062E\u0631\u0648\u062C \u0645\u0646 \u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629",
|
|
540
|
+
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.",
|
|
971
541
|
groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
972
542
|
sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
|
|
973
543
|
sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
|
|
@@ -1069,6 +639,13 @@ var TA = {
|
|
|
1069
639
|
readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
|
|
1070
640
|
stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
1071
641
|
answerTable: "\u0B85\u0B9F\u0BCD\u0B9F\u0BB5\u0BA3\u0BC8",
|
|
642
|
+
productVideo: "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1 \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB",
|
|
643
|
+
productVideoTitle: "{product} \u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1 \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB",
|
|
644
|
+
playVideo: "{title} \u0B87\u0BAF\u0B95\u0BCD\u0B95\u0BC1",
|
|
645
|
+
closeVideo: "\u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB\u0BB5\u0BC8 \u0BAE\u0BC2\u0B9F\u0BC1",
|
|
646
|
+
enterFullScreen: "\u0BAE\u0BC1\u0BB4\u0BC1\u0BA4\u0BCD \u0BA4\u0BBF\u0BB0\u0BC8",
|
|
647
|
+
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",
|
|
648
|
+
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.",
|
|
1072
649
|
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",
|
|
1073
650
|
sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
|
|
1074
651
|
sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
@@ -1111,25 +688,977 @@ var SPEECH_TAGS = {
|
|
|
1111
688
|
function speechLocaleOf(locale) {
|
|
1112
689
|
return SPEECH_TAGS[locale];
|
|
1113
690
|
}
|
|
1114
|
-
function resolveExploreLocale(explicit) {
|
|
1115
|
-
const candidates = [
|
|
1116
|
-
explicit,
|
|
1117
|
-
typeof document === "undefined" ? void 0 : document.documentElement.lang,
|
|
1118
|
-
typeof navigator === "undefined" ? void 0 : navigator.language
|
|
1119
|
-
];
|
|
1120
|
-
for (const candidate of candidates) {
|
|
1121
|
-
if (!candidate) continue;
|
|
1122
|
-
const primary = candidate.toLowerCase().split("-")[0];
|
|
1123
|
-
const match = EXPLORE_LOCALES.find((locale) => locale === primary);
|
|
1124
|
-
if (match) return match;
|
|
691
|
+
function resolveExploreLocale(explicit) {
|
|
692
|
+
const candidates = [
|
|
693
|
+
explicit,
|
|
694
|
+
typeof document === "undefined" ? void 0 : document.documentElement.lang,
|
|
695
|
+
typeof navigator === "undefined" ? void 0 : navigator.language
|
|
696
|
+
];
|
|
697
|
+
for (const candidate of candidates) {
|
|
698
|
+
if (!candidate) continue;
|
|
699
|
+
const primary = candidate.toLowerCase().split("-")[0];
|
|
700
|
+
const match = EXPLORE_LOCALES.find((locale) => locale === primary);
|
|
701
|
+
if (match) return match;
|
|
702
|
+
}
|
|
703
|
+
return "en";
|
|
704
|
+
}
|
|
705
|
+
function exploreStrings(locale) {
|
|
706
|
+
return STRINGS[locale] ?? EN;
|
|
707
|
+
}
|
|
708
|
+
function fill(template, values) {
|
|
709
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => values[key] ?? match);
|
|
710
|
+
}
|
|
711
|
+
function fullscreenSupport() {
|
|
712
|
+
if (typeof document === "undefined") return null;
|
|
713
|
+
if (document.fullscreenEnabled && typeof document.documentElement.requestFullscreen === "function") {
|
|
714
|
+
return "element";
|
|
715
|
+
}
|
|
716
|
+
if (typeof HTMLVideoElement !== "undefined" && "webkitEnterFullscreen" in HTMLVideoElement.prototype) {
|
|
717
|
+
return "video";
|
|
718
|
+
}
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
function ignoreRejection(result) {
|
|
722
|
+
result?.catch(() => void 0);
|
|
723
|
+
}
|
|
724
|
+
var VIDEO_DIALOG_CSS = `
|
|
725
|
+
.boff-explore-video-panel:fullscreen {
|
|
726
|
+
display: flex;
|
|
727
|
+
flex-direction: column;
|
|
728
|
+
gap: 0.75rem;
|
|
729
|
+
padding: 1rem;
|
|
730
|
+
background-color: hsl(var(--background));
|
|
731
|
+
color: hsl(var(--foreground));
|
|
732
|
+
}
|
|
733
|
+
.boff-explore-video-panel:fullscreen .boff-explore-video-frame {
|
|
734
|
+
display: flex;
|
|
735
|
+
flex: 1 1 auto;
|
|
736
|
+
min-height: 0;
|
|
737
|
+
align-items: center;
|
|
738
|
+
justify-content: center;
|
|
739
|
+
border: 0;
|
|
740
|
+
border-radius: 0;
|
|
741
|
+
box-shadow: none;
|
|
742
|
+
background-color: hsl(var(--background));
|
|
743
|
+
}
|
|
744
|
+
.boff-explore-video-panel:fullscreen video {
|
|
745
|
+
width: 100%;
|
|
746
|
+
height: 100%;
|
|
747
|
+
max-height: none;
|
|
748
|
+
}
|
|
749
|
+
`;
|
|
750
|
+
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";
|
|
751
|
+
function ExploreVideoDialog({
|
|
752
|
+
videoUrl,
|
|
753
|
+
posterUrl,
|
|
754
|
+
title,
|
|
755
|
+
strings: T,
|
|
756
|
+
dir,
|
|
757
|
+
onClose
|
|
758
|
+
}) {
|
|
759
|
+
const dialogRef = useRef(null);
|
|
760
|
+
const panelRef = useRef(null);
|
|
761
|
+
const videoRef = useRef(null);
|
|
762
|
+
const closeRef = useRef(null);
|
|
763
|
+
const nativeModalRef = useRef(false);
|
|
764
|
+
const titleId = useId();
|
|
765
|
+
const [support] = useState(fullscreenSupport);
|
|
766
|
+
const [fullscreen, setFullscreen] = useState(false);
|
|
767
|
+
const [failed, setFailed] = useState(false);
|
|
768
|
+
useEffect(() => {
|
|
769
|
+
const dialog = dialogRef.current;
|
|
770
|
+
if (!dialog) return;
|
|
771
|
+
if (typeof dialog.showModal === "function") {
|
|
772
|
+
if (!dialog.open) dialog.showModal();
|
|
773
|
+
nativeModalRef.current = true;
|
|
774
|
+
} else {
|
|
775
|
+
dialog.setAttribute("open", "");
|
|
776
|
+
nativeModalRef.current = false;
|
|
777
|
+
}
|
|
778
|
+
closeRef.current?.focus();
|
|
779
|
+
ignoreRejection(videoRef.current?.play());
|
|
780
|
+
return () => {
|
|
781
|
+
if (dialog.open && typeof dialog.close === "function") dialog.close();
|
|
782
|
+
};
|
|
783
|
+
}, []);
|
|
784
|
+
useEffect(() => {
|
|
785
|
+
const sync = () => {
|
|
786
|
+
const current = document.fullscreenElement;
|
|
787
|
+
setFullscreen(
|
|
788
|
+
current !== null && dialogRef.current?.contains(current) === true
|
|
789
|
+
);
|
|
790
|
+
};
|
|
791
|
+
const enter = () => setFullscreen(true);
|
|
792
|
+
const leave = () => setFullscreen(false);
|
|
793
|
+
const video = videoRef.current;
|
|
794
|
+
document.addEventListener("fullscreenchange", sync);
|
|
795
|
+
video?.addEventListener("webkitbeginfullscreen", enter);
|
|
796
|
+
video?.addEventListener("webkitendfullscreen", leave);
|
|
797
|
+
return () => {
|
|
798
|
+
document.removeEventListener("fullscreenchange", sync);
|
|
799
|
+
video?.removeEventListener("webkitbeginfullscreen", enter);
|
|
800
|
+
video?.removeEventListener("webkitendfullscreen", leave);
|
|
801
|
+
};
|
|
802
|
+
}, [failed]);
|
|
803
|
+
const leaveFullscreen = useCallback(() => {
|
|
804
|
+
const current = document.fullscreenElement;
|
|
805
|
+
if (current && dialogRef.current?.contains(current) && typeof document.exitFullscreen === "function") {
|
|
806
|
+
ignoreRejection(document.exitFullscreen());
|
|
807
|
+
}
|
|
808
|
+
const video = videoRef.current;
|
|
809
|
+
if (video?.webkitDisplayingFullscreen) video.webkitExitFullscreen?.();
|
|
810
|
+
}, []);
|
|
811
|
+
const requestClose = useCallback(() => {
|
|
812
|
+
videoRef.current?.pause();
|
|
813
|
+
leaveFullscreen();
|
|
814
|
+
onClose();
|
|
815
|
+
}, [leaveFullscreen, onClose]);
|
|
816
|
+
const toggleFullscreen = useCallback(() => {
|
|
817
|
+
if (fullscreen) {
|
|
818
|
+
leaveFullscreen();
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
if (support === "element") {
|
|
822
|
+
const panel = panelRef.current;
|
|
823
|
+
if (panel) ignoreRejection(panel.requestFullscreen());
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const video = videoRef.current;
|
|
827
|
+
video?.webkitEnterFullscreen?.();
|
|
828
|
+
}, [fullscreen, leaveFullscreen, support]);
|
|
829
|
+
const handleClose = useCallback(
|
|
830
|
+
(event) => {
|
|
831
|
+
if (event.currentTarget.open) return;
|
|
832
|
+
requestClose();
|
|
833
|
+
},
|
|
834
|
+
[requestClose]
|
|
835
|
+
);
|
|
836
|
+
const handleKeyDown = useCallback(
|
|
837
|
+
(event) => {
|
|
838
|
+
if (event.key === "Escape" && !nativeModalRef.current) {
|
|
839
|
+
event.preventDefault();
|
|
840
|
+
requestClose();
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
[requestClose]
|
|
844
|
+
);
|
|
845
|
+
const handleStageClick = useCallback(
|
|
846
|
+
(event) => {
|
|
847
|
+
if (event.target === event.currentTarget) requestClose();
|
|
848
|
+
},
|
|
849
|
+
[requestClose]
|
|
850
|
+
);
|
|
851
|
+
if (typeof document === "undefined") return null;
|
|
852
|
+
const fullscreenLabel = fullscreen ? T.exitFullScreen : T.enterFullScreen;
|
|
853
|
+
return createPortal(
|
|
854
|
+
/* @__PURE__ */ jsxs(
|
|
855
|
+
"dialog",
|
|
856
|
+
{
|
|
857
|
+
ref: dialogRef,
|
|
858
|
+
"data-boff-explore": "video-dialog",
|
|
859
|
+
"aria-labelledby": titleId,
|
|
860
|
+
dir,
|
|
861
|
+
onClose: handleClose,
|
|
862
|
+
onKeyDown: handleKeyDown,
|
|
863
|
+
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",
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: VIDEO_DIALOG_CSS } }),
|
|
866
|
+
/* @__PURE__ */ jsx(
|
|
867
|
+
"div",
|
|
868
|
+
{
|
|
869
|
+
onClick: handleStageClick,
|
|
870
|
+
className: "flex min-h-full items-center justify-center p-4 sm:p-6",
|
|
871
|
+
children: /* @__PURE__ */ jsxs(
|
|
872
|
+
"div",
|
|
873
|
+
{
|
|
874
|
+
ref: panelRef,
|
|
875
|
+
className: "boff-explore-video-panel flex w-full min-w-0 max-w-[960px] flex-col gap-3",
|
|
876
|
+
children: [
|
|
877
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
878
|
+
/* @__PURE__ */ jsx(
|
|
879
|
+
"h2",
|
|
880
|
+
{
|
|
881
|
+
id: titleId,
|
|
882
|
+
className: "min-w-0 flex-1 truncate text-sm font-semibold text-foreground sm:text-base",
|
|
883
|
+
children: title
|
|
884
|
+
}
|
|
885
|
+
),
|
|
886
|
+
support !== null && !failed ? (
|
|
887
|
+
// A plain button whose name says what pressing it does NOW, and no
|
|
888
|
+
// `aria-pressed`: a toggle button's name must not change with its state, or it
|
|
889
|
+
// announces "Exit full screen, pressed" — two answers to one question.
|
|
890
|
+
/* @__PURE__ */ jsx(
|
|
891
|
+
"button",
|
|
892
|
+
{
|
|
893
|
+
type: "button",
|
|
894
|
+
"data-boff-explore": "video-fullscreen",
|
|
895
|
+
"aria-label": fullscreenLabel,
|
|
896
|
+
title: fullscreenLabel,
|
|
897
|
+
onClick: toggleFullscreen,
|
|
898
|
+
className: DIALOG_BUTTON,
|
|
899
|
+
children: fullscreen ? /* @__PURE__ */ jsx(Minimize, { "aria-hidden": "true", className: "h-5 w-5" }) : /* @__PURE__ */ jsx(Maximize, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
900
|
+
}
|
|
901
|
+
)
|
|
902
|
+
) : null,
|
|
903
|
+
/* @__PURE__ */ jsx(
|
|
904
|
+
"button",
|
|
905
|
+
{
|
|
906
|
+
ref: closeRef,
|
|
907
|
+
type: "button",
|
|
908
|
+
"data-boff-explore": "video-close",
|
|
909
|
+
"aria-label": T.closeVideo,
|
|
910
|
+
title: T.closeVideo,
|
|
911
|
+
onClick: requestClose,
|
|
912
|
+
className: DIALOG_BUTTON,
|
|
913
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
914
|
+
}
|
|
915
|
+
)
|
|
916
|
+
] }),
|
|
917
|
+
/* @__PURE__ */ jsx("div", { className: "boff-explore-video-frame overflow-hidden rounded-xl border border-border bg-muted shadow-lg", children: failed ? (
|
|
918
|
+
// A black box with a spinning control tells the visitor nothing; this says
|
|
919
|
+
// what happened, in their language.
|
|
920
|
+
/* @__PURE__ */ jsxs(
|
|
921
|
+
"div",
|
|
922
|
+
{
|
|
923
|
+
role: "alert",
|
|
924
|
+
className: "flex aspect-video w-full flex-col items-center justify-center gap-2 p-6 text-center text-sm text-muted-foreground",
|
|
925
|
+
children: [
|
|
926
|
+
/* @__PURE__ */ jsx(VideoOff, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
927
|
+
/* @__PURE__ */ jsx("p", { children: T.videoUnavailable })
|
|
928
|
+
]
|
|
929
|
+
}
|
|
930
|
+
)
|
|
931
|
+
) : /* @__PURE__ */ jsx(
|
|
932
|
+
"video",
|
|
933
|
+
{
|
|
934
|
+
ref: videoRef,
|
|
935
|
+
"data-boff-explore": "video-player",
|
|
936
|
+
src: videoUrl,
|
|
937
|
+
poster: posterUrl,
|
|
938
|
+
controls: true,
|
|
939
|
+
controlsList: support === "element" ? "nofullscreen" : void 0,
|
|
940
|
+
playsInline: true,
|
|
941
|
+
autoPlay: true,
|
|
942
|
+
preload: "metadata",
|
|
943
|
+
onError: () => setFailed(true),
|
|
944
|
+
className: "block max-h-[80vh] w-full bg-muted object-contain"
|
|
945
|
+
}
|
|
946
|
+
) })
|
|
947
|
+
]
|
|
948
|
+
}
|
|
949
|
+
)
|
|
950
|
+
}
|
|
951
|
+
)
|
|
952
|
+
]
|
|
953
|
+
}
|
|
954
|
+
),
|
|
955
|
+
document.body
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
function MarkdownLink({
|
|
959
|
+
href,
|
|
960
|
+
children,
|
|
961
|
+
// react-markdown passes the hast node to every override; it is not a DOM attribute.
|
|
962
|
+
node: _node,
|
|
963
|
+
...props
|
|
964
|
+
}) {
|
|
965
|
+
const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
|
|
966
|
+
return /* @__PURE__ */ jsx(
|
|
967
|
+
"a",
|
|
968
|
+
{
|
|
969
|
+
href,
|
|
970
|
+
...props,
|
|
971
|
+
...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
|
|
972
|
+
children
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
var defaultComponents = {
|
|
977
|
+
a: MarkdownLink
|
|
978
|
+
};
|
|
979
|
+
function Markdown({
|
|
980
|
+
children,
|
|
981
|
+
className,
|
|
982
|
+
inline,
|
|
983
|
+
components,
|
|
984
|
+
rehypePlugins
|
|
985
|
+
}) {
|
|
986
|
+
const content = /* @__PURE__ */ jsx(
|
|
987
|
+
ReactMarkdown,
|
|
988
|
+
{
|
|
989
|
+
remarkPlugins: [remarkGfm],
|
|
990
|
+
rehypePlugins: [rehypeSanitize, ...rehypePlugins ?? []],
|
|
991
|
+
components: { ...defaultComponents, ...components },
|
|
992
|
+
...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
|
|
993
|
+
children
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
if (inline) {
|
|
997
|
+
return /* @__PURE__ */ jsx("span", { className, children: content });
|
|
998
|
+
}
|
|
999
|
+
return /* @__PURE__ */ jsx("div", { className, children: content });
|
|
1000
|
+
}
|
|
1001
|
+
function cn(...inputs) {
|
|
1002
|
+
return twMerge(clsx(inputs));
|
|
1003
|
+
}
|
|
1004
|
+
var EXPLORE_ANSWER_CSS = `
|
|
1005
|
+
/* Nothing inside an answer may widen the page. Model output carries long URLs, code and
|
|
1006
|
+
tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
|
|
1007
|
+
WHOLE document horizontally scrollable, not just the bubble. */
|
|
1008
|
+
.boff-explore-body,
|
|
1009
|
+
.boff-explore-body p,
|
|
1010
|
+
.boff-explore-body li,
|
|
1011
|
+
.boff-explore-body a,
|
|
1012
|
+
.boff-explore-body h1,
|
|
1013
|
+
.boff-explore-body h2,
|
|
1014
|
+
.boff-explore-body h3,
|
|
1015
|
+
/* h4-h6 as well: a published answer page pushes the model's own headings down a level or
|
|
1016
|
+
two so they nest under the page's, and a long unbroken word is no narrower for it. */
|
|
1017
|
+
.boff-explore-body h4,
|
|
1018
|
+
.boff-explore-body h5,
|
|
1019
|
+
.boff-explore-body h6 {
|
|
1020
|
+
overflow-wrap: anywhere;
|
|
1021
|
+
word-break: break-word;
|
|
1022
|
+
}
|
|
1023
|
+
.boff-explore-body pre {
|
|
1024
|
+
overflow-x: auto;
|
|
1025
|
+
max-width: 100%;
|
|
1026
|
+
}
|
|
1027
|
+
.boff-explore-body table {
|
|
1028
|
+
display: block;
|
|
1029
|
+
overflow-x: auto;
|
|
1030
|
+
max-width: 100%;
|
|
1031
|
+
}
|
|
1032
|
+
.boff-explore-body img {
|
|
1033
|
+
max-width: 100%;
|
|
1034
|
+
height: auto;
|
|
1035
|
+
}
|
|
1036
|
+
/* A product-video poster fills the card's reserved 16:9 frame and is cropped to it. The
|
|
1037
|
+
blanket \`img { height: auto }\` above \u2014 and ProseStyles' own \u2014 outrank a height utility,
|
|
1038
|
+
and would size it by its own aspect ratio instead, leaving a band of the frame showing
|
|
1039
|
+
whenever a poster is not exactly 16:9. */
|
|
1040
|
+
.boff-explore-body .boff-explore-video-poster {
|
|
1041
|
+
height: 100%;
|
|
1042
|
+
max-width: none;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/* ---- Comparison and pricing tables ----------------------------------------------------
|
|
1046
|
+
The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
|
|
1047
|
+
the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
|
|
1048
|
+
the page must not scroll sideways because of it, and it has to be readable on a 320px
|
|
1049
|
+
phone, which a four-column grid of 1px borders is not.
|
|
1050
|
+
|
|
1051
|
+
Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
|
|
1052
|
+
\`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
|
|
1053
|
+
cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
|
|
1054
|
+
of table from chaining to the page \u2014 the same rule the transcript needed. */
|
|
1055
|
+
.boff-explore-body .boff-explore-table {
|
|
1056
|
+
max-width: 100%;
|
|
1057
|
+
overflow-x: auto;
|
|
1058
|
+
overscroll-behavior-x: contain;
|
|
1059
|
+
margin: 0.75rem 0;
|
|
1060
|
+
border: 1px solid hsl(var(--border));
|
|
1061
|
+
border-radius: 0.75rem;
|
|
1062
|
+
background-color: hsl(var(--card));
|
|
1063
|
+
}
|
|
1064
|
+
.boff-explore-body .boff-explore-table:focus-visible {
|
|
1065
|
+
outline: 2px solid hsl(var(--ring));
|
|
1066
|
+
outline-offset: 2px;
|
|
1067
|
+
}
|
|
1068
|
+
.boff-explore-body .boff-explore-table table {
|
|
1069
|
+
/* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
|
|
1070
|
+
display: table;
|
|
1071
|
+
width: 100%;
|
|
1072
|
+
max-width: none;
|
|
1073
|
+
margin: 0;
|
|
1074
|
+
border-collapse: collapse;
|
|
1075
|
+
font-size: 0.8125rem;
|
|
1076
|
+
}
|
|
1077
|
+
.boff-explore-body .boff-explore-table th,
|
|
1078
|
+
.boff-explore-body .boff-explore-table td {
|
|
1079
|
+
border: 0;
|
|
1080
|
+
border-top: 1px solid hsl(var(--border));
|
|
1081
|
+
padding: 0.5rem 0.75rem;
|
|
1082
|
+
/* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
|
|
1083
|
+
table there reads against the grain of every other line on the page. */
|
|
1084
|
+
text-align: start;
|
|
1085
|
+
vertical-align: top;
|
|
1086
|
+
overflow-wrap: anywhere;
|
|
1087
|
+
/* Prices in a column line up under each other instead of drifting by digit width. */
|
|
1088
|
+
font-variant-numeric: tabular-nums;
|
|
1089
|
+
min-width: 6rem;
|
|
1090
|
+
}
|
|
1091
|
+
.boff-explore-body .boff-explore-table thead th {
|
|
1092
|
+
border-top: 0;
|
|
1093
|
+
background-color: hsl(var(--muted));
|
|
1094
|
+
color: hsl(var(--foreground));
|
|
1095
|
+
font-weight: 600;
|
|
1096
|
+
white-space: nowrap;
|
|
1097
|
+
}
|
|
1098
|
+
/* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
|
|
1099
|
+
the row's heading is what makes a price list scannable rather than a grid of strings. */
|
|
1100
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
1101
|
+
font-weight: 600;
|
|
1102
|
+
color: hsl(var(--card-foreground));
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/* Narrow: one card per row. Each cell prints its own column heading, so a value is never
|
|
1106
|
+
stranded without the question it answers, and the subject cell becomes the card's title.
|
|
1107
|
+
A media query rather than a measured width \u2014 these pages are serialised post-hydration,
|
|
1108
|
+
so anything measured here would ship to every phone as static HTML. */
|
|
1109
|
+
@media (max-width: 30rem) {
|
|
1110
|
+
.boff-explore-body .boff-explore-table {
|
|
1111
|
+
overflow-x: visible;
|
|
1112
|
+
}
|
|
1113
|
+
.boff-explore-body .boff-explore-table table,
|
|
1114
|
+
.boff-explore-body .boff-explore-table tbody,
|
|
1115
|
+
.boff-explore-body .boff-explore-table tr,
|
|
1116
|
+
.boff-explore-body .boff-explore-table td {
|
|
1117
|
+
display: block;
|
|
1118
|
+
width: 100%;
|
|
1119
|
+
min-width: 0;
|
|
1120
|
+
}
|
|
1121
|
+
.boff-explore-body .boff-explore-table thead {
|
|
1122
|
+
display: none;
|
|
1123
|
+
}
|
|
1124
|
+
.boff-explore-body .boff-explore-table tr {
|
|
1125
|
+
border-top: 1px solid hsl(var(--border));
|
|
1126
|
+
padding: 0.5rem 0;
|
|
1127
|
+
}
|
|
1128
|
+
.boff-explore-body .boff-explore-table tr:first-child {
|
|
1129
|
+
border-top: 0;
|
|
1130
|
+
}
|
|
1131
|
+
.boff-explore-body .boff-explore-table td {
|
|
1132
|
+
border: 0;
|
|
1133
|
+
padding: 0.125rem 0.75rem;
|
|
1134
|
+
}
|
|
1135
|
+
.boff-explore-body .boff-explore-table td[data-label]::before {
|
|
1136
|
+
content: attr(data-label);
|
|
1137
|
+
display: block;
|
|
1138
|
+
font-size: 0.6875rem;
|
|
1139
|
+
font-weight: 600;
|
|
1140
|
+
text-transform: uppercase;
|
|
1141
|
+
letter-spacing: 0.04em;
|
|
1142
|
+
color: hsl(var(--muted-foreground));
|
|
1143
|
+
}
|
|
1144
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
1145
|
+
font-size: 0.9375rem;
|
|
1146
|
+
padding-bottom: 0.25rem;
|
|
1147
|
+
}
|
|
1148
|
+
.boff-explore-body .boff-explore-table tbody td:first-child::before {
|
|
1149
|
+
/* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
|
|
1150
|
+
display: none;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
`;
|
|
1154
|
+
function ExploreAnswerStyles() {
|
|
1155
|
+
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_ANSWER_CSS } });
|
|
1156
|
+
}
|
|
1157
|
+
function inlineImageOrigins(products) {
|
|
1158
|
+
const origins = /* @__PURE__ */ new Set();
|
|
1159
|
+
for (const product of products) {
|
|
1160
|
+
let url;
|
|
1161
|
+
try {
|
|
1162
|
+
url = new URL(product.website);
|
|
1163
|
+
} catch {
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
if (url.protocol !== "https:") continue;
|
|
1167
|
+
const apex = url.hostname.replace(/^www\./, "");
|
|
1168
|
+
const port = url.port ? `:${url.port}` : "";
|
|
1169
|
+
origins.add(`https://${apex}${port}`);
|
|
1170
|
+
origins.add(`https://www.${apex}${port}`);
|
|
1171
|
+
}
|
|
1172
|
+
return origins;
|
|
1173
|
+
}
|
|
1174
|
+
var VIDEO_FILE_PATH = /\.(?:mp4|webm|m4v|mov|vtt)$/i;
|
|
1175
|
+
function catalogUrlOf(value, origins) {
|
|
1176
|
+
if (typeof value !== "string" || value === "") return null;
|
|
1177
|
+
let url;
|
|
1178
|
+
try {
|
|
1179
|
+
url = new URL(value);
|
|
1180
|
+
} catch {
|
|
1181
|
+
return null;
|
|
1182
|
+
}
|
|
1183
|
+
if (url.protocol !== "https:" || url.username || url.password) return null;
|
|
1184
|
+
return origins.has(url.origin) ? url : null;
|
|
1185
|
+
}
|
|
1186
|
+
function isInlineImageAllowed(src, origins) {
|
|
1187
|
+
const url = catalogUrlOf(src, origins);
|
|
1188
|
+
return url !== null && !VIDEO_FILE_PATH.test(url.pathname);
|
|
1189
|
+
}
|
|
1190
|
+
var INLINE_VIDEO_PATH = /\.(?:mp4|webm)$/i;
|
|
1191
|
+
function isInlineVideoAllowed(href, origins) {
|
|
1192
|
+
const url = catalogUrlOf(href, origins);
|
|
1193
|
+
return url !== null && INLINE_VIDEO_PATH.test(url.pathname);
|
|
1194
|
+
}
|
|
1195
|
+
function apexOriginOf(url) {
|
|
1196
|
+
const apex = url.hostname.replace(/^www\./, "");
|
|
1197
|
+
const port = url.port ? `:${url.port}` : "";
|
|
1198
|
+
return `https://${apex}${port}`;
|
|
1199
|
+
}
|
|
1200
|
+
function productSlugsByOrigin(products) {
|
|
1201
|
+
const slugs = /* @__PURE__ */ new Map();
|
|
1202
|
+
for (const product of products) {
|
|
1203
|
+
let url;
|
|
1204
|
+
try {
|
|
1205
|
+
url = new URL(product.website);
|
|
1206
|
+
} catch {
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
if (url.protocol !== "https:") continue;
|
|
1210
|
+
const key = apexOriginOf(url);
|
|
1211
|
+
if (!slugs.has(key)) slugs.set(key, product.slug);
|
|
1212
|
+
}
|
|
1213
|
+
return slugs;
|
|
1214
|
+
}
|
|
1215
|
+
function videoProductOf(href, slugs) {
|
|
1216
|
+
const url = new URL(href);
|
|
1217
|
+
return slugs.get(apexOriginOf(url)) ?? url.hostname.replace(/^www\./, "");
|
|
1218
|
+
}
|
|
1219
|
+
function isVideoFileTarget(href) {
|
|
1220
|
+
if (typeof href !== "string") return false;
|
|
1221
|
+
try {
|
|
1222
|
+
return VIDEO_FILE_PATH.test(
|
|
1223
|
+
new URL(href, "https://answer.invalid").pathname
|
|
1224
|
+
);
|
|
1225
|
+
} catch {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
var CODE_FENCE = /^\s*(?:```|~~~)/;
|
|
1230
|
+
var TABLE_ROW_LINE = /^\s{0,3}\|/;
|
|
1231
|
+
var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
|
|
1232
|
+
var LINKED_IMAGE = /\[!\[([^[\]]*)\]\([^)\s]*(?:\s+"[^"]*")?\)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
1233
|
+
var IMAGE = /!\[[^[\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g;
|
|
1234
|
+
var LINK = /\[([^[\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
1235
|
+
function readableLine(raw, mode) {
|
|
1236
|
+
let line = raw;
|
|
1237
|
+
if (line.includes("](")) {
|
|
1238
|
+
line = line.replace(LINKED_IMAGE, (_match, alt, href) => {
|
|
1239
|
+
if (mode === "speech") return "";
|
|
1240
|
+
return alt.trim() === "" ? href : `${alt.trim()} (${href})`;
|
|
1241
|
+
});
|
|
1242
|
+
line = line.replace(IMAGE, "");
|
|
1243
|
+
line = mode === "speech" ? (
|
|
1244
|
+
// A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
|
|
1245
|
+
// prompt asks for two to five links in every answer. The label IS the words.
|
|
1246
|
+
line.replace(LINK, "$1")
|
|
1247
|
+
) : line.replace(LINK, "$1 ($2)");
|
|
1248
|
+
}
|
|
1249
|
+
line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
|
|
1250
|
+
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
1251
|
+
line = line.replace(/^\s{0,3}>\s?/, "");
|
|
1252
|
+
if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
|
|
1253
|
+
line = line.replace(/`([^`]+)`/g, "$1");
|
|
1254
|
+
line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
1255
|
+
line = line.replace(/__([^_]+)__/g, "$1");
|
|
1256
|
+
line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
|
|
1257
|
+
line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
|
|
1258
|
+
return line.replace(/[ \t]+$/, "");
|
|
1259
|
+
}
|
|
1260
|
+
function tableCells(row) {
|
|
1261
|
+
const trimmed = row.trim();
|
|
1262
|
+
const cells = [];
|
|
1263
|
+
let cell = "";
|
|
1264
|
+
for (let at = 0; at < trimmed.length; at += 1) {
|
|
1265
|
+
const char = trimmed[at] ?? "";
|
|
1266
|
+
if (char === "\\") {
|
|
1267
|
+
cell += char + (trimmed[at + 1] ?? "");
|
|
1268
|
+
at += 1;
|
|
1269
|
+
continue;
|
|
1270
|
+
}
|
|
1271
|
+
if (char === "|") {
|
|
1272
|
+
cells.push(cell.trim());
|
|
1273
|
+
cell = "";
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
cell += char;
|
|
1277
|
+
}
|
|
1278
|
+
cells.push(cell.trim());
|
|
1279
|
+
if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
|
|
1280
|
+
if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
|
|
1281
|
+
return cells;
|
|
1282
|
+
}
|
|
1283
|
+
function asSentence(text) {
|
|
1284
|
+
return /[.!?]$/.test(text) ? text : `${text}.`;
|
|
1285
|
+
}
|
|
1286
|
+
function spokenTable(rows) {
|
|
1287
|
+
if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
|
|
1288
|
+
const labels = tableCells(rows[0] ?? "");
|
|
1289
|
+
const spoken = [];
|
|
1290
|
+
for (const row of rows.slice(2)) {
|
|
1291
|
+
const parts = [];
|
|
1292
|
+
tableCells(row).forEach((value, column) => {
|
|
1293
|
+
if (value === "") return;
|
|
1294
|
+
const label = column === 0 ? "" : labels[column] ?? "";
|
|
1295
|
+
parts.push(label === "" ? value : `${label}: ${value}`);
|
|
1296
|
+
});
|
|
1297
|
+
if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
|
|
1298
|
+
}
|
|
1299
|
+
return spoken;
|
|
1300
|
+
}
|
|
1301
|
+
function readableText(markdown, mode) {
|
|
1302
|
+
const lines = [];
|
|
1303
|
+
let inFence = false;
|
|
1304
|
+
let table = [];
|
|
1305
|
+
const flushTable = () => {
|
|
1306
|
+
if (table.length === 0) return;
|
|
1307
|
+
for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
|
|
1308
|
+
table = [];
|
|
1309
|
+
};
|
|
1310
|
+
for (const raw of markdown.split("\n")) {
|
|
1311
|
+
if (CODE_FENCE.test(raw)) {
|
|
1312
|
+
flushTable();
|
|
1313
|
+
inFence = !inFence;
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
if (inFence) {
|
|
1317
|
+
lines.push(raw);
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
|
|
1321
|
+
table.push(raw);
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1324
|
+
flushTable();
|
|
1325
|
+
lines.push(readableLine(raw, mode));
|
|
1326
|
+
}
|
|
1327
|
+
flushTable();
|
|
1328
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
1329
|
+
}
|
|
1330
|
+
function answerPlainText(markdown) {
|
|
1331
|
+
return readableText(markdown, "clipboard");
|
|
1332
|
+
}
|
|
1333
|
+
function answerSpeechText(markdown) {
|
|
1334
|
+
return readableText(markdown, "speech");
|
|
1335
|
+
}
|
|
1336
|
+
var AnswerScopeContext = createContext(null);
|
|
1337
|
+
var InsideLinkContext = createContext(false);
|
|
1338
|
+
function AnswerLink(props) {
|
|
1339
|
+
const scope = useContext(AnswerScopeContext);
|
|
1340
|
+
const role = videoRoleOf(props.node);
|
|
1341
|
+
if (role !== void 0 && scope !== null) {
|
|
1342
|
+
const video = role === "card" && scope.enabled ? productVideoOf(props.node) : null;
|
|
1343
|
+
if (video === null) return null;
|
|
1344
|
+
return /* @__PURE__ */ jsx(
|
|
1345
|
+
InlineVideoCard,
|
|
1346
|
+
{
|
|
1347
|
+
href: video.href,
|
|
1348
|
+
poster: video.poster,
|
|
1349
|
+
alt: video.alt,
|
|
1350
|
+
scope
|
|
1351
|
+
}
|
|
1352
|
+
);
|
|
1125
1353
|
}
|
|
1126
|
-
return
|
|
1354
|
+
return /* @__PURE__ */ jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx(MarkdownLink, { ...props }) });
|
|
1127
1355
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1356
|
+
var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
|
|
1357
|
+
function InlineConceptImage({ src, alt }) {
|
|
1358
|
+
const scope = useContext(AnswerScopeContext);
|
|
1359
|
+
const insideLink = useContext(InsideLinkContext);
|
|
1360
|
+
if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
|
|
1361
|
+
return null;
|
|
1362
|
+
}
|
|
1363
|
+
const { strings: T, onFailed } = scope;
|
|
1364
|
+
const picture = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1365
|
+
/* @__PURE__ */ jsx(
|
|
1366
|
+
"img",
|
|
1367
|
+
{
|
|
1368
|
+
src,
|
|
1369
|
+
alt: alt ?? "",
|
|
1370
|
+
loading: "lazy",
|
|
1371
|
+
decoding: "async",
|
|
1372
|
+
onError: () => onFailed(src),
|
|
1373
|
+
className: "block h-auto max-h-80 w-auto max-w-full object-contain"
|
|
1374
|
+
}
|
|
1375
|
+
),
|
|
1376
|
+
/* @__PURE__ */ jsx(
|
|
1377
|
+
"span",
|
|
1378
|
+
{
|
|
1379
|
+
"aria-hidden": "true",
|
|
1380
|
+
className: "pointer-events-none absolute left-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",
|
|
1381
|
+
children: T.conceptIllustration
|
|
1382
|
+
}
|
|
1383
|
+
)
|
|
1384
|
+
] });
|
|
1385
|
+
return /* @__PURE__ */ jsx("span", { "data-boff-explore": "inline-image", className: "my-3 block", children: insideLink ? /* @__PURE__ */ jsx("span", { className: INLINE_IMAGE_FRAME, children: picture }) : /* @__PURE__ */ jsxs(
|
|
1386
|
+
"a",
|
|
1387
|
+
{
|
|
1388
|
+
href: src,
|
|
1389
|
+
target: "_blank",
|
|
1390
|
+
rel: "noopener noreferrer",
|
|
1391
|
+
className: cn(
|
|
1392
|
+
INLINE_IMAGE_FRAME,
|
|
1393
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1394
|
+
),
|
|
1395
|
+
children: [
|
|
1396
|
+
picture,
|
|
1397
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
1398
|
+
"(",
|
|
1399
|
+
T.opensInNewTab,
|
|
1400
|
+
")"
|
|
1401
|
+
] })
|
|
1402
|
+
]
|
|
1403
|
+
}
|
|
1404
|
+
) });
|
|
1130
1405
|
}
|
|
1131
|
-
function
|
|
1132
|
-
|
|
1406
|
+
function markdownText(node) {
|
|
1407
|
+
if (!node) return "";
|
|
1408
|
+
if (typeof node.value === "string") return node.value;
|
|
1409
|
+
return (node.children ?? []).map(markdownText).join("");
|
|
1410
|
+
}
|
|
1411
|
+
function hasHeaderCells(node) {
|
|
1412
|
+
return (node?.children ?? []).some((cell) => cell.tagName === "th");
|
|
1413
|
+
}
|
|
1414
|
+
function findHeaderRow(node) {
|
|
1415
|
+
for (const child of node?.children ?? []) {
|
|
1416
|
+
if (child.tagName === "tr" && hasHeaderCells(child)) return child;
|
|
1417
|
+
const nested = findHeaderRow(child);
|
|
1418
|
+
if (nested !== null) return nested;
|
|
1419
|
+
}
|
|
1420
|
+
return null;
|
|
1421
|
+
}
|
|
1422
|
+
function tableColumnLabels(node) {
|
|
1423
|
+
const row = findHeaderRow(node);
|
|
1424
|
+
if (row === null) return [];
|
|
1425
|
+
return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
|
|
1426
|
+
}
|
|
1427
|
+
var TableColumnsContext = createContext([]);
|
|
1428
|
+
function AnswerTable({ node, children }) {
|
|
1429
|
+
const scope = useContext(AnswerScopeContext);
|
|
1430
|
+
const table = /* @__PURE__ */ jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsx("table", { children }) });
|
|
1431
|
+
if (!scope) return table;
|
|
1432
|
+
return /* @__PURE__ */ jsx(
|
|
1433
|
+
"div",
|
|
1434
|
+
{
|
|
1435
|
+
"data-boff-explore": "table",
|
|
1436
|
+
role: "region",
|
|
1437
|
+
"aria-label": scope.strings.answerTable,
|
|
1438
|
+
tabIndex: 0,
|
|
1439
|
+
className: "boff-explore-table",
|
|
1440
|
+
children: table
|
|
1441
|
+
}
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
function AnswerTableRow({
|
|
1445
|
+
node,
|
|
1446
|
+
children,
|
|
1447
|
+
...props
|
|
1448
|
+
}) {
|
|
1449
|
+
const labels = useContext(TableColumnsContext);
|
|
1450
|
+
if (labels.length === 0 || hasHeaderCells(node)) {
|
|
1451
|
+
return /* @__PURE__ */ jsx("tr", { ...props, children });
|
|
1452
|
+
}
|
|
1453
|
+
let column = 0;
|
|
1454
|
+
const cells = Children.map(children, (cell) => {
|
|
1455
|
+
if (!isValidElement(cell)) return cell;
|
|
1456
|
+
const label = labels[column] ?? "";
|
|
1457
|
+
column += 1;
|
|
1458
|
+
if (label === "") return cell;
|
|
1459
|
+
return cloneElement(cell, {
|
|
1460
|
+
"data-label": label
|
|
1461
|
+
});
|
|
1462
|
+
});
|
|
1463
|
+
return /* @__PURE__ */ jsx("tr", { ...props, children: cells });
|
|
1464
|
+
}
|
|
1465
|
+
function linkedImageOf(node) {
|
|
1466
|
+
if (node?.tagName !== "a") return null;
|
|
1467
|
+
const content = (node.children ?? []).filter(
|
|
1468
|
+
(child) => child.tagName !== void 0 || (child.value ?? "").trim() !== ""
|
|
1469
|
+
);
|
|
1470
|
+
const image = content.length === 1 ? content[0] : void 0;
|
|
1471
|
+
if (image?.tagName !== "img") return null;
|
|
1472
|
+
const alt = image.properties?.alt;
|
|
1473
|
+
return {
|
|
1474
|
+
href: node.properties?.href,
|
|
1475
|
+
src: image.properties?.src,
|
|
1476
|
+
alt: typeof alt === "string" ? alt.trim() : ""
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
function isCardableVideo(embed, origins) {
|
|
1480
|
+
if (!isInlineVideoAllowed(embed.href, origins) || !isInlineImageAllowed(embed.src, origins)) {
|
|
1481
|
+
return false;
|
|
1482
|
+
}
|
|
1483
|
+
return apexOriginOf(new URL(embed.href)) === apexOriginOf(new URL(embed.src));
|
|
1484
|
+
}
|
|
1485
|
+
var NO_VIDEO_CONTAINERS = /* @__PURE__ */ new Set(["li", "table"]);
|
|
1486
|
+
function rehypeProductVideo(options) {
|
|
1487
|
+
return (tree) => {
|
|
1488
|
+
let claimed = false;
|
|
1489
|
+
const visit = (node, parent, contained) => {
|
|
1490
|
+
const embed = linkedImageOf(node);
|
|
1491
|
+
if (embed !== null && isVideoFileTarget(embed.href)) {
|
|
1492
|
+
const card = !claimed && !contained && parent?.tagName === "p" && isCardableVideo(embed, options.origins);
|
|
1493
|
+
if (card) claimed = true;
|
|
1494
|
+
const data = typeof node.data === "object" ? node.data : null;
|
|
1495
|
+
node.data = { ...data, boffVideo: card ? "card" : "drop" };
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
const inside = contained || node.tagName !== void 0 && NO_VIDEO_CONTAINERS.has(node.tagName);
|
|
1499
|
+
for (const child of node.children ?? []) visit(child, node, inside);
|
|
1500
|
+
};
|
|
1501
|
+
visit(tree, null, false);
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
function videoRoleOf(node) {
|
|
1505
|
+
const data = node?.data;
|
|
1506
|
+
if (typeof data !== "object" || data === null || !("boffVideo" in data)) {
|
|
1507
|
+
return void 0;
|
|
1508
|
+
}
|
|
1509
|
+
return data.boffVideo === "card" || data.boffVideo === "drop" ? data.boffVideo : void 0;
|
|
1510
|
+
}
|
|
1511
|
+
function productVideoOf(node) {
|
|
1512
|
+
const embed = linkedImageOf(node);
|
|
1513
|
+
if (embed === null || typeof embed.href !== "string" || typeof embed.src !== "string") {
|
|
1514
|
+
return null;
|
|
1515
|
+
}
|
|
1516
|
+
return { href: embed.href, poster: embed.src, alt: embed.alt };
|
|
1517
|
+
}
|
|
1518
|
+
var CANONICAL_VIDEO_ALT = /^(.+?)\s+product video$/i;
|
|
1519
|
+
function videoTitleOf(alt, T) {
|
|
1520
|
+
const product = CANONICAL_VIDEO_ALT.exec(alt)?.[1]?.trim();
|
|
1521
|
+
if (product) return fill(T.productVideoTitle, { product });
|
|
1522
|
+
return alt === "" ? T.productVideo : alt;
|
|
1523
|
+
}
|
|
1524
|
+
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";
|
|
1525
|
+
function InlineVideoCard({
|
|
1526
|
+
href,
|
|
1527
|
+
poster,
|
|
1528
|
+
alt,
|
|
1529
|
+
scope
|
|
1530
|
+
}) {
|
|
1531
|
+
const { strings: T, failed, onFailed, productSlugs } = scope;
|
|
1532
|
+
const [dialogDir, setDialogDir] = useState(null);
|
|
1533
|
+
const buttonRef = useRef(null);
|
|
1534
|
+
const returnFocusRef = useRef(false);
|
|
1535
|
+
const open = dialogDir !== null;
|
|
1536
|
+
useEffect(() => {
|
|
1537
|
+
if (open || !returnFocusRef.current) return;
|
|
1538
|
+
returnFocusRef.current = false;
|
|
1539
|
+
const button = buttonRef.current;
|
|
1540
|
+
if (button?.isConnected) button.focus();
|
|
1541
|
+
}, [open]);
|
|
1542
|
+
const openPlayer = useCallback((event) => {
|
|
1543
|
+
const dir = event.currentTarget.closest("[dir]")?.getAttribute("dir");
|
|
1544
|
+
returnFocusRef.current = true;
|
|
1545
|
+
setDialogDir(dir === "rtl" ? "rtl" : "ltr");
|
|
1546
|
+
}, []);
|
|
1547
|
+
const closePlayer = useCallback(() => setDialogDir(null), []);
|
|
1548
|
+
const name = videoTitleOf(alt, T);
|
|
1549
|
+
const posterFailed = failed.has(poster);
|
|
1550
|
+
return /* @__PURE__ */ jsxs(
|
|
1551
|
+
"span",
|
|
1552
|
+
{
|
|
1553
|
+
"data-boff-explore": "inline-video",
|
|
1554
|
+
"data-product": videoProductOf(href, productSlugs),
|
|
1555
|
+
className: "my-3 block",
|
|
1556
|
+
children: [
|
|
1557
|
+
/* @__PURE__ */ jsxs(
|
|
1558
|
+
"button",
|
|
1559
|
+
{
|
|
1560
|
+
ref: buttonRef,
|
|
1561
|
+
type: "button",
|
|
1562
|
+
"aria-haspopup": "dialog",
|
|
1563
|
+
"aria-label": fill(T.playVideo, { title: name }),
|
|
1564
|
+
onClick: openPlayer,
|
|
1565
|
+
className: INLINE_VIDEO_FRAME,
|
|
1566
|
+
children: [
|
|
1567
|
+
posterFailed ? /* @__PURE__ */ jsx(
|
|
1568
|
+
"span",
|
|
1569
|
+
{
|
|
1570
|
+
"aria-hidden": "true",
|
|
1571
|
+
className: "absolute inset-0 flex items-center justify-center bg-muted text-muted-foreground",
|
|
1572
|
+
children: /* @__PURE__ */ jsx(Film, { className: "h-10 w-10" })
|
|
1573
|
+
}
|
|
1574
|
+
) : /* @__PURE__ */ jsx(
|
|
1575
|
+
"img",
|
|
1576
|
+
{
|
|
1577
|
+
src: poster,
|
|
1578
|
+
alt: "",
|
|
1579
|
+
loading: "lazy",
|
|
1580
|
+
decoding: "async",
|
|
1581
|
+
onError: () => onFailed(poster),
|
|
1582
|
+
className: "boff-explore-video-poster absolute inset-0 h-full w-full object-cover"
|
|
1583
|
+
}
|
|
1584
|
+
),
|
|
1585
|
+
/* @__PURE__ */ jsx(
|
|
1586
|
+
"span",
|
|
1587
|
+
{
|
|
1588
|
+
"aria-hidden": "true",
|
|
1589
|
+
className: "pointer-events-none absolute inset-0 flex items-center justify-center",
|
|
1590
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex h-14 w-14 items-center justify-center rounded-full border border-border bg-background/90 text-foreground shadow-md transition-transform group-hover:scale-105", children: /* @__PURE__ */ jsx(Play, { className: "h-6 w-6 fill-current" }) })
|
|
1591
|
+
}
|
|
1592
|
+
),
|
|
1593
|
+
/* @__PURE__ */ jsx(
|
|
1594
|
+
"span",
|
|
1595
|
+
{
|
|
1596
|
+
"aria-hidden": "true",
|
|
1597
|
+
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",
|
|
1598
|
+
children: T.productVideo
|
|
1599
|
+
}
|
|
1600
|
+
)
|
|
1601
|
+
]
|
|
1602
|
+
}
|
|
1603
|
+
),
|
|
1604
|
+
open ? /* @__PURE__ */ jsx(
|
|
1605
|
+
ExploreVideoDialog,
|
|
1606
|
+
{
|
|
1607
|
+
videoUrl: href,
|
|
1608
|
+
posterUrl: poster,
|
|
1609
|
+
title: name,
|
|
1610
|
+
strings: T,
|
|
1611
|
+
dir: dialogDir,
|
|
1612
|
+
onClose: closePlayer
|
|
1613
|
+
}
|
|
1614
|
+
) : null
|
|
1615
|
+
]
|
|
1616
|
+
}
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
1620
|
+
a: AnswerLink,
|
|
1621
|
+
img: InlineConceptImage,
|
|
1622
|
+
table: AnswerTable,
|
|
1623
|
+
tr: AnswerTableRow
|
|
1624
|
+
};
|
|
1625
|
+
var NO_PRODUCT_SLUGS = /* @__PURE__ */ new Map();
|
|
1626
|
+
function ExploreAnswerBody({
|
|
1627
|
+
children,
|
|
1628
|
+
strings,
|
|
1629
|
+
imageOrigins,
|
|
1630
|
+
videoOrigins = imageOrigins,
|
|
1631
|
+
productSlugs = NO_PRODUCT_SLUGS,
|
|
1632
|
+
enabled = true,
|
|
1633
|
+
components,
|
|
1634
|
+
className
|
|
1635
|
+
}) {
|
|
1636
|
+
const [failed, setFailed] = useState(() => /* @__PURE__ */ new Set());
|
|
1637
|
+
const onFailed = useCallback((src) => {
|
|
1638
|
+
setFailed(
|
|
1639
|
+
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
1640
|
+
);
|
|
1641
|
+
}, []);
|
|
1642
|
+
const scope = useMemo(
|
|
1643
|
+
() => ({
|
|
1644
|
+
strings,
|
|
1645
|
+
enabled,
|
|
1646
|
+
origins: imageOrigins,
|
|
1647
|
+
productSlugs,
|
|
1648
|
+
failed,
|
|
1649
|
+
onFailed
|
|
1650
|
+
}),
|
|
1651
|
+
[strings, enabled, imageOrigins, productSlugs, failed, onFailed]
|
|
1652
|
+
);
|
|
1653
|
+
const rehypePlugins = useMemo(
|
|
1654
|
+
() => [[rehypeProductVideo, { origins: videoOrigins }]],
|
|
1655
|
+
[videoOrigins]
|
|
1656
|
+
);
|
|
1657
|
+
const overrides = useMemo(
|
|
1658
|
+
() => components ? { ...ANSWER_MARKDOWN_COMPONENTS, ...components } : ANSWER_MARKDOWN_COMPONENTS,
|
|
1659
|
+
[components]
|
|
1660
|
+
);
|
|
1661
|
+
return /* @__PURE__ */ jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsx(Markdown, { components: overrides, rehypePlugins, children }) }) });
|
|
1133
1662
|
}
|
|
1134
1663
|
|
|
1135
1664
|
// src/components/explore-types.ts
|
|
@@ -1762,8 +2291,9 @@ var ECOSYSTEM_PRODUCTS = [
|
|
|
1762
2291
|
name: "FluidGrids",
|
|
1763
2292
|
websiteUrl: "https://www.fluidgrids.ai"
|
|
1764
2293
|
},
|
|
1765
|
-
// BotManor ships on
|
|
1766
|
-
|
|
2294
|
+
// BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain
|
|
2295
|
+
// still serves the same site but is no longer the name we link to).
|
|
2296
|
+
{ slug: "botlit", name: "BotManor", websiteUrl: "https://www.botmanor.com" },
|
|
1767
2297
|
{
|
|
1768
2298
|
slug: "bigconsole",
|
|
1769
2299
|
name: "BigConsole",
|
|
@@ -3037,7 +3567,7 @@ function isHighSurrogate(code) {
|
|
|
3037
3567
|
}
|
|
3038
3568
|
function safeInlineEnd(line) {
|
|
3039
3569
|
let codeSpan = null;
|
|
3040
|
-
|
|
3570
|
+
const links = [];
|
|
3041
3571
|
const emphasis = [];
|
|
3042
3572
|
let at = 0;
|
|
3043
3573
|
while (at < line.length) {
|
|
@@ -3046,6 +3576,13 @@ function safeInlineEnd(line) {
|
|
|
3046
3576
|
at += 2;
|
|
3047
3577
|
continue;
|
|
3048
3578
|
}
|
|
3579
|
+
const innermost = links[links.length - 1];
|
|
3580
|
+
if (innermost !== void 0 && innermost.kind !== "label") {
|
|
3581
|
+
if (char === "(") links.push({ kind: "paren", start: innermost.start });
|
|
3582
|
+
else if (char === ")") links.pop();
|
|
3583
|
+
at += 1;
|
|
3584
|
+
continue;
|
|
3585
|
+
}
|
|
3049
3586
|
if (char === "`") {
|
|
3050
3587
|
let run = 1;
|
|
3051
3588
|
while (line[at + run] === "`") run += 1;
|
|
@@ -3058,17 +3595,17 @@ function safeInlineEnd(line) {
|
|
|
3058
3595
|
continue;
|
|
3059
3596
|
}
|
|
3060
3597
|
if (char === "[") {
|
|
3061
|
-
|
|
3598
|
+
links.push({ kind: "label", start: line[at - 1] === "!" ? at - 1 : at });
|
|
3062
3599
|
at += 1;
|
|
3063
3600
|
continue;
|
|
3064
3601
|
}
|
|
3065
|
-
if (char === "]") {
|
|
3066
|
-
|
|
3067
|
-
at
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3602
|
+
if (char === "]" && innermost !== void 0) {
|
|
3603
|
+
links.pop();
|
|
3604
|
+
if (line[at + 1] === "(") {
|
|
3605
|
+
links.push({ kind: "destination", start: innermost.start });
|
|
3606
|
+
at += 2;
|
|
3607
|
+
continue;
|
|
3608
|
+
}
|
|
3072
3609
|
at += 1;
|
|
3073
3610
|
continue;
|
|
3074
3611
|
}
|
|
@@ -3095,9 +3632,11 @@ function safeInlineEnd(line) {
|
|
|
3095
3632
|
}
|
|
3096
3633
|
at += 1;
|
|
3097
3634
|
}
|
|
3098
|
-
const open = [
|
|
3099
|
-
|
|
3100
|
-
|
|
3635
|
+
const open = [
|
|
3636
|
+
codeSpan,
|
|
3637
|
+
links[0]?.start ?? null,
|
|
3638
|
+
emphasis[0]?.start ?? null
|
|
3639
|
+
].filter((value) => value !== null);
|
|
3101
3640
|
return open.length === 0 ? line.length : Math.min(...open);
|
|
3102
3641
|
}
|
|
3103
3642
|
function revealBoundary(text, limit) {
|
|
@@ -5084,6 +5623,7 @@ function AssistantBubble({
|
|
|
5084
5623
|
message,
|
|
5085
5624
|
activity,
|
|
5086
5625
|
imageOrigins,
|
|
5626
|
+
productSlugs,
|
|
5087
5627
|
productNames,
|
|
5088
5628
|
speechLang,
|
|
5089
5629
|
onNavigate,
|
|
@@ -5198,6 +5738,7 @@ function AssistantBubble({
|
|
|
5198
5738
|
{
|
|
5199
5739
|
strings: T,
|
|
5200
5740
|
imageOrigins,
|
|
5741
|
+
productSlugs,
|
|
5201
5742
|
enabled: completed,
|
|
5202
5743
|
className: "boff-prose-sm text-card-foreground",
|
|
5203
5744
|
children: visible
|
|
@@ -5955,6 +6496,10 @@ function ExplorePage({
|
|
|
5955
6496
|
() => productNamesByOrigin(catalog.products),
|
|
5956
6497
|
[catalog.products]
|
|
5957
6498
|
);
|
|
6499
|
+
const productSlugs = useMemo(
|
|
6500
|
+
() => productSlugsByOrigin(catalog.products),
|
|
6501
|
+
[catalog.products]
|
|
6502
|
+
);
|
|
5958
6503
|
useEffect(() => {
|
|
5959
6504
|
const el = textareaRef.current;
|
|
5960
6505
|
if (!el) return;
|
|
@@ -6517,6 +7062,7 @@ function ExplorePage({
|
|
|
6517
7062
|
message,
|
|
6518
7063
|
activity,
|
|
6519
7064
|
imageOrigins,
|
|
7065
|
+
productSlugs,
|
|
6520
7066
|
productNames,
|
|
6521
7067
|
speechLang,
|
|
6522
7068
|
onNavigate,
|
|
@@ -6840,6 +7386,6 @@ function ExplorePage({
|
|
|
6840
7386
|
);
|
|
6841
7387
|
}
|
|
6842
7388
|
|
|
6843
|
-
export { EXPLORE_PREFILL_MAX_LENGTH, ExplorePage, answerPlainText, answerSpeechText, exploreQuestionHref, inlineImageOrigins, isInlineImageAllowed, logoUrlOf, productNamesByOrigin, referenceKindOf, summariseReferences };
|
|
7389
|
+
export { EXPLORE_PREFILL_MAX_LENGTH, ExplorePage, answerPlainText, answerSpeechText, exploreQuestionHref, inlineImageOrigins, isInlineImageAllowed, isInlineVideoAllowed, logoUrlOf, productNamesByOrigin, referenceKindOf, summariseReferences };
|
|
6844
7390
|
//# sourceMappingURL=explore.mjs.map
|
|
6845
7391
|
//# sourceMappingURL=explore.mjs.map
|