@burdenoff/website-sdk 2026.922.4 → 2026.923.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/explore.d.mts +4 -158
- package/dist/components/explore.d.ts +4 -158
- package/dist/components/explore.js +1464 -263
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +1466 -266
- package/dist/components/explore.mjs.map +1 -1
- package/dist/explore-D25zoXy9.d.mts +240 -0
- package/dist/explore-I4QnY4J9.d.ts +240 -0
- package/dist/{explore-types-DvQPiL1t.d.mts → explore-types-B5sdOC5M.d.mts} +75 -2
- package/dist/{explore-types-DvQPiL1t.d.ts → explore-types-B5sdOC5M.d.ts} +75 -2
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +481 -8
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +479 -9
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +433 -25
- package/dist/index.d.ts +433 -25
- package/dist/index.js +2839 -409
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2817 -412
- package/dist/index.mjs.map +1 -1
- package/dist/{use-explore-chat-Dgm-jf96.d.mts → use-progressive-reveal-Cmy8bA7L.d.mts} +88 -7
- package/dist/{use-explore-chat-CNEdKBc1.d.ts → use-progressive-reveal-DHPGGxZu.d.ts} +88 -7
- package/package.json +3 -2
|
@@ -1,19 +1,485 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext } from 'react';
|
|
3
|
-
import { Sparkles, RotateCcw, Link2, History, Trash2, Paperclip, X, Mic, Square, ArrowUp, BookOpenText, Layers, TriangleAlert, ChevronLeft, ChevronRight, ExternalLink, Check, Copy, ThumbsUp, ThumbsDown } from 'lucide-react';
|
|
2
|
+
import { createContext, useMemo, useState, useRef, useCallback, useEffect, 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';
|
|
4
4
|
import ReactMarkdown from 'react-markdown';
|
|
5
5
|
import rehypeSanitize from 'rehype-sanitize';
|
|
6
6
|
import remarkGfm from 'remark-gfm';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
import { clsx } from 'clsx';
|
|
9
|
+
import { twMerge } from 'tailwind-merge';
|
|
8
10
|
import { Helmet } from 'react-helmet-async';
|
|
9
11
|
import { Slot } from '@radix-ui/react-slot';
|
|
10
12
|
import { cva } from 'class-variance-authority';
|
|
11
|
-
import { clsx } from 'clsx';
|
|
12
|
-
import { twMerge } from 'tailwind-merge';
|
|
13
13
|
|
|
14
14
|
var __defProp = Object.defineProperty;
|
|
15
15
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
16
16
|
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
|
+
|
|
155
|
+
/* Narrow: one card per row. Each cell prints its own column heading, so a value is never
|
|
156
|
+
stranded without the question it answers, and the subject cell becomes the card's title.
|
|
157
|
+
A media query rather than a measured width \u2014 these pages are serialised post-hydration,
|
|
158
|
+
so anything measured here would ship to every phone as static HTML. */
|
|
159
|
+
@media (max-width: 30rem) {
|
|
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
|
+
}
|
|
17
483
|
|
|
18
484
|
// src/components/explore-i18n.ts
|
|
19
485
|
var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
|
|
@@ -32,7 +498,7 @@ var EN = {
|
|
|
32
498
|
showLess: "Show less",
|
|
33
499
|
sources: "Sources",
|
|
34
500
|
nextYouCouldAsk: "Next you could ask",
|
|
35
|
-
placeholder: "Describe the problem
|
|
501
|
+
placeholder: "Describe the problem\u2026",
|
|
36
502
|
placeholderProduct: "Describe a problem, or ask about {product}\u2026",
|
|
37
503
|
send: "Send",
|
|
38
504
|
stop: "Stop",
|
|
@@ -73,6 +539,20 @@ var EN = {
|
|
|
73
539
|
feedbackDismiss: "Dismiss",
|
|
74
540
|
feedbackThanks: "Thanks \u2014 that helps.",
|
|
75
541
|
feedbackNotSent: "That didn't send.",
|
|
542
|
+
escalateOpen: "Talk to a person",
|
|
543
|
+
escalateTitle: "Send this conversation to a person",
|
|
544
|
+
escalateBody: "We'll pass the whole conversation on, so you don't have to type any of it again.",
|
|
545
|
+
escalateEmailLabel: "Your email address",
|
|
546
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
547
|
+
escalateNoteLabel: "Anything else a person should know? (optional)",
|
|
548
|
+
escalateSubmit: "Send to a person",
|
|
549
|
+
escalateSending: "Sending\u2026",
|
|
550
|
+
escalateCancel: "Cancel",
|
|
551
|
+
escalateInvalidEmail: "That doesn't look like an address we can reply to. Please check it.",
|
|
552
|
+
escalateSent: "A person now has this conversation. We'll reply to {email}.",
|
|
553
|
+
escalateRateLimited: "That's a lot of requests in a short time. Please try again in a few minutes.",
|
|
554
|
+
escalateCaptcha: "We couldn't verify that you're human. Please try that again.",
|
|
555
|
+
escalateFailed: "We couldn't pass that on just now. Please try again.",
|
|
76
556
|
prevSuggestion: "Previous suggestion",
|
|
77
557
|
nextSuggestion: "Next suggestion",
|
|
78
558
|
conceptIllustration: "Concept illustration",
|
|
@@ -83,6 +563,9 @@ var EN = {
|
|
|
83
563
|
conceptAskQuestion: "How does {title} work in {product}?",
|
|
84
564
|
copyAnswer: "Copy answer",
|
|
85
565
|
copyFailed: "Couldn't copy",
|
|
566
|
+
readAloud: "Read aloud",
|
|
567
|
+
stopReading: "Stop reading",
|
|
568
|
+
answerTable: "Table",
|
|
86
569
|
groundedIn: "What this answer is based on",
|
|
87
570
|
sourceCountOne: "1 source",
|
|
88
571
|
sourceCountMany: "{count} sources",
|
|
@@ -117,7 +600,7 @@ var ZH = {
|
|
|
117
600
|
showLess: "\u6536\u8D77",
|
|
118
601
|
sources: "\u6765\u6E90",
|
|
119
602
|
nextYouCouldAsk: "\u4F60\u8FD8\u53EF\u4EE5\u95EE",
|
|
120
|
-
placeholder: "\u63CF\u8FF0\u4F60\
|
|
603
|
+
placeholder: "\u63CF\u8FF0\u4F60\u7684\u95EE\u9898\u2026",
|
|
121
604
|
placeholderProduct: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u6216\u8BE2\u95EE {product}\u2026",
|
|
122
605
|
send: "\u53D1\u9001",
|
|
123
606
|
stop: "\u505C\u6B62",
|
|
@@ -155,6 +638,20 @@ var ZH = {
|
|
|
155
638
|
feedbackDismiss: "\u5173\u95ED",
|
|
156
639
|
feedbackThanks: "\u8C22\u8C22\uFF0C\u8FD9\u5BF9\u6211\u4EEC\u5F88\u6709\u5E2E\u52A9\u3002",
|
|
157
640
|
feedbackNotSent: "\u6CA1\u6709\u63D0\u4EA4\u6210\u529F\u3002",
|
|
641
|
+
escalateOpen: "\u8054\u7CFB\u771F\u4EBA",
|
|
642
|
+
escalateTitle: "\u628A\u8FD9\u6BB5\u5BF9\u8BDD\u8F6C\u7ED9\u771F\u4EBA",
|
|
643
|
+
escalateBody: "\u6211\u4EEC\u4F1A\u628A\u6574\u6BB5\u5BF9\u8BDD\u4E00\u8D77\u8F6C\u8FC7\u53BB\uFF0C\u60A8\u4E0D\u7528\u518D\u91CD\u65B0\u6253\u4E00\u904D\u3002",
|
|
644
|
+
escalateEmailLabel: "\u60A8\u7684\u90AE\u7BB1\u5730\u5740",
|
|
645
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
646
|
+
escalateNoteLabel: "\u8FD8\u6709\u4EC0\u4E48\u9700\u8981\u544A\u8BC9\u6211\u4EEC\u7684\u5417\uFF1F\uFF08\u9009\u586B\uFF09",
|
|
647
|
+
escalateSubmit: "\u8F6C\u7ED9\u771F\u4EBA",
|
|
648
|
+
escalateSending: "\u6B63\u5728\u53D1\u9001\u2026",
|
|
649
|
+
escalateCancel: "\u53D6\u6D88",
|
|
650
|
+
escalateInvalidEmail: "\u8FD9\u4F3C\u4E4E\u4E0D\u662F\u4E00\u4E2A\u6211\u4EEC\u53EF\u4EE5\u56DE\u590D\u7684\u90AE\u7BB1\u5730\u5740\uFF0C\u8BF7\u68C0\u67E5\u4E00\u4E0B\u3002",
|
|
651
|
+
escalateSent: "\u5DF2\u6709\u540C\u4E8B\u63A5\u624B\u8FD9\u6BB5\u5BF9\u8BDD\uFF0C\u6211\u4EEC\u4F1A\u56DE\u590D\u5230 {email}\u3002",
|
|
652
|
+
escalateRateLimited: "\u77ED\u65F6\u95F4\u5185\u8BF7\u6C42\u8FC7\u591A\uFF0C\u8BF7\u51E0\u5206\u949F\u540E\u518D\u8BD5\u3002",
|
|
653
|
+
escalateCaptcha: "\u6211\u4EEC\u65E0\u6CD5\u9A8C\u8BC1\u60A8\u662F\u771F\u4EBA\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21\u3002",
|
|
654
|
+
escalateFailed: "\u6682\u65F6\u65E0\u6CD5\u8F6C\u4EA4\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21\u3002",
|
|
158
655
|
prevSuggestion: "\u4E0A\u4E00\u4E2A\u5EFA\u8BAE",
|
|
159
656
|
nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
|
|
160
657
|
conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
|
|
@@ -165,6 +662,9 @@ var ZH = {
|
|
|
165
662
|
conceptAskQuestion: "{product} \u7684 {title} \u662F\u600E\u4E48\u8FD0\u4F5C\u7684\uFF1F",
|
|
166
663
|
copyAnswer: "\u590D\u5236\u56DE\u7B54",
|
|
167
664
|
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
665
|
+
readAloud: "\u6717\u8BFB",
|
|
666
|
+
stopReading: "\u505C\u6B62\u6717\u8BFB",
|
|
667
|
+
answerTable: "\u8868\u683C",
|
|
168
668
|
groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
|
|
169
669
|
sourceCountOne: "1 \u4E2A\u6765\u6E90",
|
|
170
670
|
sourceCountMany: "{count} \u4E2A\u6765\u6E90",
|
|
@@ -195,7 +695,7 @@ var HI = {
|
|
|
195
695
|
showLess: "\u0915\u092E \u0926\u093F\u0916\u093E\u090F\u0901",
|
|
196
696
|
sources: "\u0938\u094D\u0930\u094B\u0924",
|
|
197
697
|
nextYouCouldAsk: "\u0906\u0917\u0947 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
|
|
198
|
-
placeholder: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \
|
|
698
|
+
placeholder: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902\u2026",
|
|
199
699
|
placeholderProduct: "\u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902, \u092F\u093E {product} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902\u2026",
|
|
200
700
|
send: "\u092D\u0947\u091C\u0947\u0902",
|
|
201
701
|
stop: "\u0930\u094B\u0915\u0947\u0902",
|
|
@@ -233,6 +733,20 @@ var HI = {
|
|
|
233
733
|
feedbackDismiss: "\u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
234
734
|
feedbackThanks: "\u0927\u0928\u094D\u092F\u0935\u093E\u0926, \u0907\u0938\u0938\u0947 \u092E\u0926\u0926 \u092E\u093F\u0932\u0924\u0940 \u0939\u0948\u0964",
|
|
235
735
|
feedbackNotSent: "\u092F\u0939 \u092D\u0947\u091C\u093E \u0928\u0939\u0940\u0902 \u091C\u093E \u0938\u0915\u093E\u0964",
|
|
736
|
+
escalateOpen: "\u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0938\u0947 \u092C\u093E\u0924 \u0915\u0930\u0947\u0902",
|
|
737
|
+
escalateTitle: "\u092F\u0939 \u092C\u093E\u0924\u091A\u0940\u0924 \u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u094B \u092D\u0947\u091C\u0947\u0902",
|
|
738
|
+
escalateBody: "\u0939\u092E \u092A\u0942\u0930\u0940 \u092C\u093E\u0924\u091A\u0940\u0924 \u0906\u0917\u0947 \u092D\u0947\u091C \u0926\u0947\u0902\u0917\u0947, \u0924\u093E\u0915\u093F \u0906\u092A\u0915\u094B \u0926\u094B\u092C\u093E\u0930\u093E \u0915\u0941\u091B \u0932\u093F\u0916\u0928\u093E \u0928 \u092A\u0921\u093C\u0947\u0964",
|
|
739
|
+
escalateEmailLabel: "\u0906\u092A\u0915\u093E \u0908\u092E\u0947\u0932 \u092A\u0924\u093E",
|
|
740
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
741
|
+
escalateNoteLabel: "\u0915\u094D\u092F\u093E \u0915\u0941\u091B \u0914\u0930 \u092C\u0924\u093E\u0928\u093E \u091A\u093E\u0939\u0947\u0902\u0917\u0947? (\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915)",
|
|
742
|
+
escalateSubmit: "\u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u094B \u092D\u0947\u091C\u0947\u0902",
|
|
743
|
+
escalateSending: "\u092D\u0947\u091C\u093E \u091C\u093E \u0930\u0939\u093E \u0939\u0948\u2026",
|
|
744
|
+
escalateCancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
|
|
745
|
+
escalateInvalidEmail: "\u092F\u0939 \u0910\u0938\u093E \u0908\u092E\u0947\u0932 \u092A\u0924\u093E \u0928\u0939\u0940\u0902 \u0932\u0917\u0924\u093E \u091C\u093F\u0938 \u092A\u0930 \u0939\u092E \u091C\u0935\u093E\u092C \u092D\u0947\u091C \u0938\u0915\u0947\u0902\u0964 \u0915\u0943\u092A\u092F\u093E \u091C\u093E\u0901\u091A \u0932\u0947\u0902\u0964",
|
|
746
|
+
escalateSent: "\u0905\u092C \u092F\u0939 \u092C\u093E\u0924\u091A\u0940\u0924 \u090F\u0915 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u0947 \u092A\u093E\u0938 \u0939\u0948\u0964 \u0939\u092E {email} \u092A\u0930 \u091C\u0935\u093E\u092C \u0926\u0947\u0902\u0917\u0947\u0964",
|
|
747
|
+
escalateRateLimited: "\u0925\u094B\u0921\u093C\u0947 \u0938\u092E\u092F \u092E\u0947\u0902 \u092C\u0939\u0941\u0924 \u0938\u093E\u0930\u0947 \u0905\u0928\u0941\u0930\u094B\u0927 \u0939\u094B \u0917\u090F\u0964 \u0915\u0943\u092A\u092F\u093E \u0915\u0941\u091B \u092E\u093F\u0928\u091F \u092C\u093E\u0926 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
748
|
+
escalateCaptcha: "\u0939\u092E \u092F\u0939 \u0938\u0924\u094D\u092F\u093E\u092A\u093F\u0924 \u0928\u0939\u0940\u0902 \u0915\u0930 \u0938\u0915\u0947 \u0915\u093F \u0906\u092A \u090F\u0915 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0939\u0948\u0902\u0964 \u0915\u0943\u092A\u092F\u093E \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
749
|
+
escalateFailed: "\u0905\u092D\u0940 \u0907\u0938\u0947 \u0906\u0917\u0947 \u0928\u0939\u0940\u0902 \u092D\u0947\u091C\u093E \u091C\u093E \u0938\u0915\u093E\u0964 \u0915\u0943\u092A\u092F\u093E \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
236
750
|
prevSuggestion: "\u092A\u093F\u091B\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
|
|
237
751
|
nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
|
|
238
752
|
conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
|
|
@@ -243,6 +757,9 @@ var HI = {
|
|
|
243
757
|
conceptAskQuestion: "{product} \u092E\u0947\u0902 {title} \u0915\u0948\u0938\u0947 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948?",
|
|
244
758
|
copyAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
|
|
245
759
|
copyFailed: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E",
|
|
760
|
+
readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
|
|
761
|
+
stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
762
|
+
answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
|
|
246
763
|
groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
|
|
247
764
|
sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
|
|
248
765
|
sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
|
|
@@ -277,7 +794,7 @@ var ES = {
|
|
|
277
794
|
showLess: "Ver menos",
|
|
278
795
|
sources: "Fuentes",
|
|
279
796
|
nextYouCouldAsk: "Tambi\xE9n puedes preguntar",
|
|
280
|
-
placeholder: "Describe el problema
|
|
797
|
+
placeholder: "Describe el problema\u2026",
|
|
281
798
|
placeholderProduct: "Describe un problema o pregunta por {product}\u2026",
|
|
282
799
|
send: "Enviar",
|
|
283
800
|
stop: "Detener",
|
|
@@ -315,6 +832,20 @@ var ES = {
|
|
|
315
832
|
feedbackDismiss: "Cerrar",
|
|
316
833
|
feedbackThanks: "Gracias, nos sirve.",
|
|
317
834
|
feedbackNotSent: "No se ha podido enviar.",
|
|
835
|
+
escalateOpen: "Hablar con una persona",
|
|
836
|
+
escalateTitle: "Env\xEDa esta conversaci\xF3n a una persona",
|
|
837
|
+
escalateBody: "Le pasaremos la conversaci\xF3n entera, as\xED no tienes que volver a escribirla.",
|
|
838
|
+
escalateEmailLabel: "Tu direcci\xF3n de correo",
|
|
839
|
+
escalateEmailPlaceholder: "tu@ejemplo.com",
|
|
840
|
+
escalateNoteLabel: "\xBFAlgo m\xE1s que deber\xEDamos saber? (opcional)",
|
|
841
|
+
escalateSubmit: "Enviar a una persona",
|
|
842
|
+
escalateSending: "Enviando\u2026",
|
|
843
|
+
escalateCancel: "Cancelar",
|
|
844
|
+
escalateInvalidEmail: "No parece una direcci\xF3n a la que podamos responder. Rev\xEDsala, por favor.",
|
|
845
|
+
escalateSent: "Una persona ya tiene esta conversaci\xF3n. Te responderemos a {email}.",
|
|
846
|
+
escalateRateLimited: "Han sido muchas peticiones en poco tiempo. Int\xE9ntalo de nuevo en unos minutos.",
|
|
847
|
+
escalateCaptcha: "No hemos podido verificar que eres una persona. Int\xE9ntalo de nuevo.",
|
|
848
|
+
escalateFailed: "No hemos podido pasarlo ahora mismo. Int\xE9ntalo de nuevo.",
|
|
318
849
|
prevSuggestion: "Sugerencia anterior",
|
|
319
850
|
nextSuggestion: "Sugerencia siguiente",
|
|
320
851
|
conceptIllustration: "Ilustraci\xF3n del concepto",
|
|
@@ -325,6 +856,9 @@ var ES = {
|
|
|
325
856
|
conceptAskQuestion: "\xBFC\xF3mo funciona {title} en {product}?",
|
|
326
857
|
copyAnswer: "Copiar respuesta",
|
|
327
858
|
copyFailed: "No se pudo copiar",
|
|
859
|
+
readAloud: "Leer en voz alta",
|
|
860
|
+
stopReading: "Dejar de leer",
|
|
861
|
+
answerTable: "Tabla",
|
|
328
862
|
groundedIn: "En qu\xE9 se basa esta respuesta",
|
|
329
863
|
sourceCountOne: "1 fuente",
|
|
330
864
|
sourceCountMany: "{count} fuentes",
|
|
@@ -359,7 +893,7 @@ var AR = {
|
|
|
359
893
|
showLess: "\u0639\u0631\u0636 \u0623\u0642\u0644",
|
|
360
894
|
sources: "\u0627\u0644\u0645\u0635\u0627\u062F\u0631",
|
|
361
895
|
nextYouCouldAsk: "\u064A\u0645\u0643\u0646\u0643 \u0623\u0646 \u062A\u0633\u0623\u0644 \u0628\u0639\u062F \u0630\u0644\u0643",
|
|
362
|
-
placeholder: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629
|
|
896
|
+
placeholder: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629\u2026",
|
|
363
897
|
placeholderProduct: "\u0635\u0641 \u0645\u0634\u0643\u0644\u062A\u0643 \u0623\u0648 \u0627\u0633\u0623\u0644 \u0639\u0646 {product}\u2026",
|
|
364
898
|
send: "\u0625\u0631\u0633\u0627\u0644",
|
|
365
899
|
stop: "\u0625\u064A\u0642\u0627\u0641",
|
|
@@ -397,6 +931,20 @@ var AR = {
|
|
|
397
931
|
feedbackDismiss: "\u0625\u063A\u0644\u0627\u0642",
|
|
398
932
|
feedbackThanks: "\u0634\u0643\u0631\u064B\u0627\u060C \u0647\u0630\u0627 \u064A\u0633\u0627\u0639\u062F\u0646\u0627.",
|
|
399
933
|
feedbackNotSent: "\u0644\u0645 \u064A\u062A\u0645 \u0627\u0644\u0625\u0631\u0633\u0627\u0644.",
|
|
934
|
+
escalateOpen: "\u062A\u0648\u0627\u0635\u0644 \u0645\u0639 \u0634\u062E\u0635",
|
|
935
|
+
escalateTitle: "\u0623\u0631\u0633\u0644 \u0647\u0630\u0647 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0625\u0644\u0649 \u0634\u062E\u0635",
|
|
936
|
+
escalateBody: "\u0633\u0646\u0645\u0631\u0651\u0631 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0643\u0627\u0645\u0644\u0629\u060C \u062D\u062A\u0649 \u0644\u0627 \u062A\u0636\u0637\u0631 \u0625\u0644\u0649 \u0643\u062A\u0627\u0628\u062A\u0647\u0627 \u0645\u0646 \u062C\u062F\u064A\u062F.",
|
|
937
|
+
escalateEmailLabel: "\u0628\u0631\u064A\u062F\u0643 \u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A",
|
|
938
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
939
|
+
escalateNoteLabel: "\u0647\u0644 \u0645\u0646 \u0634\u064A\u0621 \u0622\u062E\u0631 \u064A\u0646\u0628\u063A\u064A \u0623\u0646 \u0646\u0639\u0631\u0641\u0647\u061F (\u0627\u062E\u062A\u064A\u0627\u0631\u064A)",
|
|
940
|
+
escalateSubmit: "\u0625\u0631\u0633\u0627\u0644 \u0625\u0644\u0649 \u0634\u062E\u0635",
|
|
941
|
+
escalateSending: "\u062C\u0627\u0631\u064D \u0627\u0644\u0625\u0631\u0633\u0627\u0644\u2026",
|
|
942
|
+
escalateCancel: "\u0625\u0644\u063A\u0627\u0621",
|
|
943
|
+
escalateInvalidEmail: "\u0644\u0627 \u064A\u0628\u062F\u0648 \u0647\u0630\u0627 \u0639\u0646\u0648\u0627\u0646\u064B\u0627 \u064A\u0645\u0643\u0646\u0646\u0627 \u0627\u0644\u0631\u062F \u0639\u0644\u064A\u0647. \u064A\u0631\u062C\u0649 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646\u0647.",
|
|
944
|
+
escalateSent: "\u0623\u0635\u0628\u062D\u062A \u0647\u0630\u0647 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0644\u062F\u0649 \u0623\u062D\u062F \u0627\u0644\u0632\u0645\u0644\u0627\u0621. \u0633\u0646\u0631\u062F \u0639\u0644\u0649 {email}.",
|
|
945
|
+
escalateRateLimited: "\u0637\u0644\u0628\u0627\u062A \u0643\u062B\u064A\u0631\u0629 \u062E\u0644\u0627\u0644 \u0648\u0642\u062A \u0642\u0635\u064A\u0631. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0628\u0639\u062F \u0628\u0636\u0639 \u062F\u0642\u0627\u0626\u0642.",
|
|
946
|
+
escalateCaptcha: "\u062A\u0639\u0630\u0651\u0631 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u0623\u0646\u0643 \u0644\u0633\u062A \u0631\u0648\u0628\u0648\u062A\u064B\u0627. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
|
|
947
|
+
escalateFailed: "\u062A\u0639\u0630\u0651\u0631 \u062A\u0645\u0631\u064A\u0631 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0627\u0644\u0622\u0646. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
|
|
400
948
|
prevSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u0633\u0627\u0628\u0642",
|
|
401
949
|
nextSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u062A\u0627\u0644\u064A",
|
|
402
950
|
conceptIllustration: "\u0631\u0633\u0645 \u062A\u0648\u0636\u064A\u062D\u064A \u0644\u0644\u0645\u0641\u0647\u0648\u0645",
|
|
@@ -407,6 +955,9 @@ var AR = {
|
|
|
407
955
|
conceptAskQuestion: "\u0643\u064A\u0641 \u064A\u0639\u0645\u0644 {title} \u0641\u064A {product}\u061F",
|
|
408
956
|
copyAnswer: "\u0646\u0633\u062E \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
409
957
|
copyFailed: "\u062A\u0639\u0630\u0651\u0631 \u0627\u0644\u0646\u0633\u062E",
|
|
958
|
+
readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
|
|
959
|
+
stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
|
|
960
|
+
answerTable: "\u062C\u062F\u0648\u0644",
|
|
410
961
|
groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
411
962
|
sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
|
|
412
963
|
sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
|
|
@@ -441,7 +992,7 @@ var TA = {
|
|
|
441
992
|
showLess: "\u0B95\u0BC1\u0BB1\u0BC8\u0BB5\u0BBE\u0B95\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1",
|
|
442
993
|
sources: "\u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
443
994
|
nextYouCouldAsk: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BC7\u0B9F\u0BCD\u0B95\u0BB2\u0BBE\u0BAE\u0BCD",
|
|
444
|
-
placeholder: "\
|
|
995
|
+
placeholder: "\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
|
|
445
996
|
placeholderProduct: "\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD, \u0B85\u0BB2\u0BCD\u0BB2\u0BA4\u0BC1 {product} \u0BAA\u0BB1\u0BCD\u0BB1\u0BBF\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
|
|
446
997
|
send: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1",
|
|
447
998
|
stop: "\u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
@@ -479,6 +1030,20 @@ var TA = {
|
|
|
479
1030
|
feedbackDismiss: "\u0BAE\u0BC2\u0B9F\u0BC1",
|
|
480
1031
|
feedbackThanks: "\u0BA8\u0BA9\u0BCD\u0BB1\u0BBF, \u0B87\u0BA4\u0BC1 \u0B8E\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B89\u0BA4\u0BB5\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1.",
|
|
481
1032
|
feedbackNotSent: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
|
|
1033
|
+
escalateOpen: "\u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BBF\u0B9F\u0BAE\u0BCD \u0BAA\u0BC7\u0B9A\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
1034
|
+
escalateTitle: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BC8 \u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
1035
|
+
escalateBody: "\u0BAE\u0BC1\u0BB4\u0BC1 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD \u0BA8\u0BBE\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BBF\u0BB5\u0BBF\u0B9F\u0BC1\u0BB5\u0BCB\u0BAE\u0BCD; \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BA4\u0B9F\u0BCD\u0B9F\u0B9A\u0BCD\u0B9A\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD\u0BAF \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BBF\u0BAF\u0BA4\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
|
|
1036
|
+
escalateEmailLabel: "\u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BBF\u0BA9\u0BCD\u0BA9\u0B9E\u0BCD\u0B9A\u0BB2\u0BCD \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF",
|
|
1037
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
1038
|
+
escalateNoteLabel: "\u0BB5\u0BC7\u0BB1\u0BC1 \u0B8F\u0BA4\u0BC7\u0BA9\u0BC1\u0BAE\u0BCD \u0B9A\u0BCA\u0BB2\u0BCD\u0BB2 \u0BB5\u0BBF\u0BB0\u0BC1\u0BAE\u0BCD\u0BAA\u0BC1\u0B95\u0BBF\u0BB1\u0BC0\u0BB0\u0BCD\u0B95\u0BB3\u0BBE? (\u0BB5\u0BBF\u0BB0\u0BC1\u0BAA\u0BCD\u0BAA\u0BAE\u0BCD)",
|
|
1039
|
+
escalateSubmit: "\u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1",
|
|
1040
|
+
escalateSending: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
|
|
1041
|
+
escalateCancel: "\u0BB0\u0BA4\u0BCD\u0BA4\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD",
|
|
1042
|
+
escalateInvalidEmail: "\u0B87\u0BA4\u0BC1 \u0BA8\u0BBE\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BB3\u0BBF\u0B95\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0BAE\u0BBF\u0BA9\u0BCD\u0BA9\u0B9E\u0BCD\u0B9A\u0BB2\u0BCD \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF \u0BAA\u0BCB\u0BB2\u0BCD \u0BA4\u0BC6\u0BB0\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
1043
|
+
escalateSent: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BCD \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BBF\u0B9F\u0BAE\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1. {email} \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BB3\u0BBF\u0BAA\u0BCD\u0BAA\u0BCB\u0BAE\u0BCD.",
|
|
1044
|
+
escalateRateLimited: "\u0B95\u0BC1\u0BB1\u0BC1\u0B95\u0BBF\u0BAF \u0BA8\u0BC7\u0BB0\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BA4\u0BBF\u0B95 \u0B95\u0BCB\u0BB0\u0BBF\u0B95\u0BCD\u0B95\u0BC8\u0B95\u0BB3\u0BCD. \u0B9A\u0BBF\u0BB2 \u0BA8\u0BBF\u0BAE\u0BBF\u0B9F\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BB4\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1 \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
1045
|
+
escalateCaptcha: "\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BA9\u0BBF\u0BA4\u0BB0\u0BCD \u0B8E\u0BA9\u0BCD\u0BAA\u0BA4\u0BC8 \u0B89\u0BB1\u0BC1\u0BA4\u0BBF\u0B9A\u0BC6\u0BAF\u0BCD\u0BAF \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
1046
|
+
escalateFailed: "\u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
482
1047
|
prevSuggestion: "\u0BAE\u0BC1\u0BA8\u0BCD\u0BA4\u0BC8\u0BAF \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
|
|
483
1048
|
nextSuggestion: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
|
|
484
1049
|
conceptIllustration: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BB5\u0BBF\u0BB3\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BAE\u0BCD",
|
|
@@ -489,6 +1054,9 @@ var TA = {
|
|
|
489
1054
|
conceptAskQuestion: "{product} \u0B87\u0BB2\u0BCD {title} \u0B8E\u0BAA\u0BCD\u0BAA\u0B9F\u0BBF \u0BB5\u0BC7\u0BB2\u0BC8 \u0B9A\u0BC6\u0BAF\u0BCD\u0B95\u0BBF\u0BB1\u0BA4\u0BC1?",
|
|
490
1055
|
copyAnswer: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1",
|
|
491
1056
|
copyFailed: "\u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8",
|
|
1057
|
+
readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
|
|
1058
|
+
stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
1059
|
+
answerTable: "\u0B85\u0B9F\u0BCD\u0B9F\u0BB5\u0BA3\u0BC8",
|
|
492
1060
|
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",
|
|
493
1061
|
sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
|
|
494
1062
|
sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
@@ -520,6 +1088,17 @@ var STRINGS = {
|
|
|
520
1088
|
ar: AR,
|
|
521
1089
|
ta: TA
|
|
522
1090
|
};
|
|
1091
|
+
var SPEECH_TAGS = {
|
|
1092
|
+
en: "en-US",
|
|
1093
|
+
zh: "zh-CN",
|
|
1094
|
+
hi: "hi-IN",
|
|
1095
|
+
es: "es-ES",
|
|
1096
|
+
ar: "ar-SA",
|
|
1097
|
+
ta: "ta-IN"
|
|
1098
|
+
};
|
|
1099
|
+
function speechLocaleOf(locale) {
|
|
1100
|
+
return SPEECH_TAGS[locale];
|
|
1101
|
+
}
|
|
523
1102
|
function resolveExploreLocale(explicit) {
|
|
524
1103
|
const candidates = [
|
|
525
1104
|
explicit,
|
|
@@ -707,6 +1286,29 @@ var SUBMIT_EXPLORE_FEEDBACK_MUTATION = (
|
|
|
707
1286
|
`
|
|
708
1287
|
);
|
|
709
1288
|
var EXPLORE_FEEDBACK_COMMENT_SOFT_MAX = 500;
|
|
1289
|
+
var ESCALATE_EXPLORE_CONVERSATION_MUTATION = (
|
|
1290
|
+
/* GraphQL */
|
|
1291
|
+
`
|
|
1292
|
+
mutation EscalateExploreConversation(
|
|
1293
|
+
$input: EscalateExploreConversationInput!
|
|
1294
|
+
) {
|
|
1295
|
+
escalateExploreConversation(input: $input) {
|
|
1296
|
+
created
|
|
1297
|
+
escalation {
|
|
1298
|
+
email
|
|
1299
|
+
createdAt
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
`
|
|
1304
|
+
);
|
|
1305
|
+
var EXPLORE_ESCALATION_NOTE_SOFT_MAX = 2e3;
|
|
1306
|
+
var DELIVERABLE_EMAIL = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}$/;
|
|
1307
|
+
function looksLikeDeliverableEmail(value) {
|
|
1308
|
+
const trimmed = value.trim();
|
|
1309
|
+
if (trimmed.length === 0 || trimmed.length > 254) return false;
|
|
1310
|
+
return DELIVERABLE_EMAIL.test(trimmed);
|
|
1311
|
+
}
|
|
710
1312
|
var CREATE_EXPLORE_SHARE_LINK_MUTATION = (
|
|
711
1313
|
/* GraphQL */
|
|
712
1314
|
`
|
|
@@ -727,48 +1329,6 @@ var EXPLORE_SHARED_CONVERSATION_QUERY = (
|
|
|
727
1329
|
}
|
|
728
1330
|
`
|
|
729
1331
|
);
|
|
730
|
-
function MarkdownLink({
|
|
731
|
-
href,
|
|
732
|
-
children,
|
|
733
|
-
// react-markdown passes the hast node to every override; it is not a DOM attribute.
|
|
734
|
-
node: _node,
|
|
735
|
-
...props
|
|
736
|
-
}) {
|
|
737
|
-
const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
|
|
738
|
-
return /* @__PURE__ */ jsx(
|
|
739
|
-
"a",
|
|
740
|
-
{
|
|
741
|
-
href,
|
|
742
|
-
...props,
|
|
743
|
-
...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
|
|
744
|
-
children
|
|
745
|
-
}
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
var defaultComponents = {
|
|
749
|
-
a: MarkdownLink
|
|
750
|
-
};
|
|
751
|
-
function Markdown({
|
|
752
|
-
children,
|
|
753
|
-
className,
|
|
754
|
-
inline,
|
|
755
|
-
components
|
|
756
|
-
}) {
|
|
757
|
-
const content = /* @__PURE__ */ jsx(
|
|
758
|
-
ReactMarkdown,
|
|
759
|
-
{
|
|
760
|
-
remarkPlugins: [remarkGfm],
|
|
761
|
-
rehypePlugins: [rehypeSanitize],
|
|
762
|
-
components: { ...defaultComponents, ...components },
|
|
763
|
-
...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
|
|
764
|
-
children
|
|
765
|
-
}
|
|
766
|
-
);
|
|
767
|
-
if (inline) {
|
|
768
|
-
return /* @__PURE__ */ jsx("span", { className, children: content });
|
|
769
|
-
}
|
|
770
|
-
return /* @__PURE__ */ jsx("div", { className, children: content });
|
|
771
|
-
}
|
|
772
1332
|
var PROSE_CSS = `
|
|
773
1333
|
.boff-prose {
|
|
774
1334
|
max-width: none;
|
|
@@ -1165,6 +1725,10 @@ function useWebSDK() {
|
|
|
1165
1725
|
}
|
|
1166
1726
|
return client;
|
|
1167
1727
|
}
|
|
1728
|
+
function useWebSDKConfig() {
|
|
1729
|
+
const client = useWebSDK();
|
|
1730
|
+
return client.getConfig();
|
|
1731
|
+
}
|
|
1168
1732
|
|
|
1169
1733
|
// src/data/products.ts
|
|
1170
1734
|
var ECOSYSTEM_PRODUCTS = [
|
|
@@ -1511,6 +2075,25 @@ var VALIDATION_CODES = /* @__PURE__ */ new Set([
|
|
|
1511
2075
|
"CONVERSATION_LIMIT",
|
|
1512
2076
|
"INVALID_INPUT"
|
|
1513
2077
|
]);
|
|
2078
|
+
function escalateOutcomeForCode(code) {
|
|
2079
|
+
switch (code) {
|
|
2080
|
+
case "INVALID_EMAIL":
|
|
2081
|
+
return "invalid-email";
|
|
2082
|
+
case "RATE_LIMITED":
|
|
2083
|
+
case "HTTP_429":
|
|
2084
|
+
return "rate-limited";
|
|
2085
|
+
case "CAPTCHA_REQUIRED":
|
|
2086
|
+
case "CAPTCHA_FAILED":
|
|
2087
|
+
return "captcha";
|
|
2088
|
+
// The token owns nothing, or owns a different conversation. The server answers NOT_FOUND
|
|
2089
|
+
// rather than FORBIDDEN so the endpoint is not an oracle for whether a thread exists;
|
|
2090
|
+
// either way there is nothing this visitor can do about it, so it reads as a failure.
|
|
2091
|
+
case "NOT_FOUND":
|
|
2092
|
+
return "failed";
|
|
2093
|
+
default:
|
|
2094
|
+
return "failed";
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
1514
2097
|
function classifyExploreError(code, serverMessage) {
|
|
1515
2098
|
const c = code ?? "UNKNOWN";
|
|
1516
2099
|
if (RATE_LIMIT_CODES.has(c)) {
|
|
@@ -1677,6 +2260,7 @@ function useExploreChat(options = {}) {
|
|
|
1677
2260
|
onSend
|
|
1678
2261
|
} = options;
|
|
1679
2262
|
const client = useWebSDK();
|
|
2263
|
+
const { productId } = useWebSDKConfig();
|
|
1680
2264
|
const promptKey = JSON.stringify(examplePrompts ?? []);
|
|
1681
2265
|
const welcomeKey = JSON.stringify(fallbackWelcome ?? null);
|
|
1682
2266
|
const fallbackCatalog = useMemo(
|
|
@@ -1700,6 +2284,7 @@ function useExploreChat(options = {}) {
|
|
|
1700
2284
|
const pollGenerationRef = useRef(0);
|
|
1701
2285
|
const pollTimerRef = useRef(null);
|
|
1702
2286
|
const conversationTokenRef = useRef(null);
|
|
2287
|
+
const [hasConversation, setHasConversation] = useState(false);
|
|
1703
2288
|
const shareTokenRef = useRef(void 0);
|
|
1704
2289
|
if (shareTokenRef.current === void 0) {
|
|
1705
2290
|
shareTokenRef.current = readShareToken();
|
|
@@ -1714,6 +2299,10 @@ function useExploreChat(options = {}) {
|
|
|
1714
2299
|
);
|
|
1715
2300
|
const inFlightRef = useRef(false);
|
|
1716
2301
|
catalogRef.current = catalog;
|
|
2302
|
+
const rememberConversationToken = useCallback((token) => {
|
|
2303
|
+
conversationTokenRef.current = token;
|
|
2304
|
+
setHasConversation(token !== null);
|
|
2305
|
+
}, []);
|
|
1717
2306
|
const clearPollTimer = useCallback(() => {
|
|
1718
2307
|
if (pollTimerRef.current !== null) {
|
|
1719
2308
|
clearTimeout(pollTimerRef.current);
|
|
@@ -1780,7 +2369,7 @@ function useExploreChat(options = {}) {
|
|
|
1780
2369
|
setArchive(readArchive(storageKey));
|
|
1781
2370
|
const stored = readPersisted(storageKey);
|
|
1782
2371
|
if (stored) {
|
|
1783
|
-
|
|
2372
|
+
rememberConversationToken(stored.conversationToken);
|
|
1784
2373
|
focusProductRef.current = stored.focusProduct;
|
|
1785
2374
|
const settled = stored.messages.flatMap((m) => {
|
|
1786
2375
|
if (m.role !== "ASSISTANT") return [m];
|
|
@@ -1796,7 +2385,7 @@ function useExploreChat(options = {}) {
|
|
|
1796
2385
|
setTurnCount(turns);
|
|
1797
2386
|
}
|
|
1798
2387
|
setHydrated(true);
|
|
1799
|
-
}, [persist, storageKey]);
|
|
2388
|
+
}, [persist, rememberConversationToken, storageKey]);
|
|
1800
2389
|
useEffect(() => {
|
|
1801
2390
|
if (!persist || !hydrated || typeof window === "undefined") return;
|
|
1802
2391
|
try {
|
|
@@ -1989,7 +2578,7 @@ function useExploreChat(options = {}) {
|
|
|
1989
2578
|
setPhase(classified.kind === "rate-limit" ? "limited" : "error");
|
|
1990
2579
|
return;
|
|
1991
2580
|
}
|
|
1992
|
-
|
|
2581
|
+
rememberConversationToken(result.conversation.token);
|
|
1993
2582
|
turnCountRef.current = result.conversation.turnCount;
|
|
1994
2583
|
setTurnCount(result.conversation.turnCount);
|
|
1995
2584
|
setRemainingToday(result.remainingToday ?? null);
|
|
@@ -2015,7 +2604,14 @@ function useExploreChat(options = {}) {
|
|
|
2015
2604
|
setPhase("streaming");
|
|
2016
2605
|
startPolling(result.conversation.token, assistantMessage.id);
|
|
2017
2606
|
},
|
|
2018
|
-
[
|
|
2607
|
+
[
|
|
2608
|
+
client,
|
|
2609
|
+
locale,
|
|
2610
|
+
pageUrl,
|
|
2611
|
+
productSlug,
|
|
2612
|
+
rememberConversationToken,
|
|
2613
|
+
startPolling
|
|
2614
|
+
]
|
|
2019
2615
|
);
|
|
2020
2616
|
const send = useCallback(
|
|
2021
2617
|
async (message, attachments = []) => {
|
|
@@ -2132,7 +2728,7 @@ function useExploreChat(options = {}) {
|
|
|
2132
2728
|
);
|
|
2133
2729
|
cancelPolling();
|
|
2134
2730
|
inFlightRef.current = false;
|
|
2135
|
-
|
|
2731
|
+
rememberConversationToken(null);
|
|
2136
2732
|
lastSentTextRef.current = null;
|
|
2137
2733
|
lastSentAttachmentsRef.current = [];
|
|
2138
2734
|
turnCountRef.current = 0;
|
|
@@ -2147,7 +2743,7 @@ function useExploreChat(options = {}) {
|
|
|
2147
2743
|
} catch {
|
|
2148
2744
|
}
|
|
2149
2745
|
}
|
|
2150
|
-
}, [cancelPolling, storageKey]);
|
|
2746
|
+
}, [cancelPolling, rememberConversationToken, storageKey]);
|
|
2151
2747
|
const openConversation = useCallback(
|
|
2152
2748
|
(token) => {
|
|
2153
2749
|
const entry = readArchive(storageKey).find((e) => e.token === token);
|
|
@@ -2159,7 +2755,7 @@ function useExploreChat(options = {}) {
|
|
|
2159
2755
|
});
|
|
2160
2756
|
cancelPolling();
|
|
2161
2757
|
inFlightRef.current = false;
|
|
2162
|
-
|
|
2758
|
+
rememberConversationToken(entry.token);
|
|
2163
2759
|
turnCountRef.current = entry.messages.filter(
|
|
2164
2760
|
(m) => m.role === "USER"
|
|
2165
2761
|
).length;
|
|
@@ -2171,7 +2767,7 @@ function useExploreChat(options = {}) {
|
|
|
2171
2767
|
setPhase(catalogRef.current.enabled ? "ready" : "disabled");
|
|
2172
2768
|
setArchive(readArchive(storageKey));
|
|
2173
2769
|
},
|
|
2174
|
-
[cancelPolling, storageKey]
|
|
2770
|
+
[cancelPolling, rememberConversationToken, storageKey]
|
|
2175
2771
|
);
|
|
2176
2772
|
const deleteConversation = useCallback(
|
|
2177
2773
|
(token) => {
|
|
@@ -2260,6 +2856,69 @@ function useExploreChat(options = {}) {
|
|
|
2260
2856
|
},
|
|
2261
2857
|
[client]
|
|
2262
2858
|
);
|
|
2859
|
+
const escalate = useCallback(
|
|
2860
|
+
async (email, note) => {
|
|
2861
|
+
const token = conversationTokenRef.current;
|
|
2862
|
+
if (!token) {
|
|
2863
|
+
return { outcome: "no-conversation", email: null, createdAt: null };
|
|
2864
|
+
}
|
|
2865
|
+
const attempt = async (captchaAttempt) => {
|
|
2866
|
+
const activeCatalog = catalogRef.current;
|
|
2867
|
+
let recaptchaToken;
|
|
2868
|
+
if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {
|
|
2869
|
+
try {
|
|
2870
|
+
recaptchaToken = await getRecaptchaV3Token(
|
|
2871
|
+
activeCatalog.recaptchaSiteKey
|
|
2872
|
+
);
|
|
2873
|
+
} catch (err) {
|
|
2874
|
+
console.warn(
|
|
2875
|
+
"[WebSDK] reCAPTCHA v3 token could not be minted",
|
|
2876
|
+
err
|
|
2877
|
+
);
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
const input = {
|
|
2881
|
+
conversationToken: token,
|
|
2882
|
+
email: email.trim(),
|
|
2883
|
+
note: note?.trim() ? note.trim() : null,
|
|
2884
|
+
...recaptchaToken ? { recaptchaToken } : {},
|
|
2885
|
+
...productId ? { productId } : {}
|
|
2886
|
+
};
|
|
2887
|
+
try {
|
|
2888
|
+
const res = await client.mutate(
|
|
2889
|
+
ESCALATE_EXPLORE_CONVERSATION_MUTATION,
|
|
2890
|
+
{ input }
|
|
2891
|
+
);
|
|
2892
|
+
const first = res?.errors?.[0];
|
|
2893
|
+
const result = res?.data?.escalateExploreConversation;
|
|
2894
|
+
if (first || !result) {
|
|
2895
|
+
const code = typeof first?.extensions?.code === "string" ? first.extensions.code : "ESCALATION_FAILED";
|
|
2896
|
+
if (code === "CAPTCHA_FAILED" && captchaAttempt === 0) {
|
|
2897
|
+
return attempt(1);
|
|
2898
|
+
}
|
|
2899
|
+
return {
|
|
2900
|
+
outcome: escalateOutcomeForCode(code),
|
|
2901
|
+
email: null,
|
|
2902
|
+
createdAt: null
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
return {
|
|
2906
|
+
// `created: false` is the double-click, the retry, the reload. The handover
|
|
2907
|
+
// exists, so this is a success — and the address comes from the SERVER, because
|
|
2908
|
+
// on a repeat submit the reply goes to whatever the FIRST handover used.
|
|
2909
|
+
outcome: result.created ? "sent" : "already-handed-over",
|
|
2910
|
+
email: result.escalation?.email ?? null,
|
|
2911
|
+
createdAt: result.escalation?.createdAt ?? null
|
|
2912
|
+
};
|
|
2913
|
+
} catch {
|
|
2914
|
+
return { outcome: "failed", email: null, createdAt: null };
|
|
2915
|
+
}
|
|
2916
|
+
};
|
|
2917
|
+
return attempt(0);
|
|
2918
|
+
},
|
|
2919
|
+
[client, productId]
|
|
2920
|
+
);
|
|
2921
|
+
const canEscalate = hasConversation && !isSharedView;
|
|
2263
2922
|
return {
|
|
2264
2923
|
phase,
|
|
2265
2924
|
catalog,
|
|
@@ -2280,6 +2939,8 @@ function useExploreChat(options = {}) {
|
|
|
2280
2939
|
clearHistory,
|
|
2281
2940
|
recordClick,
|
|
2282
2941
|
submitFeedback,
|
|
2942
|
+
escalate,
|
|
2943
|
+
canEscalate,
|
|
2283
2944
|
createShareLink,
|
|
2284
2945
|
isSharedView
|
|
2285
2946
|
};
|
|
@@ -2289,6 +2950,224 @@ function makeOptimisticId() {
|
|
|
2289
2950
|
optimisticCounter += 1;
|
|
2290
2951
|
return `boff-explore-local-${Date.now()}-${optimisticCounter}`;
|
|
2291
2952
|
}
|
|
2953
|
+
var DRAIN_TARGET_MS = 1200;
|
|
2954
|
+
var MIN_CHARS_PER_SECOND = 90;
|
|
2955
|
+
var MAX_CHARS_PER_SECOND = 2200;
|
|
2956
|
+
var MAX_STEP_MS = 250;
|
|
2957
|
+
var COMMIT_INTERVAL_MS = 45;
|
|
2958
|
+
var FALLBACK_STEP_MS = 50;
|
|
2959
|
+
var FENCE = /^ {0,3}(?:```|~~~)/;
|
|
2960
|
+
var TABLE_ROW = /^ {0,3}\|/;
|
|
2961
|
+
var EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\d{1,9}[.)])\s*$/;
|
|
2962
|
+
function isWordCharacter(char) {
|
|
2963
|
+
return char !== void 0 && /[\p{L}\p{N}]/u.test(char);
|
|
2964
|
+
}
|
|
2965
|
+
function clamp(value, min, max) {
|
|
2966
|
+
return Math.min(Math.max(value, min), max);
|
|
2967
|
+
}
|
|
2968
|
+
function now() {
|
|
2969
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
2970
|
+
}
|
|
2971
|
+
function isHighSurrogate(code) {
|
|
2972
|
+
return code >= 55296 && code <= 56319;
|
|
2973
|
+
}
|
|
2974
|
+
function safeInlineEnd(line) {
|
|
2975
|
+
let codeSpan = null;
|
|
2976
|
+
let link = null;
|
|
2977
|
+
const emphasis = [];
|
|
2978
|
+
let at = 0;
|
|
2979
|
+
while (at < line.length) {
|
|
2980
|
+
const char = line[at] ?? "";
|
|
2981
|
+
if (char === "\\") {
|
|
2982
|
+
at += 2;
|
|
2983
|
+
continue;
|
|
2984
|
+
}
|
|
2985
|
+
if (char === "`") {
|
|
2986
|
+
let run = 1;
|
|
2987
|
+
while (line[at + run] === "`") run += 1;
|
|
2988
|
+
codeSpan = codeSpan === null ? at : null;
|
|
2989
|
+
at += run;
|
|
2990
|
+
continue;
|
|
2991
|
+
}
|
|
2992
|
+
if (codeSpan !== null) {
|
|
2993
|
+
at += 1;
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2996
|
+
if (char === "[") {
|
|
2997
|
+
if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
|
|
2998
|
+
at += 1;
|
|
2999
|
+
continue;
|
|
3000
|
+
}
|
|
3001
|
+
if (char === "]") {
|
|
3002
|
+
if (link !== null && line[at + 1] !== "(") link = null;
|
|
3003
|
+
at += 1;
|
|
3004
|
+
continue;
|
|
3005
|
+
}
|
|
3006
|
+
if (char === ")") {
|
|
3007
|
+
link = null;
|
|
3008
|
+
at += 1;
|
|
3009
|
+
continue;
|
|
3010
|
+
}
|
|
3011
|
+
if (char === "*" || char === "_" || char === "~") {
|
|
3012
|
+
let run = 1;
|
|
3013
|
+
while (line[at + run] === char) run += 1;
|
|
3014
|
+
const start = at;
|
|
3015
|
+
at += run;
|
|
3016
|
+
const before = line[start - 1];
|
|
3017
|
+
const after = line[start + run];
|
|
3018
|
+
if (run < 2) continue;
|
|
3019
|
+
if (char === "_" && isWordCharacter(before) && isWordCharacter(after)) {
|
|
3020
|
+
continue;
|
|
3021
|
+
}
|
|
3022
|
+
const marker = char.repeat(run);
|
|
3023
|
+
const open2 = emphasis.findIndex((entry) => entry.marker === marker);
|
|
3024
|
+
if (open2 !== -1) {
|
|
3025
|
+
emphasis.splice(open2, 1);
|
|
3026
|
+
continue;
|
|
3027
|
+
}
|
|
3028
|
+
if (after !== void 0 && /\s/.test(after)) continue;
|
|
3029
|
+
emphasis.push({ marker, start });
|
|
3030
|
+
continue;
|
|
3031
|
+
}
|
|
3032
|
+
at += 1;
|
|
3033
|
+
}
|
|
3034
|
+
const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
|
|
3035
|
+
(value) => value !== null
|
|
3036
|
+
);
|
|
3037
|
+
return open.length === 0 ? line.length : Math.min(...open);
|
|
3038
|
+
}
|
|
3039
|
+
function revealBoundary(text, limit) {
|
|
3040
|
+
if (limit <= 0) return 0;
|
|
3041
|
+
const caughtUp = limit >= text.length;
|
|
3042
|
+
const head = caughtUp ? text : text.slice(0, limit);
|
|
3043
|
+
const lineStart = head.lastIndexOf("\n") + 1;
|
|
3044
|
+
const line = head.slice(lineStart);
|
|
3045
|
+
let fenceStart = null;
|
|
3046
|
+
let tableStart = null;
|
|
3047
|
+
let tableRows = 0;
|
|
3048
|
+
let at = 0;
|
|
3049
|
+
while (at < lineStart) {
|
|
3050
|
+
const breakAt = head.indexOf("\n", at);
|
|
3051
|
+
const end = breakAt === -1 ? lineStart : breakAt;
|
|
3052
|
+
const row = head.slice(at, end);
|
|
3053
|
+
if (fenceStart !== null) {
|
|
3054
|
+
if (FENCE.test(row)) fenceStart = null;
|
|
3055
|
+
} else if (FENCE.test(row)) {
|
|
3056
|
+
fenceStart = at;
|
|
3057
|
+
} else if (TABLE_ROW.test(row)) {
|
|
3058
|
+
if (tableStart === null) {
|
|
3059
|
+
tableStart = at;
|
|
3060
|
+
tableRows = 0;
|
|
3061
|
+
}
|
|
3062
|
+
tableRows += 1;
|
|
3063
|
+
} else if (tableStart !== null) {
|
|
3064
|
+
tableStart = null;
|
|
3065
|
+
tableRows = 0;
|
|
3066
|
+
}
|
|
3067
|
+
at = end + 1;
|
|
3068
|
+
}
|
|
3069
|
+
if (fenceStart === null) {
|
|
3070
|
+
const lastIsRow = TABLE_ROW.test(line);
|
|
3071
|
+
if (lastIsRow && tableStart === null) {
|
|
3072
|
+
tableStart = lineStart;
|
|
3073
|
+
tableRows = 0;
|
|
3074
|
+
} else if (!lastIsRow && line !== "") {
|
|
3075
|
+
tableStart = null;
|
|
3076
|
+
tableRows = 0;
|
|
3077
|
+
}
|
|
3078
|
+
if (tableStart !== null && tableRows < 2) return tableStart;
|
|
3079
|
+
}
|
|
3080
|
+
if (caughtUp) return text.length;
|
|
3081
|
+
if (fenceStart !== null) return fenceStart;
|
|
3082
|
+
if (TABLE_ROW.test(line)) return lineStart;
|
|
3083
|
+
if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;
|
|
3084
|
+
const cut = lineStart + safeInlineEnd(line);
|
|
3085
|
+
return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;
|
|
3086
|
+
}
|
|
3087
|
+
function prefersReducedMotion() {
|
|
3088
|
+
if (typeof window === "undefined") return true;
|
|
3089
|
+
try {
|
|
3090
|
+
return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
3091
|
+
} catch {
|
|
3092
|
+
return false;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
function useProgressiveReveal(text, settled) {
|
|
3096
|
+
const [, setFrame] = useState(0);
|
|
3097
|
+
const [reduced] = useState(prefersReducedMotion);
|
|
3098
|
+
const immediate = settled || reduced;
|
|
3099
|
+
const textRef = useRef(text);
|
|
3100
|
+
const cursorRef = useRef(immediate ? text.length : 0);
|
|
3101
|
+
const cutRef = useRef(immediate ? text.length : 0);
|
|
3102
|
+
const committedRef = useRef(0);
|
|
3103
|
+
if (text !== textRef.current) {
|
|
3104
|
+
const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));
|
|
3105
|
+
if (revealed !== "" && !text.startsWith(revealed)) {
|
|
3106
|
+
cursorRef.current = text.length;
|
|
3107
|
+
}
|
|
3108
|
+
cursorRef.current = Math.min(cursorRef.current, text.length);
|
|
3109
|
+
cutRef.current = Math.min(cutRef.current, text.length);
|
|
3110
|
+
textRef.current = text;
|
|
3111
|
+
}
|
|
3112
|
+
if (immediate) cursorRef.current = text.length;
|
|
3113
|
+
const cut = immediate ? text.length : Math.max(
|
|
3114
|
+
cutRef.current,
|
|
3115
|
+
revealBoundary(text, Math.floor(cursorRef.current))
|
|
3116
|
+
);
|
|
3117
|
+
cutRef.current = cut;
|
|
3118
|
+
useEffect(() => {
|
|
3119
|
+
if (immediate) return;
|
|
3120
|
+
if (cursorRef.current >= text.length) return;
|
|
3121
|
+
let cancelled = false;
|
|
3122
|
+
let cancelStep = null;
|
|
3123
|
+
let previous = now();
|
|
3124
|
+
const commit = (at, force) => {
|
|
3125
|
+
if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;
|
|
3126
|
+
committedRef.current = at;
|
|
3127
|
+
setFrame((value) => value + 1);
|
|
3128
|
+
};
|
|
3129
|
+
const step = () => {
|
|
3130
|
+
cancelStep = null;
|
|
3131
|
+
if (cancelled) return;
|
|
3132
|
+
const at = now();
|
|
3133
|
+
const elapsed = clamp(at - previous, 0, MAX_STEP_MS);
|
|
3134
|
+
previous = at;
|
|
3135
|
+
const length = textRef.current.length;
|
|
3136
|
+
const pending = length - cursorRef.current;
|
|
3137
|
+
if (pending <= 0) {
|
|
3138
|
+
commit(at, true);
|
|
3139
|
+
return;
|
|
3140
|
+
}
|
|
3141
|
+
const perSecond = clamp(
|
|
3142
|
+
pending / (DRAIN_TARGET_MS / 1e3),
|
|
3143
|
+
MIN_CHARS_PER_SECOND,
|
|
3144
|
+
MAX_CHARS_PER_SECOND
|
|
3145
|
+
);
|
|
3146
|
+
cursorRef.current = Math.min(
|
|
3147
|
+
length,
|
|
3148
|
+
cursorRef.current + perSecond * elapsed / 1e3
|
|
3149
|
+
);
|
|
3150
|
+
const done = cursorRef.current >= length;
|
|
3151
|
+
commit(at, done);
|
|
3152
|
+
if (!done) schedule();
|
|
3153
|
+
};
|
|
3154
|
+
const schedule = () => {
|
|
3155
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
3156
|
+
const id2 = window.requestAnimationFrame(step);
|
|
3157
|
+
cancelStep = () => window.cancelAnimationFrame(id2);
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
3160
|
+
const id = setTimeout(step, FALLBACK_STEP_MS);
|
|
3161
|
+
cancelStep = () => clearTimeout(id);
|
|
3162
|
+
};
|
|
3163
|
+
schedule();
|
|
3164
|
+
return () => {
|
|
3165
|
+
cancelled = true;
|
|
3166
|
+
cancelStep?.();
|
|
3167
|
+
};
|
|
3168
|
+
}, [text, immediate]);
|
|
3169
|
+
return text.slice(0, cut);
|
|
3170
|
+
}
|
|
2292
3171
|
function getRecognitionCtor() {
|
|
2293
3172
|
if (typeof window === "undefined") return null;
|
|
2294
3173
|
const w = window;
|
|
@@ -2507,6 +3386,141 @@ function useSpeechInput({
|
|
|
2507
3386
|
);
|
|
2508
3387
|
return { supported, listening, interim, error, start, stop, toggle };
|
|
2509
3388
|
}
|
|
3389
|
+
var SILENT_FAILURE_MS = 1500;
|
|
3390
|
+
function speechPlatform() {
|
|
3391
|
+
if (typeof window === "undefined") return null;
|
|
3392
|
+
const scope = window;
|
|
3393
|
+
const synth = scope.speechSynthesis;
|
|
3394
|
+
const Utterance = scope.SpeechSynthesisUtterance;
|
|
3395
|
+
if (!synth || typeof synth.speak !== "function") return null;
|
|
3396
|
+
if (typeof Utterance !== "function") return null;
|
|
3397
|
+
return { synth, Utterance };
|
|
3398
|
+
}
|
|
3399
|
+
function normaliseTag(tag) {
|
|
3400
|
+
return tag.trim().toLowerCase().replace(/_/g, "-");
|
|
3401
|
+
}
|
|
3402
|
+
function primaryOf(tag) {
|
|
3403
|
+
return normaliseTag(tag).split("-")[0] ?? "";
|
|
3404
|
+
}
|
|
3405
|
+
function pickVoice(voices, lang) {
|
|
3406
|
+
const wanted = normaliseTag(lang);
|
|
3407
|
+
if (wanted === "") return null;
|
|
3408
|
+
const primary = primaryOf(wanted);
|
|
3409
|
+
const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);
|
|
3410
|
+
const related = voices.filter((voice) => primaryOf(voice.lang) === primary);
|
|
3411
|
+
return exact.find((voice) => voice.default) ?? exact[0] ?? related.find((voice) => voice.default) ?? related[0] ?? null;
|
|
3412
|
+
}
|
|
3413
|
+
var activeOwner = null;
|
|
3414
|
+
var activeStop = null;
|
|
3415
|
+
function stopSpeaking() {
|
|
3416
|
+
activeStop?.();
|
|
3417
|
+
}
|
|
3418
|
+
function useSpeechOutput({
|
|
3419
|
+
lang
|
|
3420
|
+
} = {}) {
|
|
3421
|
+
const [available, setAvailable] = useState(() => speechPlatform() !== null);
|
|
3422
|
+
const [speaking, setSpeaking] = useState(false);
|
|
3423
|
+
const utteranceRef = useRef(null);
|
|
3424
|
+
const watchdogRef = useRef(null);
|
|
3425
|
+
const ownerRef = useRef({});
|
|
3426
|
+
const stopRef = useRef(() => void 0);
|
|
3427
|
+
const langRef = useRef(lang);
|
|
3428
|
+
langRef.current = lang;
|
|
3429
|
+
const clearWatchdog = useCallback(() => {
|
|
3430
|
+
if (watchdogRef.current === null) return;
|
|
3431
|
+
clearTimeout(watchdogRef.current);
|
|
3432
|
+
watchdogRef.current = null;
|
|
3433
|
+
}, []);
|
|
3434
|
+
const settle = useCallback(() => {
|
|
3435
|
+
clearWatchdog();
|
|
3436
|
+
utteranceRef.current = null;
|
|
3437
|
+
if (activeOwner === ownerRef.current) {
|
|
3438
|
+
activeOwner = null;
|
|
3439
|
+
activeStop = null;
|
|
3440
|
+
}
|
|
3441
|
+
setSpeaking(false);
|
|
3442
|
+
}, [clearWatchdog]);
|
|
3443
|
+
const stop = useCallback(() => {
|
|
3444
|
+
const owned = utteranceRef.current !== null;
|
|
3445
|
+
settle();
|
|
3446
|
+
if (owned) speechPlatform()?.synth.cancel();
|
|
3447
|
+
}, [settle]);
|
|
3448
|
+
stopRef.current = stop;
|
|
3449
|
+
const speak = useCallback(
|
|
3450
|
+
(text) => {
|
|
3451
|
+
const platform = speechPlatform();
|
|
3452
|
+
if (!platform) {
|
|
3453
|
+
setAvailable(false);
|
|
3454
|
+
return;
|
|
3455
|
+
}
|
|
3456
|
+
const spoken = text.trim();
|
|
3457
|
+
if (spoken === "") return;
|
|
3458
|
+
if (activeStop && activeOwner !== ownerRef.current) activeStop();
|
|
3459
|
+
platform.synth.cancel();
|
|
3460
|
+
const utterance = new platform.Utterance(spoken);
|
|
3461
|
+
const wanted = langRef.current;
|
|
3462
|
+
if (wanted !== void 0 && wanted !== "") {
|
|
3463
|
+
const voice = pickVoice(platform.synth.getVoices(), wanted);
|
|
3464
|
+
utterance.lang = voice?.lang ?? wanted;
|
|
3465
|
+
if (voice) utterance.voice = voice;
|
|
3466
|
+
}
|
|
3467
|
+
const mine = () => utteranceRef.current === utterance;
|
|
3468
|
+
utterance.onstart = () => {
|
|
3469
|
+
if (!mine()) return;
|
|
3470
|
+
clearWatchdog();
|
|
3471
|
+
setSpeaking(true);
|
|
3472
|
+
};
|
|
3473
|
+
utterance.onend = () => {
|
|
3474
|
+
if (!mine()) return;
|
|
3475
|
+
settle();
|
|
3476
|
+
};
|
|
3477
|
+
utterance.onerror = (event) => {
|
|
3478
|
+
if (!mine()) return;
|
|
3479
|
+
const code = typeof event?.error === "string" ? event.error : "";
|
|
3480
|
+
settle();
|
|
3481
|
+
if (code !== "interrupted" && code !== "canceled") setAvailable(false);
|
|
3482
|
+
};
|
|
3483
|
+
utteranceRef.current = utterance;
|
|
3484
|
+
activeOwner = ownerRef.current;
|
|
3485
|
+
activeStop = () => {
|
|
3486
|
+
stopRef.current();
|
|
3487
|
+
};
|
|
3488
|
+
setSpeaking(true);
|
|
3489
|
+
platform.synth.speak(utterance);
|
|
3490
|
+
clearWatchdog();
|
|
3491
|
+
watchdogRef.current = setTimeout(() => {
|
|
3492
|
+
watchdogRef.current = null;
|
|
3493
|
+
if (!mine()) return;
|
|
3494
|
+
if (platform.synth.speaking) return;
|
|
3495
|
+
settle();
|
|
3496
|
+
setAvailable(false);
|
|
3497
|
+
}, SILENT_FAILURE_MS);
|
|
3498
|
+
},
|
|
3499
|
+
[clearWatchdog, settle]
|
|
3500
|
+
);
|
|
3501
|
+
const toggle = useCallback(
|
|
3502
|
+
(text) => {
|
|
3503
|
+
if (speaking) stop();
|
|
3504
|
+
else speak(text);
|
|
3505
|
+
},
|
|
3506
|
+
[speak, speaking, stop]
|
|
3507
|
+
);
|
|
3508
|
+
useEffect(() => {
|
|
3509
|
+
if (!speaking || typeof window === "undefined") return;
|
|
3510
|
+
const onHide = () => {
|
|
3511
|
+
stopRef.current();
|
|
3512
|
+
};
|
|
3513
|
+
window.addEventListener("pagehide", onHide);
|
|
3514
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
3515
|
+
}, [speaking]);
|
|
3516
|
+
useEffect(
|
|
3517
|
+
() => () => {
|
|
3518
|
+
stopRef.current();
|
|
3519
|
+
},
|
|
3520
|
+
[]
|
|
3521
|
+
);
|
|
3522
|
+
return { supported: available, speaking, speak, stop, toggle };
|
|
3523
|
+
}
|
|
2510
3524
|
var SWIPE_THRESHOLD = 40;
|
|
2511
3525
|
function useSwipe({
|
|
2512
3526
|
onSwipeLeft,
|
|
@@ -2612,9 +3626,6 @@ function PageHead({
|
|
|
2612
3626
|
))
|
|
2613
3627
|
] });
|
|
2614
3628
|
}
|
|
2615
|
-
function cn(...inputs) {
|
|
2616
|
-
return twMerge(clsx(inputs));
|
|
2617
|
-
}
|
|
2618
3629
|
var buttonVariants = cva(
|
|
2619
3630
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
2620
3631
|
{
|
|
@@ -2872,34 +3883,7 @@ var EXPLORE_CSS = `
|
|
|
2872
3883
|
}
|
|
2873
3884
|
.boff-explore-fade { animation: boff-explore-fade-in 220ms ease-out; }
|
|
2874
3885
|
@media (prefers-reduced-motion: reduce) {
|
|
2875
|
-
.boff-explore-fade { animation: none; }
|
|
2876
|
-
}
|
|
2877
|
-
|
|
2878
|
-
/* Nothing inside an answer may widen the page. Model output carries long URLs, code and
|
|
2879
|
-
tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
|
|
2880
|
-
WHOLE document horizontally scrollable, not just the bubble. */
|
|
2881
|
-
.boff-explore-body,
|
|
2882
|
-
.boff-explore-body p,
|
|
2883
|
-
.boff-explore-body li,
|
|
2884
|
-
.boff-explore-body a,
|
|
2885
|
-
.boff-explore-body h1,
|
|
2886
|
-
.boff-explore-body h2,
|
|
2887
|
-
.boff-explore-body h3 {
|
|
2888
|
-
overflow-wrap: anywhere;
|
|
2889
|
-
word-break: break-word;
|
|
2890
|
-
}
|
|
2891
|
-
.boff-explore-body pre {
|
|
2892
|
-
overflow-x: auto;
|
|
2893
|
-
max-width: 100%;
|
|
2894
|
-
}
|
|
2895
|
-
.boff-explore-body table {
|
|
2896
|
-
display: block;
|
|
2897
|
-
overflow-x: auto;
|
|
2898
|
-
max-width: 100%;
|
|
2899
|
-
}
|
|
2900
|
-
.boff-explore-body img {
|
|
2901
|
-
max-width: 100%;
|
|
2902
|
-
height: auto;
|
|
3886
|
+
.boff-explore-fade { animation: none; }
|
|
2903
3887
|
}
|
|
2904
3888
|
|
|
2905
3889
|
/* ---- Ambient backdrop -----------------------------------------------------------------
|
|
@@ -2958,11 +3942,40 @@ var EXPLORE_CSS = `
|
|
|
2958
3942
|
animation: boff-explore-sheen 1.5s ease-in-out infinite;
|
|
2959
3943
|
}
|
|
2960
3944
|
|
|
3945
|
+
/* ---- The composer, before anyone has typed -----------------------------------------
|
|
3946
|
+
On the welcome screen the box IS the page: everything above it is a way of explaining
|
|
3947
|
+
what to put in it. Unhighlighted it reads as a search bar at the bottom of a landing
|
|
3948
|
+
page, which people scroll past \u2014 so while the conversation is empty it gets a soft halo
|
|
3949
|
+
in the site's own --primary and one slow breath of it, and the moment there is a
|
|
3950
|
+
transcript, or the visitor focuses or types, it drops back to the ordinary card it was.
|
|
3951
|
+
An input that keeps pulsing while you are using it is nagging, not inviting.
|
|
3952
|
+
|
|
3953
|
+
Colour comes from the product's --primary token, so it is whatever that site's palette
|
|
3954
|
+
already is, never a colour chosen here. */
|
|
3955
|
+
.boff-explore-invite {
|
|
3956
|
+
border-color: hsl(var(--primary) / 0.45);
|
|
3957
|
+
box-shadow:
|
|
3958
|
+
0 0 0 4px hsl(var(--primary) / 0.08),
|
|
3959
|
+
0 8px 24px -12px hsl(var(--primary) / 0.45);
|
|
3960
|
+
animation: boff-explore-breathe 3.2s ease-in-out infinite;
|
|
3961
|
+
}
|
|
3962
|
+
/* Focus and typing both mean "I have found it", and the hint has done its job. */
|
|
3963
|
+
.boff-explore-invite:focus-within,
|
|
3964
|
+
.boff-explore-invite.boff-explore-invite-quiet {
|
|
3965
|
+
animation: none;
|
|
3966
|
+
box-shadow: 0 0 0 4px hsl(var(--primary) / 0.10);
|
|
3967
|
+
}
|
|
3968
|
+
@keyframes boff-explore-breathe {
|
|
3969
|
+
0%, 100% { box-shadow: 0 0 0 4px hsl(var(--primary) / 0.07), 0 8px 24px -12px hsl(var(--primary) / 0.40); }
|
|
3970
|
+
50% { box-shadow: 0 0 0 7px hsl(var(--primary) / 0.13), 0 8px 28px -12px hsl(var(--primary) / 0.55); }
|
|
3971
|
+
}
|
|
3972
|
+
|
|
2961
3973
|
/* Every one of the above is decoration. Reduced motion removes the movement and keeps the
|
|
2962
3974
|
final state, rather than leaving content mid-animation and invisible. */
|
|
2963
3975
|
@media (prefers-reduced-motion: reduce) {
|
|
2964
3976
|
.boff-explore-aura::before,
|
|
2965
|
-
.boff-explore-busy::after
|
|
3977
|
+
.boff-explore-busy::after,
|
|
3978
|
+
.boff-explore-invite { animation: none; }
|
|
2966
3979
|
.boff-explore-rise { animation: none; opacity: 1; transform: none; }
|
|
2967
3980
|
}
|
|
2968
3981
|
|
|
@@ -3073,7 +4086,7 @@ function isOwnElement(el, root) {
|
|
|
3073
4086
|
function visualViewportHeight() {
|
|
3074
4087
|
return window.visualViewport?.height ?? window.innerHeight;
|
|
3075
4088
|
}
|
|
3076
|
-
function
|
|
4089
|
+
function clamp2(value, min, max) {
|
|
3077
4090
|
return Math.min(Math.max(value, min), max);
|
|
3078
4091
|
}
|
|
3079
4092
|
function isRestoredNavigation() {
|
|
@@ -3110,34 +4123,6 @@ function logoUrlOf(url) {
|
|
|
3110
4123
|
return null;
|
|
3111
4124
|
}
|
|
3112
4125
|
}
|
|
3113
|
-
function inlineImageOrigins(products) {
|
|
3114
|
-
const origins = /* @__PURE__ */ new Set();
|
|
3115
|
-
for (const product of products) {
|
|
3116
|
-
let url;
|
|
3117
|
-
try {
|
|
3118
|
-
url = new URL(product.website);
|
|
3119
|
-
} catch {
|
|
3120
|
-
continue;
|
|
3121
|
-
}
|
|
3122
|
-
if (url.protocol !== "https:") continue;
|
|
3123
|
-
const apex = url.hostname.replace(/^www\./, "");
|
|
3124
|
-
const port = url.port ? `:${url.port}` : "";
|
|
3125
|
-
origins.add(`https://${apex}${port}`);
|
|
3126
|
-
origins.add(`https://www.${apex}${port}`);
|
|
3127
|
-
}
|
|
3128
|
-
return origins;
|
|
3129
|
-
}
|
|
3130
|
-
function isInlineImageAllowed(src, origins) {
|
|
3131
|
-
if (typeof src !== "string" || src === "") return false;
|
|
3132
|
-
let url;
|
|
3133
|
-
try {
|
|
3134
|
-
url = new URL(src);
|
|
3135
|
-
} catch {
|
|
3136
|
-
return false;
|
|
3137
|
-
}
|
|
3138
|
-
if (url.protocol !== "https:" || url.username || url.password) return false;
|
|
3139
|
-
return origins.has(url.origin);
|
|
3140
|
-
}
|
|
3141
4126
|
var REFERENCE_KIND_ORDER = [
|
|
3142
4127
|
"CONCEPT",
|
|
3143
4128
|
"DOCS",
|
|
@@ -3222,97 +4207,6 @@ function summariseReferences(references, origins, productNames) {
|
|
|
3222
4207
|
products
|
|
3223
4208
|
};
|
|
3224
4209
|
}
|
|
3225
|
-
var CODE_FENCE = /^\s*(?:```|~~~)/;
|
|
3226
|
-
function answerPlainText(markdown) {
|
|
3227
|
-
const lines = [];
|
|
3228
|
-
let inFence = false;
|
|
3229
|
-
for (const raw of markdown.split("\n")) {
|
|
3230
|
-
if (CODE_FENCE.test(raw)) {
|
|
3231
|
-
inFence = !inFence;
|
|
3232
|
-
continue;
|
|
3233
|
-
}
|
|
3234
|
-
if (inFence) {
|
|
3235
|
-
lines.push(raw);
|
|
3236
|
-
continue;
|
|
3237
|
-
}
|
|
3238
|
-
let line = raw;
|
|
3239
|
-
line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
|
|
3240
|
-
line = line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
|
|
3241
|
-
line = line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
|
|
3242
|
-
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
3243
|
-
line = line.replace(/^\s{0,3}>\s?/, "");
|
|
3244
|
-
if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) {
|
|
3245
|
-
lines.push("");
|
|
3246
|
-
continue;
|
|
3247
|
-
}
|
|
3248
|
-
line = line.replace(/`([^`]+)`/g, "$1");
|
|
3249
|
-
line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
3250
|
-
line = line.replace(/__([^_]+)__/g, "$1");
|
|
3251
|
-
line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
|
|
3252
|
-
line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
|
|
3253
|
-
lines.push(line.replace(/[ \t]+$/, ""));
|
|
3254
|
-
}
|
|
3255
|
-
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
3256
|
-
}
|
|
3257
|
-
var InlineImageScopeContext = createContext(null);
|
|
3258
|
-
var InsideLinkContext = createContext(false);
|
|
3259
|
-
function AnswerLink(props) {
|
|
3260
|
-
return /* @__PURE__ */ jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx(MarkdownLink, { ...props }) });
|
|
3261
|
-
}
|
|
3262
|
-
var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
|
|
3263
|
-
function InlineConceptImage({ src, alt }) {
|
|
3264
|
-
const scope = useContext(InlineImageScopeContext);
|
|
3265
|
-
const insideLink = useContext(InsideLinkContext);
|
|
3266
|
-
if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
|
|
3267
|
-
return null;
|
|
3268
|
-
}
|
|
3269
|
-
const { strings: T, onFailed } = scope;
|
|
3270
|
-
const picture = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3271
|
-
/* @__PURE__ */ jsx(
|
|
3272
|
-
"img",
|
|
3273
|
-
{
|
|
3274
|
-
src,
|
|
3275
|
-
alt: alt ?? "",
|
|
3276
|
-
loading: "lazy",
|
|
3277
|
-
decoding: "async",
|
|
3278
|
-
onError: () => onFailed(src),
|
|
3279
|
-
className: "block h-auto max-h-80 w-auto max-w-full object-contain"
|
|
3280
|
-
}
|
|
3281
|
-
),
|
|
3282
|
-
/* @__PURE__ */ jsx(
|
|
3283
|
-
"span",
|
|
3284
|
-
{
|
|
3285
|
-
"aria-hidden": "true",
|
|
3286
|
-
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",
|
|
3287
|
-
children: T.conceptIllustration
|
|
3288
|
-
}
|
|
3289
|
-
)
|
|
3290
|
-
] });
|
|
3291
|
-
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(
|
|
3292
|
-
"a",
|
|
3293
|
-
{
|
|
3294
|
-
href: src,
|
|
3295
|
-
target: "_blank",
|
|
3296
|
-
rel: "noopener noreferrer",
|
|
3297
|
-
className: cn(
|
|
3298
|
-
INLINE_IMAGE_FRAME,
|
|
3299
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
3300
|
-
),
|
|
3301
|
-
children: [
|
|
3302
|
-
picture,
|
|
3303
|
-
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
3304
|
-
"(",
|
|
3305
|
-
T.opensInNewTab,
|
|
3306
|
-
")"
|
|
3307
|
-
] })
|
|
3308
|
-
]
|
|
3309
|
-
}
|
|
3310
|
-
) });
|
|
3311
|
-
}
|
|
3312
|
-
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
3313
|
-
a: AnswerLink,
|
|
3314
|
-
img: InlineConceptImage
|
|
3315
|
-
};
|
|
3316
4210
|
function ReferenceCard({
|
|
3317
4211
|
reference,
|
|
3318
4212
|
onFollow
|
|
@@ -3651,6 +4545,195 @@ function AnswerFeedback({
|
|
|
3651
4545
|
}
|
|
3652
4546
|
);
|
|
3653
4547
|
}
|
|
4548
|
+
function EscalateForm({
|
|
4549
|
+
strings: T,
|
|
4550
|
+
formId,
|
|
4551
|
+
onEscalate,
|
|
4552
|
+
onClose,
|
|
4553
|
+
onBusyChange
|
|
4554
|
+
}) {
|
|
4555
|
+
const [email, setEmail] = useState("");
|
|
4556
|
+
const [note, setNote] = useState("");
|
|
4557
|
+
const [error, setError] = useState(null);
|
|
4558
|
+
const [sending, setSending] = useState(false);
|
|
4559
|
+
const [sentTo, setSentTo] = useState(null);
|
|
4560
|
+
const emailRef = useRef(null);
|
|
4561
|
+
const inFlightRef = useRef(false);
|
|
4562
|
+
const titleId = `${formId}-title`;
|
|
4563
|
+
const emailId = `${formId}-email`;
|
|
4564
|
+
const noteId = `${formId}-note`;
|
|
4565
|
+
const errorId = `${formId}-error`;
|
|
4566
|
+
useEffect(() => {
|
|
4567
|
+
emailRef.current?.focus();
|
|
4568
|
+
}, []);
|
|
4569
|
+
useEffect(() => {
|
|
4570
|
+
onBusyChange?.(sending);
|
|
4571
|
+
return () => onBusyChange?.(false);
|
|
4572
|
+
}, [sending, onBusyChange]);
|
|
4573
|
+
const handleSubmit = useCallback(
|
|
4574
|
+
async (event) => {
|
|
4575
|
+
event.preventDefault();
|
|
4576
|
+
if (inFlightRef.current) return;
|
|
4577
|
+
const address = email.trim();
|
|
4578
|
+
if (!looksLikeDeliverableEmail(address)) {
|
|
4579
|
+
setError(T.escalateInvalidEmail);
|
|
4580
|
+
emailRef.current?.focus();
|
|
4581
|
+
return;
|
|
4582
|
+
}
|
|
4583
|
+
inFlightRef.current = true;
|
|
4584
|
+
setSending(true);
|
|
4585
|
+
setError(null);
|
|
4586
|
+
const result = await onEscalate(address, note);
|
|
4587
|
+
inFlightRef.current = false;
|
|
4588
|
+
setSending(false);
|
|
4589
|
+
switch (result.outcome) {
|
|
4590
|
+
case "sent":
|
|
4591
|
+
case "already-handed-over":
|
|
4592
|
+
setSentTo(result.email ?? address);
|
|
4593
|
+
return;
|
|
4594
|
+
case "invalid-email":
|
|
4595
|
+
setError(T.escalateInvalidEmail);
|
|
4596
|
+
emailRef.current?.focus();
|
|
4597
|
+
return;
|
|
4598
|
+
case "rate-limited":
|
|
4599
|
+
setError(T.escalateRateLimited);
|
|
4600
|
+
return;
|
|
4601
|
+
case "captcha":
|
|
4602
|
+
setError(T.escalateCaptcha);
|
|
4603
|
+
return;
|
|
4604
|
+
// `no-conversation` included: the form should not have been offered at all, and the
|
|
4605
|
+
// honest thing to show is that nothing was passed on.
|
|
4606
|
+
default:
|
|
4607
|
+
setError(T.escalateFailed);
|
|
4608
|
+
return;
|
|
4609
|
+
}
|
|
4610
|
+
},
|
|
4611
|
+
[T, email, note, onEscalate]
|
|
4612
|
+
);
|
|
4613
|
+
if (sentTo !== null) {
|
|
4614
|
+
return /* @__PURE__ */ jsx(
|
|
4615
|
+
"p",
|
|
4616
|
+
{
|
|
4617
|
+
id: formId,
|
|
4618
|
+
"data-boff-explore": "escalate-sent",
|
|
4619
|
+
role: "status",
|
|
4620
|
+
className: "boff-explore-fade mt-2 rounded-xl border border-border bg-card px-3 py-2 text-xs leading-relaxed text-card-foreground",
|
|
4621
|
+
children: fill(T.escalateSent, { email: sentTo })
|
|
4622
|
+
}
|
|
4623
|
+
);
|
|
4624
|
+
}
|
|
4625
|
+
return /* @__PURE__ */ jsxs(
|
|
4626
|
+
"form",
|
|
4627
|
+
{
|
|
4628
|
+
id: formId,
|
|
4629
|
+
"data-boff-explore": "escalate-form",
|
|
4630
|
+
"aria-labelledby": titleId,
|
|
4631
|
+
noValidate: true,
|
|
4632
|
+
onSubmit: handleSubmit,
|
|
4633
|
+
onKeyDown: (event) => {
|
|
4634
|
+
if (event.key !== "Escape") return;
|
|
4635
|
+
event.stopPropagation();
|
|
4636
|
+
if (sending) return;
|
|
4637
|
+
onClose();
|
|
4638
|
+
},
|
|
4639
|
+
className: "boff-explore-fade mt-2 space-y-2 rounded-xl border border-border bg-card p-3",
|
|
4640
|
+
children: [
|
|
4641
|
+
/* @__PURE__ */ jsx("p", { id: titleId, className: "text-xs font-medium text-card-foreground", children: T.escalateTitle }),
|
|
4642
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: T.escalateBody }),
|
|
4643
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
4644
|
+
/* @__PURE__ */ jsx(
|
|
4645
|
+
"label",
|
|
4646
|
+
{
|
|
4647
|
+
htmlFor: emailId,
|
|
4648
|
+
className: "block text-xs font-medium text-card-foreground",
|
|
4649
|
+
children: T.escalateEmailLabel
|
|
4650
|
+
}
|
|
4651
|
+
),
|
|
4652
|
+
/* @__PURE__ */ jsx(
|
|
4653
|
+
"input",
|
|
4654
|
+
{
|
|
4655
|
+
ref: emailRef,
|
|
4656
|
+
id: emailId,
|
|
4657
|
+
name: "email",
|
|
4658
|
+
"data-boff-explore": "escalate-email",
|
|
4659
|
+
type: "email",
|
|
4660
|
+
inputMode: "email",
|
|
4661
|
+
autoComplete: "email",
|
|
4662
|
+
required: true,
|
|
4663
|
+
value: email,
|
|
4664
|
+
onChange: (event) => {
|
|
4665
|
+
setEmail(event.target.value);
|
|
4666
|
+
if (error) setError(null);
|
|
4667
|
+
},
|
|
4668
|
+
"aria-invalid": error ? "true" : void 0,
|
|
4669
|
+
"aria-describedby": error ? errorId : void 0,
|
|
4670
|
+
placeholder: T.escalateEmailPlaceholder,
|
|
4671
|
+
className: "w-full rounded-md border border-border bg-background px-2 py-1 text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:text-sm"
|
|
4672
|
+
}
|
|
4673
|
+
)
|
|
4674
|
+
] }),
|
|
4675
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
4676
|
+
/* @__PURE__ */ jsx(
|
|
4677
|
+
"label",
|
|
4678
|
+
{
|
|
4679
|
+
htmlFor: noteId,
|
|
4680
|
+
className: "block text-xs font-medium text-card-foreground",
|
|
4681
|
+
children: T.escalateNoteLabel
|
|
4682
|
+
}
|
|
4683
|
+
),
|
|
4684
|
+
/* @__PURE__ */ jsx(
|
|
4685
|
+
Textarea,
|
|
4686
|
+
{
|
|
4687
|
+
id: noteId,
|
|
4688
|
+
name: "note",
|
|
4689
|
+
"data-boff-explore": "escalate-note",
|
|
4690
|
+
rows: 2,
|
|
4691
|
+
value: note,
|
|
4692
|
+
onChange: (event) => setNote(event.target.value),
|
|
4693
|
+
maxLength: EXPLORE_ESCALATION_NOTE_SOFT_MAX,
|
|
4694
|
+
className: "min-h-0 border-border bg-background"
|
|
4695
|
+
}
|
|
4696
|
+
)
|
|
4697
|
+
] }),
|
|
4698
|
+
error ? /* @__PURE__ */ jsx(
|
|
4699
|
+
"p",
|
|
4700
|
+
{
|
|
4701
|
+
id: errorId,
|
|
4702
|
+
"data-boff-explore": "escalate-error",
|
|
4703
|
+
role: "alert",
|
|
4704
|
+
className: "text-xs leading-relaxed text-destructive",
|
|
4705
|
+
children: error
|
|
4706
|
+
}
|
|
4707
|
+
) : null,
|
|
4708
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
4709
|
+
/* @__PURE__ */ jsx(
|
|
4710
|
+
Button,
|
|
4711
|
+
{
|
|
4712
|
+
"data-boff-explore": "escalate-send",
|
|
4713
|
+
type: "submit",
|
|
4714
|
+
size: "sm",
|
|
4715
|
+
disabled: sending,
|
|
4716
|
+
children: sending ? T.escalateSending : T.escalateSubmit
|
|
4717
|
+
}
|
|
4718
|
+
),
|
|
4719
|
+
/* @__PURE__ */ jsx(
|
|
4720
|
+
Button,
|
|
4721
|
+
{
|
|
4722
|
+
"data-boff-explore": "escalate-cancel",
|
|
4723
|
+
type: "button",
|
|
4724
|
+
size: "sm",
|
|
4725
|
+
variant: "ghost",
|
|
4726
|
+
className: "text-muted-foreground",
|
|
4727
|
+
disabled: sending,
|
|
4728
|
+
onClick: onClose,
|
|
4729
|
+
children: T.escalateCancel
|
|
4730
|
+
}
|
|
4731
|
+
)
|
|
4732
|
+
] })
|
|
4733
|
+
]
|
|
4734
|
+
}
|
|
4735
|
+
);
|
|
4736
|
+
}
|
|
3654
4737
|
var PROVENANCE_CHIP = "inline-flex max-w-full items-center gap-1 rounded-full border border-border bg-muted/60 px-2 py-0.5 text-[11px] leading-5 text-muted-foreground";
|
|
3655
4738
|
function ProvenanceChips({
|
|
3656
4739
|
strings: T,
|
|
@@ -3700,9 +4783,42 @@ function ProvenanceChips({
|
|
|
3700
4783
|
);
|
|
3701
4784
|
}
|
|
3702
4785
|
var COPY_FEEDBACK_MS = 2500;
|
|
4786
|
+
var ANSWER_ACTION_PILL = "inline-flex max-w-full items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
|
|
4787
|
+
var ANSWER_ACTION_IDLE = "border-border bg-card text-muted-foreground hover:border-primary/40 hover:bg-accent hover:text-foreground";
|
|
4788
|
+
var ANSWER_ACTION_ACTIVE = "border-primary/40 bg-accent text-primary";
|
|
4789
|
+
function ReadAloudButton({
|
|
4790
|
+
strings: T,
|
|
4791
|
+
text,
|
|
4792
|
+
lang
|
|
4793
|
+
}) {
|
|
4794
|
+
const speech = useSpeechOutput({ lang });
|
|
4795
|
+
if (!speech.supported) return null;
|
|
4796
|
+
const label = speech.speaking ? T.stopReading : T.readAloud;
|
|
4797
|
+
return /* @__PURE__ */ jsxs(
|
|
4798
|
+
"button",
|
|
4799
|
+
{
|
|
4800
|
+
"data-boff-explore": "read-aloud",
|
|
4801
|
+
"data-speaking": speech.speaking ? "true" : "false",
|
|
4802
|
+
type: "button",
|
|
4803
|
+
title: label,
|
|
4804
|
+
"aria-pressed": speech.speaking,
|
|
4805
|
+
onClick: () => speech.toggle(text),
|
|
4806
|
+
className: cn(
|
|
4807
|
+
ANSWER_ACTION_PILL,
|
|
4808
|
+
speech.speaking ? ANSWER_ACTION_ACTIVE : ANSWER_ACTION_IDLE
|
|
4809
|
+
),
|
|
4810
|
+
children: [
|
|
4811
|
+
/* @__PURE__ */ jsx(Volume2, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
4812
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: label })
|
|
4813
|
+
]
|
|
4814
|
+
}
|
|
4815
|
+
);
|
|
4816
|
+
}
|
|
3703
4817
|
function AnswerActions({
|
|
3704
4818
|
strings: T,
|
|
3705
|
-
text
|
|
4819
|
+
text,
|
|
4820
|
+
speechText,
|
|
4821
|
+
speechLang
|
|
3706
4822
|
}) {
|
|
3707
4823
|
const [outcome, setOutcome] = useState("idle");
|
|
3708
4824
|
const resetRef = useRef(null);
|
|
@@ -3745,9 +4861,8 @@ function AnswerActions({
|
|
|
3745
4861
|
void copy();
|
|
3746
4862
|
},
|
|
3747
4863
|
className: cn(
|
|
3748
|
-
|
|
3749
|
-
"
|
|
3750
|
-
outcome === "failed" ? "border-destructive/40 bg-card text-destructive" : "border-border bg-card text-muted-foreground hover:border-primary/40 hover:bg-accent hover:text-foreground"
|
|
4864
|
+
ANSWER_ACTION_PILL,
|
|
4865
|
+
outcome === "failed" ? "border-destructive/40 bg-card text-destructive" : ANSWER_ACTION_IDLE
|
|
3751
4866
|
),
|
|
3752
4867
|
children: [
|
|
3753
4868
|
outcome === "copied" ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
@@ -3755,6 +4870,7 @@ function AnswerActions({
|
|
|
3755
4870
|
]
|
|
3756
4871
|
}
|
|
3757
4872
|
),
|
|
4873
|
+
/* @__PURE__ */ jsx(ReadAloudButton, { strings: T, text: speechText, lang: speechLang }),
|
|
3758
4874
|
/* @__PURE__ */ jsx("span", { "data-boff-explore": "copy-status", role: "status", className: "sr-only", children: outcome === "idle" ? "" : label })
|
|
3759
4875
|
]
|
|
3760
4876
|
}
|
|
@@ -3806,34 +4922,20 @@ function AssistantBubble({
|
|
|
3806
4922
|
activity,
|
|
3807
4923
|
imageOrigins,
|
|
3808
4924
|
productNames,
|
|
4925
|
+
speechLang,
|
|
3809
4926
|
onNavigate,
|
|
3810
4927
|
anchorRef,
|
|
3811
4928
|
onFollow,
|
|
3812
4929
|
onFollowUp,
|
|
3813
|
-
onFeedback
|
|
4930
|
+
onFeedback,
|
|
4931
|
+
onEscalate,
|
|
4932
|
+
isLatestAnswer = false
|
|
3814
4933
|
}) {
|
|
3815
4934
|
const streaming = message.status === "PENDING" || message.status === "RUNNING";
|
|
3816
4935
|
const body = message.content || message.partialContent || "";
|
|
3817
4936
|
const failed = message.status === "FAILED";
|
|
3818
4937
|
const completed = message.status === "COMPLETED";
|
|
3819
|
-
const
|
|
3820
|
-
() => /* @__PURE__ */ new Set()
|
|
3821
|
-
);
|
|
3822
|
-
const markImageFailed = useCallback((src) => {
|
|
3823
|
-
setFailedImages(
|
|
3824
|
-
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
3825
|
-
);
|
|
3826
|
-
}, []);
|
|
3827
|
-
const imageScope = useMemo(
|
|
3828
|
-
() => ({
|
|
3829
|
-
strings: T,
|
|
3830
|
-
enabled: completed,
|
|
3831
|
-
origins: imageOrigins,
|
|
3832
|
-
failed: failedImages,
|
|
3833
|
-
onFailed: markImageFailed
|
|
3834
|
-
}),
|
|
3835
|
-
[T, completed, imageOrigins, failedImages, markImageFailed]
|
|
3836
|
-
);
|
|
4938
|
+
const visible = useProgressiveReveal(body, !streaming);
|
|
3837
4939
|
const provenance = useMemo(
|
|
3838
4940
|
() => summariseReferences(message.references, imageOrigins, productNames),
|
|
3839
4941
|
[message.references, imageOrigins, productNames]
|
|
@@ -3842,7 +4944,25 @@ function AssistantBubble({
|
|
|
3842
4944
|
() => answerPlainText(message.content),
|
|
3843
4945
|
[message.content]
|
|
3844
4946
|
);
|
|
4947
|
+
const speechText = useMemo(
|
|
4948
|
+
() => answerSpeechText(message.content),
|
|
4949
|
+
[message.content]
|
|
4950
|
+
);
|
|
3845
4951
|
const grounded = completed && message.answered !== false;
|
|
4952
|
+
const [escalateOpen, setEscalateOpen] = useState(false);
|
|
4953
|
+
const [escalateBusy, setEscalateBusy] = useState(false);
|
|
4954
|
+
const escalateTriggerRef = useRef(null);
|
|
4955
|
+
const escalateFormId = `boff-explore-escalate-${message.id}`;
|
|
4956
|
+
const unanswered = completed && message.answered === false;
|
|
4957
|
+
const offerEscalate = Boolean(onEscalate) && completed;
|
|
4958
|
+
const showQuietEscalate = offerEscalate && !unanswered && isLatestAnswer;
|
|
4959
|
+
const closeEscalate = useCallback(() => {
|
|
4960
|
+
setEscalateOpen(false);
|
|
4961
|
+
escalateTriggerRef.current?.focus();
|
|
4962
|
+
}, []);
|
|
4963
|
+
const handleEscalateBusy = useCallback((busy) => {
|
|
4964
|
+
setEscalateBusy(busy);
|
|
4965
|
+
}, []);
|
|
3846
4966
|
return /* @__PURE__ */ jsxs(
|
|
3847
4967
|
"div",
|
|
3848
4968
|
{
|
|
@@ -3860,7 +4980,7 @@ function AssistantBubble({
|
|
|
3860
4980
|
}
|
|
3861
4981
|
),
|
|
3862
4982
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
|
|
3863
|
-
|
|
4983
|
+
unanswered ? /* @__PURE__ */ jsxs(
|
|
3864
4984
|
"div",
|
|
3865
4985
|
{
|
|
3866
4986
|
"data-boff-explore": "no-answer",
|
|
@@ -3870,26 +4990,56 @@ function AssistantBubble({
|
|
|
3870
4990
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
3871
4991
|
T.noAnswer,
|
|
3872
4992
|
" ",
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
4993
|
+
offerEscalate ? (
|
|
4994
|
+
/* The point of the wave: the same words, but they now open a form that
|
|
4995
|
+
sends the conversation we are both looking at, instead of a link to a
|
|
4996
|
+
blank contact page that asks the visitor to type it all again. */
|
|
4997
|
+
/* @__PURE__ */ jsx(
|
|
4998
|
+
"button",
|
|
4999
|
+
{
|
|
5000
|
+
ref: escalateTriggerRef,
|
|
5001
|
+
"data-boff-explore": "escalate-open",
|
|
5002
|
+
type: "button",
|
|
5003
|
+
"aria-expanded": escalateOpen,
|
|
5004
|
+
"aria-controls": escalateOpen ? escalateFormId : void 0,
|
|
5005
|
+
onClick: () => setEscalateOpen(true),
|
|
5006
|
+
className: "rounded-sm underline underline-offset-2 hover:text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
5007
|
+
children: T.askHuman
|
|
5008
|
+
}
|
|
5009
|
+
)
|
|
5010
|
+
) : (
|
|
5011
|
+
/* No conversation to hand over (a shared, read-only view). The old dead end
|
|
5012
|
+
is still better than a control whose submit could only fail. */
|
|
5013
|
+
/* @__PURE__ */ jsx(
|
|
5014
|
+
"a",
|
|
5015
|
+
{
|
|
5016
|
+
href: "/contact",
|
|
5017
|
+
className: "underline underline-offset-2 hover:text-primary",
|
|
5018
|
+
onClick: (event) => {
|
|
5019
|
+
if (!onNavigate) return;
|
|
5020
|
+
event.preventDefault();
|
|
5021
|
+
onNavigate("/contact");
|
|
5022
|
+
},
|
|
5023
|
+
children: T.askHuman
|
|
5024
|
+
}
|
|
5025
|
+
)
|
|
3885
5026
|
),
|
|
3886
5027
|
"."
|
|
3887
5028
|
] })
|
|
3888
5029
|
]
|
|
3889
5030
|
}
|
|
3890
5031
|
) : null,
|
|
3891
|
-
|
|
3892
|
-
/* @__PURE__ */ jsx(
|
|
5032
|
+
visible ? /* @__PURE__ */ jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
|
|
5033
|
+
/* @__PURE__ */ jsx(
|
|
5034
|
+
ExploreAnswerBody,
|
|
5035
|
+
{
|
|
5036
|
+
strings: T,
|
|
5037
|
+
imageOrigins,
|
|
5038
|
+
enabled: completed,
|
|
5039
|
+
className: "boff-prose-sm text-card-foreground",
|
|
5040
|
+
children: visible
|
|
5041
|
+
}
|
|
5042
|
+
),
|
|
3893
5043
|
streaming ? /* @__PURE__ */ jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
|
|
3894
5044
|
] }) : null,
|
|
3895
5045
|
grounded && provenance.total > 0 ? /* @__PURE__ */ jsx(ProvenanceChips, { strings: T, provenance }) : null,
|
|
@@ -3932,7 +5082,15 @@ function AssistantBubble({
|
|
|
3932
5082
|
question
|
|
3933
5083
|
)) })
|
|
3934
5084
|
] }) : null,
|
|
3935
|
-
completed && copyText !== "" ? /* @__PURE__ */ jsx(
|
|
5085
|
+
completed && copyText !== "" ? /* @__PURE__ */ jsx(
|
|
5086
|
+
AnswerActions,
|
|
5087
|
+
{
|
|
5088
|
+
strings: T,
|
|
5089
|
+
text: copyText,
|
|
5090
|
+
speechText,
|
|
5091
|
+
speechLang
|
|
5092
|
+
}
|
|
5093
|
+
) : null,
|
|
3936
5094
|
completed && onFeedback ? /* @__PURE__ */ jsx(
|
|
3937
5095
|
AnswerFeedback,
|
|
3938
5096
|
{
|
|
@@ -3941,6 +5099,30 @@ function AssistantBubble({
|
|
|
3941
5099
|
feedback: message.feedback,
|
|
3942
5100
|
onSubmit: onFeedback
|
|
3943
5101
|
}
|
|
5102
|
+
) : null,
|
|
5103
|
+
showQuietEscalate ? /* @__PURE__ */ jsx("div", { className: "pt-0.5", children: /* @__PURE__ */ jsx(
|
|
5104
|
+
"button",
|
|
5105
|
+
{
|
|
5106
|
+
ref: escalateTriggerRef,
|
|
5107
|
+
"data-boff-explore": "escalate-open",
|
|
5108
|
+
type: "button",
|
|
5109
|
+
"aria-expanded": escalateOpen,
|
|
5110
|
+
"aria-controls": escalateOpen ? escalateFormId : void 0,
|
|
5111
|
+
disabled: escalateBusy,
|
|
5112
|
+
onClick: () => setEscalateOpen((open) => !open),
|
|
5113
|
+
className: "rounded-sm text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
5114
|
+
children: T.escalateOpen
|
|
5115
|
+
}
|
|
5116
|
+
) }) : null,
|
|
5117
|
+
escalateOpen && onEscalate ? /* @__PURE__ */ jsx(
|
|
5118
|
+
EscalateForm,
|
|
5119
|
+
{
|
|
5120
|
+
strings: T,
|
|
5121
|
+
formId: escalateFormId,
|
|
5122
|
+
onEscalate,
|
|
5123
|
+
onClose: closeEscalate,
|
|
5124
|
+
onBusyChange: handleEscalateBusy
|
|
5125
|
+
}
|
|
3944
5126
|
) : null
|
|
3945
5127
|
] })
|
|
3946
5128
|
]
|
|
@@ -4380,15 +5562,6 @@ function WelcomeState({
|
|
|
4380
5562
|
}
|
|
4381
5563
|
)
|
|
4382
5564
|
] }) : null,
|
|
4383
|
-
/* @__PURE__ */ jsx(
|
|
4384
|
-
ConceptStrip,
|
|
4385
|
-
{
|
|
4386
|
-
strings: T,
|
|
4387
|
-
highlights: conceptHighlights,
|
|
4388
|
-
onAsk: onPrompt,
|
|
4389
|
-
disabled
|
|
4390
|
-
}
|
|
4391
|
-
),
|
|
4392
5565
|
products.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "boff-explore-rise boff-explore-rise-3 mt-6", children: [
|
|
4393
5566
|
/* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: T.focusProduct }),
|
|
4394
5567
|
/* @__PURE__ */ jsx(
|
|
@@ -4400,7 +5573,16 @@ function WelcomeState({
|
|
|
4400
5573
|
onFocus
|
|
4401
5574
|
}
|
|
4402
5575
|
)
|
|
4403
|
-
] }) : null
|
|
5576
|
+
] }) : null,
|
|
5577
|
+
/* @__PURE__ */ jsx(
|
|
5578
|
+
ConceptStrip,
|
|
5579
|
+
{
|
|
5580
|
+
strings: T,
|
|
5581
|
+
highlights: conceptHighlights,
|
|
5582
|
+
onAsk: onPrompt,
|
|
5583
|
+
disabled
|
|
5584
|
+
}
|
|
5585
|
+
)
|
|
4404
5586
|
] });
|
|
4405
5587
|
}
|
|
4406
5588
|
function ExplorePage({
|
|
@@ -4426,6 +5608,7 @@ function ExplorePage({
|
|
|
4426
5608
|
);
|
|
4427
5609
|
const T = useMemo(() => exploreStrings(activeLocale), [activeLocale]);
|
|
4428
5610
|
const rtl = isRtlLocale(activeLocale);
|
|
5611
|
+
const speechLang = speechLocaleOf(activeLocale);
|
|
4429
5612
|
const {
|
|
4430
5613
|
phase,
|
|
4431
5614
|
catalog,
|
|
@@ -4446,6 +5629,8 @@ function ExplorePage({
|
|
|
4446
5629
|
clearHistory,
|
|
4447
5630
|
recordClick,
|
|
4448
5631
|
submitFeedback,
|
|
5632
|
+
escalate,
|
|
5633
|
+
canEscalate,
|
|
4449
5634
|
createShareLink,
|
|
4450
5635
|
isSharedView
|
|
4451
5636
|
} = useExploreChat({
|
|
@@ -4530,11 +5715,11 @@ function ExplorePage({
|
|
|
4530
5715
|
const layoutHeight = window.innerHeight;
|
|
4531
5716
|
if (layoutHeight === 0) return;
|
|
4532
5717
|
const top = Math.round(
|
|
4533
|
-
|
|
5718
|
+
clamp2(el.getBoundingClientRect().top, 0, layoutHeight)
|
|
4534
5719
|
);
|
|
4535
5720
|
const viewport = window.visualViewport;
|
|
4536
5721
|
const inset = viewport ? Math.round(
|
|
4537
|
-
|
|
5722
|
+
clamp2(
|
|
4538
5723
|
layoutHeight - viewport.height - viewport.offsetTop,
|
|
4539
5724
|
0,
|
|
4540
5725
|
layoutHeight
|
|
@@ -4815,6 +6000,7 @@ function ExplorePage({
|
|
|
4815
6000
|
const text = draft.trim();
|
|
4816
6001
|
if (!text || overLimit || busy || !canSend) return;
|
|
4817
6002
|
stopDictation();
|
|
6003
|
+
stopSpeaking();
|
|
4818
6004
|
setDraft("");
|
|
4819
6005
|
stickToBottomRef.current = true;
|
|
4820
6006
|
void send(text, toPayload(stagedRef.current));
|
|
@@ -4825,6 +6011,7 @@ function ExplorePage({
|
|
|
4825
6011
|
const sendPrompt = useCallback(
|
|
4826
6012
|
(prompt) => {
|
|
4827
6013
|
if (!canSend || busy) return;
|
|
6014
|
+
stopSpeaking();
|
|
4828
6015
|
setDraft("");
|
|
4829
6016
|
stickToBottomRef.current = true;
|
|
4830
6017
|
void send(prompt, toPayload(stagedRef.current));
|
|
@@ -4887,6 +6074,7 @@ function ExplorePage({
|
|
|
4887
6074
|
),
|
|
4888
6075
|
/* @__PURE__ */ jsx(ProseStyles, {}),
|
|
4889
6076
|
/* @__PURE__ */ jsx(ExploreStyles, {}),
|
|
6077
|
+
/* @__PURE__ */ jsx(ExploreAnswerStyles, {}),
|
|
4890
6078
|
!showWelcome ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 border-b border-border px-4 py-2.5", children: [
|
|
4891
6079
|
/* @__PURE__ */ jsx(
|
|
4892
6080
|
"span",
|
|
@@ -5060,11 +6248,14 @@ function ExplorePage({
|
|
|
5060
6248
|
activity,
|
|
5061
6249
|
imageOrigins,
|
|
5062
6250
|
productNames,
|
|
6251
|
+
speechLang,
|
|
5063
6252
|
onNavigate,
|
|
5064
6253
|
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
|
|
5065
6254
|
onFollow: (kind, url) => recordClick(message.id, kind, url),
|
|
5066
6255
|
onFollowUp: sendPrompt,
|
|
5067
|
-
onFeedback: isSharedView ? void 0 : (rating, detail) => submitFeedback(message.id, rating, detail)
|
|
6256
|
+
onFeedback: isSharedView ? void 0 : (rating, detail) => submitFeedback(message.id, rating, detail),
|
|
6257
|
+
onEscalate: canEscalate ? escalate : void 0,
|
|
6258
|
+
isLatestAnswer: message.id === latestAssistantId
|
|
5068
6259
|
},
|
|
5069
6260
|
message.id
|
|
5070
6261
|
)
|
|
@@ -5171,10 +6362,19 @@ function ExplorePage({
|
|
|
5171
6362
|
/* @__PURE__ */ jsxs(
|
|
5172
6363
|
"div",
|
|
5173
6364
|
{
|
|
6365
|
+
"data-boff-explore": "composer-field",
|
|
5174
6366
|
className: cn(
|
|
5175
6367
|
"flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
|
|
5176
6368
|
busy && "boff-explore-busy",
|
|
5177
|
-
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
|
|
6369
|
+
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring",
|
|
6370
|
+
// The halo is an invitation, and an invitation is only useful before it has
|
|
6371
|
+
// been accepted: while the conversation is empty, the composer is usable, and
|
|
6372
|
+
// nothing has been typed. Once any of those stops being true the class goes
|
|
6373
|
+
// and the box is an ordinary card again. `-quiet` keeps the ring but stops the
|
|
6374
|
+
// movement the moment there are characters in the field, so it does not pulse
|
|
6375
|
+
// at someone who is mid-sentence.
|
|
6376
|
+
showWelcome && !composerDisabled && !overLimit && "boff-explore-invite",
|
|
6377
|
+
showWelcome && draft.length > 0 && "boff-explore-invite-quiet"
|
|
5178
6378
|
),
|
|
5179
6379
|
children: [
|
|
5180
6380
|
/* @__PURE__ */ jsx(
|
|
@@ -5362,6 +6562,6 @@ function ExplorePage({
|
|
|
5362
6562
|
);
|
|
5363
6563
|
}
|
|
5364
6564
|
|
|
5365
|
-
export { ExplorePage, answerPlainText, inlineImageOrigins, isInlineImageAllowed, logoUrlOf, productNamesByOrigin, referenceKindOf, summariseReferences };
|
|
6565
|
+
export { ExplorePage, answerPlainText, answerSpeechText, inlineImageOrigins, isInlineImageAllowed, logoUrlOf, productNamesByOrigin, referenceKindOf, summariseReferences };
|
|
5366
6566
|
//# sourceMappingURL=explore.mjs.map
|
|
5367
6567
|
//# sourceMappingURL=explore.mjs.map
|