@burdenoff/website-sdk 2026.922.3 → 2026.922.5
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 +92 -2
- package/dist/components/explore.d.ts +92 -2
- package/dist/components/explore.js +1171 -17
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +1169 -20
- package/dist/components/explore.mjs.map +1 -1
- package/dist/{explore-types-CPF1QoMn.d.mts → explore-types-DvQPiL1t.d.mts} +58 -2
- package/dist/{explore-types-CPF1QoMn.d.ts → explore-types-DvQPiL1t.d.ts} +58 -2
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +417 -8
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +415 -9
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +53 -3
- package/dist/index.d.ts +53 -3
- package/dist/index.js +1179 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1177 -28
- package/dist/index.mjs.map +1 -1
- package/dist/{use-explore-chat--CxNtkrJ.d.ts → use-progressive-reveal-BfU_IHip.d.ts} +36 -2
- package/dist/{use-explore-chat-C6Y8JQMj.d.mts → use-progressive-reveal-Ha3gYPfs.d.mts} +36 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
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, 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';
|
|
@@ -77,6 +77,23 @@ var EN = {
|
|
|
77
77
|
nextSuggestion: "Next suggestion",
|
|
78
78
|
conceptIllustration: "Concept illustration",
|
|
79
79
|
opensInNewTab: "opens in a new tab",
|
|
80
|
+
conceptStripTitle: "Or start from a concept",
|
|
81
|
+
conceptStripLabel: "Concepts you can ask about",
|
|
82
|
+
conceptAsk: "Ask about {title} in {product}",
|
|
83
|
+
conceptAskQuestion: "How does {title} work in {product}?",
|
|
84
|
+
copyAnswer: "Copy answer",
|
|
85
|
+
copyFailed: "Couldn't copy",
|
|
86
|
+
readAloud: "Read aloud",
|
|
87
|
+
stopReading: "Stop reading",
|
|
88
|
+
answerTable: "Table",
|
|
89
|
+
groundedIn: "What this answer is based on",
|
|
90
|
+
sourceCountOne: "1 source",
|
|
91
|
+
sourceCountMany: "{count} sources",
|
|
92
|
+
sourceKindConcept: "Concept",
|
|
93
|
+
sourceKindDocs: "Documentation",
|
|
94
|
+
sourceKindPricing: "Pricing",
|
|
95
|
+
sourceKindUseCase: "Use case",
|
|
96
|
+
sourceKindPage: "Product page",
|
|
80
97
|
seoTitle: "Describe a problem, get an answer",
|
|
81
98
|
seoDescription: "Describe the problem you're facing in your own words and get an answer drawn from our product documentation, with links to the exact pages it came from.",
|
|
82
99
|
seoDescriptionProduct: "Describe the problem you're facing in your own words and get an answer drawn from {product}'s documentation \u2014 and every other product we build \u2014 with links to the exact pages it came from.",
|
|
@@ -145,6 +162,23 @@ var ZH = {
|
|
|
145
162
|
nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
|
|
146
163
|
conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
|
|
147
164
|
opensInNewTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00",
|
|
165
|
+
conceptStripTitle: "\u6216\u8005\u4ECE\u4E00\u4E2A\u6982\u5FF5\u5F00\u59CB",
|
|
166
|
+
conceptStripLabel: "\u4F60\u53EF\u4EE5\u8BE2\u95EE\u7684\u6982\u5FF5",
|
|
167
|
+
conceptAsk: "\u8BE2\u95EE {product} \u4E2D\u7684 {title}",
|
|
168
|
+
conceptAskQuestion: "{product} \u7684 {title} \u662F\u600E\u4E48\u8FD0\u4F5C\u7684\uFF1F",
|
|
169
|
+
copyAnswer: "\u590D\u5236\u56DE\u7B54",
|
|
170
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
171
|
+
readAloud: "\u6717\u8BFB",
|
|
172
|
+
stopReading: "\u505C\u6B62\u6717\u8BFB",
|
|
173
|
+
answerTable: "\u8868\u683C",
|
|
174
|
+
groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
|
|
175
|
+
sourceCountOne: "1 \u4E2A\u6765\u6E90",
|
|
176
|
+
sourceCountMany: "{count} \u4E2A\u6765\u6E90",
|
|
177
|
+
sourceKindConcept: "\u6982\u5FF5",
|
|
178
|
+
sourceKindDocs: "\u6587\u6863",
|
|
179
|
+
sourceKindPricing: "\u4EF7\u683C",
|
|
180
|
+
sourceKindUseCase: "\u5E94\u7528\u573A\u666F",
|
|
181
|
+
sourceKindPage: "\u4EA7\u54C1\u9875\u9762",
|
|
148
182
|
seoTitle: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u83B7\u5F97\u7B54\u6848",
|
|
149
183
|
seoDescription: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u9047\u5230\u7684\u95EE\u9898\uFF0C\u5373\u53EF\u83B7\u5F97\u6765\u81EA\u6211\u4EEC\u4EA7\u54C1\u6587\u6863\u7684\u56DE\u7B54\uFF0C\u5E76\u9644\u4E0A\u7B54\u6848\u51FA\u5904\u7684\u9875\u9762\u94FE\u63A5\u3002",
|
|
150
184
|
seoDescriptionProduct: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u9047\u5230\u7684\u95EE\u9898\uFF0C\u5373\u53EF\u83B7\u5F97\u6765\u81EA {product} \u53CA\u6211\u4EEC\u5176\u4ED6\u4EA7\u54C1\u6587\u6863\u7684\u56DE\u7B54\uFF0C\u5E76\u9644\u4E0A\u7B54\u6848\u51FA\u5904\u7684\u9875\u9762\u94FE\u63A5\u3002",
|
|
@@ -209,6 +243,23 @@ var HI = {
|
|
|
209
243
|
nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
|
|
210
244
|
conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
|
|
211
245
|
opensInNewTab: "\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948",
|
|
246
|
+
conceptStripTitle: "\u092F\u093E \u0915\u093F\u0938\u0940 \u0905\u0935\u0927\u093E\u0930\u0923\u093E \u0938\u0947 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
|
|
247
|
+
conceptStripLabel: "\u091C\u093F\u0928 \u0905\u0935\u0927\u093E\u0930\u0923\u093E\u0913\u0902 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
|
|
248
|
+
conceptAsk: "{product} \u092E\u0947\u0902 {title} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902",
|
|
249
|
+
conceptAskQuestion: "{product} \u092E\u0947\u0902 {title} \u0915\u0948\u0938\u0947 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948?",
|
|
250
|
+
copyAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
|
|
251
|
+
copyFailed: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E",
|
|
252
|
+
readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
|
|
253
|
+
stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
254
|
+
answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
|
|
255
|
+
groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
|
|
256
|
+
sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
|
|
257
|
+
sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
|
|
258
|
+
sourceKindConcept: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E",
|
|
259
|
+
sourceKindDocs: "\u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C",
|
|
260
|
+
sourceKindPricing: "\u092E\u0942\u0932\u094D\u092F",
|
|
261
|
+
sourceKindUseCase: "\u0909\u092A\u092F\u094B\u0917 \u0915\u093E \u0909\u0926\u093E\u0939\u0930\u0923",
|
|
262
|
+
sourceKindPage: "\u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0947\u091C",
|
|
212
263
|
seoTitle: "\u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902, \u091C\u0935\u093E\u092C \u092A\u093E\u090F\u0902",
|
|
213
264
|
seoDescription: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u0905\u092A\u0928\u0947 \u0936\u092C\u094D\u0926\u094B\u0902 \u092E\u0947\u0902 \u092C\u0924\u093E\u090F\u0902 \u0914\u0930 \u0939\u092E\u093E\u0930\u0947 \u0909\u0924\u094D\u092A\u093E\u0926 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u0938\u0947 \u0932\u093F\u092F\u093E \u0917\u092F\u093E \u091C\u0935\u093E\u092C \u092A\u093E\u090F\u0902, \u0938\u093E\u0925 \u092E\u0947\u0902 \u0909\u0928\u094D\u0939\u0940\u0902 \u092A\u0928\u094D\u0928\u094B\u0902 \u0915\u0947 \u0932\u093F\u0902\u0915 \u091C\u0939\u093E\u0901 \u0938\u0947 \u0935\u0939 \u0906\u092F\u093E \u0939\u0948\u0964",
|
|
214
265
|
seoDescriptionProduct: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u0905\u092A\u0928\u0947 \u0936\u092C\u094D\u0926\u094B\u0902 \u092E\u0947\u0902 \u092C\u0924\u093E\u090F\u0902 \u0914\u0930 {product} \u0924\u0925\u093E \u0939\u092E\u093E\u0930\u0947 \u092C\u093E\u0915\u0940 \u0909\u0924\u094D\u092A\u093E\u0926\u094B\u0902 \u0915\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u0938\u0947 \u0932\u093F\u092F\u093E \u0917\u092F\u093E \u091C\u0935\u093E\u092C \u092A\u093E\u090F\u0902, \u0938\u093E\u0925 \u092E\u0947\u0902 \u0909\u0928\u094D\u0939\u0940\u0902 \u092A\u0928\u094D\u0928\u094B\u0902 \u0915\u0947 \u0932\u093F\u0902\u0915 \u091C\u0939\u093E\u0901 \u0938\u0947 \u0935\u0939 \u0906\u092F\u093E \u0939\u0948\u0964",
|
|
@@ -277,6 +328,23 @@ var ES = {
|
|
|
277
328
|
nextSuggestion: "Sugerencia siguiente",
|
|
278
329
|
conceptIllustration: "Ilustraci\xF3n del concepto",
|
|
279
330
|
opensInNewTab: "se abre en una pesta\xF1a nueva",
|
|
331
|
+
conceptStripTitle: "O empieza por un concepto",
|
|
332
|
+
conceptStripLabel: "Conceptos sobre los que puedes preguntar",
|
|
333
|
+
conceptAsk: "Preguntar por {title} en {product}",
|
|
334
|
+
conceptAskQuestion: "\xBFC\xF3mo funciona {title} en {product}?",
|
|
335
|
+
copyAnswer: "Copiar respuesta",
|
|
336
|
+
copyFailed: "No se pudo copiar",
|
|
337
|
+
readAloud: "Leer en voz alta",
|
|
338
|
+
stopReading: "Dejar de leer",
|
|
339
|
+
answerTable: "Tabla",
|
|
340
|
+
groundedIn: "En qu\xE9 se basa esta respuesta",
|
|
341
|
+
sourceCountOne: "1 fuente",
|
|
342
|
+
sourceCountMany: "{count} fuentes",
|
|
343
|
+
sourceKindConcept: "Concepto",
|
|
344
|
+
sourceKindDocs: "Documentaci\xF3n",
|
|
345
|
+
sourceKindPricing: "Precios",
|
|
346
|
+
sourceKindUseCase: "Caso de uso",
|
|
347
|
+
sourceKindPage: "P\xE1gina de producto",
|
|
280
348
|
seoTitle: "Describe un problema y obt\xE9n una respuesta",
|
|
281
349
|
seoDescription: "Describe con tus palabras el problema que tienes y recibe una respuesta extra\xEDda de la documentaci\xF3n de nuestros productos, con enlaces a las p\xE1ginas exactas de las que procede.",
|
|
282
350
|
seoDescriptionProduct: "Describe con tus palabras el problema que tienes y recibe una respuesta extra\xEDda de la documentaci\xF3n de {product} y del resto de nuestros productos, con enlaces a las p\xE1ginas exactas de las que procede.",
|
|
@@ -345,6 +413,23 @@ var AR = {
|
|
|
345
413
|
nextSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u062A\u0627\u0644\u064A",
|
|
346
414
|
conceptIllustration: "\u0631\u0633\u0645 \u062A\u0648\u0636\u064A\u062D\u064A \u0644\u0644\u0645\u0641\u0647\u0648\u0645",
|
|
347
415
|
opensInNewTab: "\u064A\u064F\u0641\u062A\u062D \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u062C\u062F\u064A\u062F\u0629",
|
|
416
|
+
conceptStripTitle: "\u0623\u0648 \u0627\u0628\u062F\u0623 \u0645\u0646 \u0645\u0641\u0647\u0648\u0645",
|
|
417
|
+
conceptStripLabel: "\u0645\u0641\u0627\u0647\u064A\u0645 \u064A\u0645\u0643\u0646\u0643 \u0627\u0644\u0633\u0624\u0627\u0644 \u0639\u0646\u0647\u0627",
|
|
418
|
+
conceptAsk: "\u0627\u0633\u0623\u0644 \u0639\u0646 {title} \u0641\u064A {product}",
|
|
419
|
+
conceptAskQuestion: "\u0643\u064A\u0641 \u064A\u0639\u0645\u0644 {title} \u0641\u064A {product}\u061F",
|
|
420
|
+
copyAnswer: "\u0646\u0633\u062E \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
421
|
+
copyFailed: "\u062A\u0639\u0630\u0651\u0631 \u0627\u0644\u0646\u0633\u062E",
|
|
422
|
+
readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
|
|
423
|
+
stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
|
|
424
|
+
answerTable: "\u062C\u062F\u0648\u0644",
|
|
425
|
+
groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
426
|
+
sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
|
|
427
|
+
sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
|
|
428
|
+
sourceKindConcept: "\u0645\u0641\u0647\u0648\u0645",
|
|
429
|
+
sourceKindDocs: "\u0627\u0644\u062A\u0648\u062B\u064A\u0642",
|
|
430
|
+
sourceKindPricing: "\u0627\u0644\u0623\u0633\u0639\u0627\u0631",
|
|
431
|
+
sourceKindUseCase: "\u062D\u0627\u0644\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645",
|
|
432
|
+
sourceKindPage: "\u0635\u0641\u062D\u0629 \u0627\u0644\u0645\u0646\u062A\u062C",
|
|
348
433
|
seoTitle: "\u0635\u0641 \u0645\u0634\u0643\u0644\u062A\u0643 \u0648\u0627\u062D\u0635\u0644 \u0639\u0644\u0649 \u0625\u062C\u0627\u0628\u0629",
|
|
349
434
|
seoDescription: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629 \u0627\u0644\u062A\u064A \u062A\u0648\u0627\u062C\u0647\u0647\u0627 \u0628\u0643\u0644\u0645\u0627\u062A\u0643 \u0648\u0627\u062D\u0635\u0644 \u0639\u0644\u0649 \u0625\u062C\u0627\u0628\u0629 \u0645\u0633\u062A\u0645\u062F\u0629 \u0645\u0646 \u0648\u062B\u0627\u0626\u0642 \u0645\u0646\u062A\u062C\u0627\u062A\u0646\u0627\u060C \u0645\u0639 \u0631\u0648\u0627\u0628\u0637 \u0625\u0644\u0649 \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u062A\u064A \u062C\u0627\u0621\u062A \u0645\u0646\u0647\u0627.",
|
|
350
435
|
seoDescriptionProduct: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629 \u0627\u0644\u062A\u064A \u062A\u0648\u0627\u062C\u0647\u0647\u0627 \u0628\u0643\u0644\u0645\u0627\u062A\u0643 \u0648\u0627\u062D\u0635\u0644 \u0639\u0644\u0649 \u0625\u062C\u0627\u0628\u0629 \u0645\u0633\u062A\u0645\u062F\u0629 \u0645\u0646 \u0648\u062B\u0627\u0626\u0642 {product} \u0648\u0628\u0642\u064A\u0629 \u0645\u0646\u062A\u062C\u0627\u062A\u0646\u0627\u060C \u0645\u0639 \u0631\u0648\u0627\u0628\u0637 \u0625\u0644\u0649 \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u062A\u064A \u062C\u0627\u0621\u062A \u0645\u0646\u0647\u0627.",
|
|
@@ -413,6 +498,23 @@ var TA = {
|
|
|
413
498
|
nextSuggestion: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
|
|
414
499
|
conceptIllustration: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BB5\u0BBF\u0BB3\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BAE\u0BCD",
|
|
415
500
|
opensInNewTab: "\u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0BA4\u0BBE\u0BB5\u0BB2\u0BBF\u0BB2\u0BCD \u0BA4\u0BBF\u0BB1\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD",
|
|
501
|
+
conceptStripTitle: "\u0B85\u0BB2\u0BCD\u0BB2\u0BA4\u0BC1 \u0B92\u0BB0\u0BC1 \u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BBF\u0BB0\u0BC1\u0BA8\u0BCD\u0BA4\u0BC1 \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
502
|
+
conceptStripLabel: "\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BC7\u0B9F\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1\u0B95\u0BB3\u0BCD",
|
|
503
|
+
conceptAsk: "{product} \u0B87\u0BB2\u0BCD {title} \u0BAA\u0BB1\u0BCD\u0BB1\u0BBF\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
504
|
+
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?",
|
|
505
|
+
copyAnswer: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1",
|
|
506
|
+
copyFailed: "\u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8",
|
|
507
|
+
readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
|
|
508
|
+
stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
509
|
+
answerTable: "\u0B85\u0B9F\u0BCD\u0B9F\u0BB5\u0BA3\u0BC8",
|
|
510
|
+
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",
|
|
511
|
+
sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
|
|
512
|
+
sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
513
|
+
sourceKindConcept: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
514
|
+
sourceKindDocs: "\u0B86\u0BB5\u0BA3\u0BAE\u0BCD",
|
|
515
|
+
sourceKindPricing: "\u0BB5\u0BBF\u0BB2\u0BC8",
|
|
516
|
+
sourceKindUseCase: "\u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1 \u0BA8\u0BBF\u0BB2\u0BC8",
|
|
517
|
+
sourceKindPage: "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1\u0BAA\u0BCD \u0BAA\u0B95\u0BCD\u0B95\u0BAE\u0BCD",
|
|
416
518
|
seoTitle: "\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD, \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0BAA\u0BC6\u0BB1\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
417
519
|
seoDescription: "\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0B95\u0BCA\u0BB3\u0BCD\u0BB3\u0BC1\u0BAE\u0BCD \u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B9A\u0BCA\u0BA8\u0BCD\u0BA4 \u0BB5\u0BBE\u0BB0\u0BCD\u0BA4\u0BCD\u0BA4\u0BC8\u0B95\u0BB3\u0BBF\u0BB2\u0BCD \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD; \u0B8E\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1 \u0B86\u0BB5\u0BA3\u0B99\u0BCD\u0B95\u0BB3\u0BBF\u0BB2\u0BBF\u0BB0\u0BC1\u0BA8\u0BCD\u0BA4\u0BC1 \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B95\u0BBF\u0B9F\u0BC8\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD, \u0B85\u0BA4\u0BC1 \u0BB5\u0BA8\u0BCD\u0BA4 \u0BAA\u0B95\u0BCD\u0B95\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BC1\u0B9F\u0BA9\u0BCD.",
|
|
418
520
|
seoDescriptionProduct: "{product} \u0BAE\u0BB1\u0BCD\u0BB1\u0BC1\u0BAE\u0BCD \u0B8E\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAA\u0BBF\u0BB1 \u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BBF\u0BA9\u0BCD \u0B86\u0BB5\u0BA3\u0B99\u0BCD\u0B95\u0BB3\u0BBF\u0BB2\u0BBF\u0BB0\u0BC1\u0BA8\u0BCD\u0BA4\u0BC1 \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0BAA\u0BC6\u0BB1, \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0B95\u0BCA\u0BB3\u0BCD\u0BB3\u0BC1\u0BAE\u0BCD \u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B9A\u0BCA\u0BA8\u0BCD\u0BA4 \u0BB5\u0BBE\u0BB0\u0BCD\u0BA4\u0BCD\u0BA4\u0BC8\u0B95\u0BB3\u0BBF\u0BB2\u0BCD \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u2014 \u0B85\u0BA4\u0BC1 \u0BB5\u0BA8\u0BCD\u0BA4 \u0BAA\u0B95\u0BCD\u0B95\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BC1\u0B9F\u0BA9\u0BCD.",
|
|
@@ -436,6 +538,17 @@ var STRINGS = {
|
|
|
436
538
|
ar: AR,
|
|
437
539
|
ta: TA
|
|
438
540
|
};
|
|
541
|
+
var SPEECH_TAGS = {
|
|
542
|
+
en: "en-US",
|
|
543
|
+
zh: "zh-CN",
|
|
544
|
+
hi: "hi-IN",
|
|
545
|
+
es: "es-ES",
|
|
546
|
+
ar: "ar-SA",
|
|
547
|
+
ta: "ta-IN"
|
|
548
|
+
};
|
|
549
|
+
function speechLocaleOf(locale) {
|
|
550
|
+
return SPEECH_TAGS[locale];
|
|
551
|
+
}
|
|
439
552
|
function resolveExploreLocale(explicit) {
|
|
440
553
|
const candidates = [
|
|
441
554
|
explicit,
|
|
@@ -514,11 +627,9 @@ var EXPLORE_OWN_MESSAGE_FIELDS = (
|
|
|
514
627
|
}
|
|
515
628
|
`
|
|
516
629
|
);
|
|
517
|
-
var
|
|
630
|
+
var EXPLORE_CATALOG_ESTABLISHED_FIELDS = (
|
|
518
631
|
/* GraphQL */
|
|
519
632
|
`
|
|
520
|
-
query ExploreCatalog($productSlug: String) {
|
|
521
|
-
exploreCatalog(productSlug: $productSlug) {
|
|
522
633
|
enabled
|
|
523
634
|
products {
|
|
524
635
|
slug
|
|
@@ -536,11 +647,35 @@ var EXPLORE_CATALOG_QUERY = (
|
|
|
536
647
|
historyTurns
|
|
537
648
|
}
|
|
538
649
|
captchaRequired
|
|
539
|
-
recaptchaSiteKey
|
|
650
|
+
recaptchaSiteKey`
|
|
651
|
+
);
|
|
652
|
+
var EXPLORE_CATALOG_HIGHLIGHT_FIELD = (
|
|
653
|
+
/* GraphQL */
|
|
654
|
+
`
|
|
655
|
+
conceptHighlights {
|
|
656
|
+
product
|
|
657
|
+
productName
|
|
658
|
+
title
|
|
659
|
+
url
|
|
660
|
+
imageUrl
|
|
661
|
+
alt
|
|
662
|
+
}`
|
|
663
|
+
);
|
|
664
|
+
function exploreCatalogDocument(extraFields) {
|
|
665
|
+
return (
|
|
666
|
+
/* GraphQL */
|
|
667
|
+
`
|
|
668
|
+
query ExploreCatalog($productSlug: String) {
|
|
669
|
+
exploreCatalog(productSlug: $productSlug) {${EXPLORE_CATALOG_ESTABLISHED_FIELDS}${extraFields}
|
|
540
670
|
}
|
|
541
671
|
}
|
|
542
672
|
`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
var EXPLORE_CATALOG_QUERY = exploreCatalogDocument(
|
|
676
|
+
EXPLORE_CATALOG_HIGHLIGHT_FIELD
|
|
543
677
|
);
|
|
678
|
+
var EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS = exploreCatalogDocument("");
|
|
544
679
|
var EXPLORE_MESSAGE_QUERY = (
|
|
545
680
|
/* GraphQL */
|
|
546
681
|
`
|
|
@@ -1343,7 +1478,12 @@ function buildFallbackCatalog(examplePrompts, welcome) {
|
|
|
1343
1478
|
welcomeBody: welcome?.body ?? FALLBACK_WELCOME_BODY,
|
|
1344
1479
|
limits: FALLBACK_EXPLORE_LIMITS,
|
|
1345
1480
|
captchaRequired: false,
|
|
1346
|
-
recaptchaSiteKey: null
|
|
1481
|
+
recaptchaSiteKey: null,
|
|
1482
|
+
// Empty, and deliberately not synthesised from `ECOSYSTEM_PRODUCTS`: a concept highlight
|
|
1483
|
+
// needs a real published illustration URL, and this SDK has no registry of them. An
|
|
1484
|
+
// invented path would render 38 broken frames on the one screen every prerender captures.
|
|
1485
|
+
// The strip renders nothing at all when the list is empty, which is the honest fallback.
|
|
1486
|
+
conceptHighlights: []
|
|
1347
1487
|
};
|
|
1348
1488
|
}
|
|
1349
1489
|
function normalizeCatalog(raw, fallback) {
|
|
@@ -1360,9 +1500,27 @@ function normalizeCatalog(raw, fallback) {
|
|
|
1360
1500
|
historyTurns: raw.limits?.historyTurns || fallback.limits.historyTurns
|
|
1361
1501
|
},
|
|
1362
1502
|
captchaRequired: raw.captchaRequired === true,
|
|
1363
|
-
recaptchaSiteKey: raw.recaptchaSiteKey ?? null
|
|
1503
|
+
recaptchaSiteKey: raw.recaptchaSiteKey ?? null,
|
|
1504
|
+
// `[ExploreConceptHighlight!]!` on the wire, so a gateway that KNOWS the field always
|
|
1505
|
+
// sends an array. The one response that arrives without it is the retry against an older
|
|
1506
|
+
// supergraph (`EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS`) — that gateway does not answer
|
|
1507
|
+
// the full document at all, it refuses it. Either way
|
|
1508
|
+
// `catalog.conceptHighlights.filter(...)` on `undefined` would throw inside render,
|
|
1509
|
+
// blanking the welcome screen over a decorative strip.
|
|
1510
|
+
conceptHighlights: raw.conceptHighlights?.length ? raw.conceptHighlights : fallback.conceptHighlights
|
|
1364
1511
|
};
|
|
1365
1512
|
}
|
|
1513
|
+
var CATALOG_DOCUMENT_REJECTED_CODES = /* @__PURE__ */ new Set([
|
|
1514
|
+
"GRAPHQL_VALIDATION_FAILED",
|
|
1515
|
+
"HTTP_400"
|
|
1516
|
+
]);
|
|
1517
|
+
function catalogRejectedTheHighlightField(res) {
|
|
1518
|
+
if (res.data?.exploreCatalog) return false;
|
|
1519
|
+
return (res.errors ?? []).some((error) => {
|
|
1520
|
+
const code = error.extensions?.code;
|
|
1521
|
+
return typeof code === "string" && CATALOG_DOCUMENT_REJECTED_CODES.has(code) || error.message.includes("conceptHighlights");
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1366
1524
|
var RATE_LIMIT_CODES = /* @__PURE__ */ new Set([
|
|
1367
1525
|
"RATE_LIMITED",
|
|
1368
1526
|
"HTTP_429",
|
|
@@ -1702,11 +1860,19 @@ function useExploreChat(options = {}) {
|
|
|
1702
1860
|
useEffect(() => {
|
|
1703
1861
|
let cancelled = false;
|
|
1704
1862
|
const load = async () => {
|
|
1705
|
-
const
|
|
1863
|
+
const variables = { productSlug: productSlug ?? null };
|
|
1864
|
+
let res = await client.query(
|
|
1706
1865
|
EXPLORE_CATALOG_QUERY,
|
|
1707
|
-
|
|
1866
|
+
variables
|
|
1708
1867
|
);
|
|
1709
1868
|
if (cancelled || !mountedRef.current) return;
|
|
1869
|
+
if (catalogRejectedTheHighlightField(res)) {
|
|
1870
|
+
res = await client.query(
|
|
1871
|
+
EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,
|
|
1872
|
+
variables
|
|
1873
|
+
);
|
|
1874
|
+
if (cancelled || !mountedRef.current) return;
|
|
1875
|
+
}
|
|
1710
1876
|
const raw = res.data?.exploreCatalog;
|
|
1711
1877
|
if (res.errors?.length || !raw) {
|
|
1712
1878
|
console.warn(
|
|
@@ -2152,6 +2318,224 @@ function makeOptimisticId() {
|
|
|
2152
2318
|
optimisticCounter += 1;
|
|
2153
2319
|
return `boff-explore-local-${Date.now()}-${optimisticCounter}`;
|
|
2154
2320
|
}
|
|
2321
|
+
var DRAIN_TARGET_MS = 1200;
|
|
2322
|
+
var MIN_CHARS_PER_SECOND = 90;
|
|
2323
|
+
var MAX_CHARS_PER_SECOND = 2200;
|
|
2324
|
+
var MAX_STEP_MS = 250;
|
|
2325
|
+
var COMMIT_INTERVAL_MS = 45;
|
|
2326
|
+
var FALLBACK_STEP_MS = 50;
|
|
2327
|
+
var FENCE = /^ {0,3}(?:```|~~~)/;
|
|
2328
|
+
var TABLE_ROW = /^ {0,3}\|/;
|
|
2329
|
+
var EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\d{1,9}[.)])\s*$/;
|
|
2330
|
+
function isWordCharacter(char) {
|
|
2331
|
+
return char !== void 0 && /[\p{L}\p{N}]/u.test(char);
|
|
2332
|
+
}
|
|
2333
|
+
function clamp(value, min, max) {
|
|
2334
|
+
return Math.min(Math.max(value, min), max);
|
|
2335
|
+
}
|
|
2336
|
+
function now() {
|
|
2337
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
2338
|
+
}
|
|
2339
|
+
function isHighSurrogate(code) {
|
|
2340
|
+
return code >= 55296 && code <= 56319;
|
|
2341
|
+
}
|
|
2342
|
+
function safeInlineEnd(line) {
|
|
2343
|
+
let codeSpan = null;
|
|
2344
|
+
let link = null;
|
|
2345
|
+
const emphasis = [];
|
|
2346
|
+
let at = 0;
|
|
2347
|
+
while (at < line.length) {
|
|
2348
|
+
const char = line[at] ?? "";
|
|
2349
|
+
if (char === "\\") {
|
|
2350
|
+
at += 2;
|
|
2351
|
+
continue;
|
|
2352
|
+
}
|
|
2353
|
+
if (char === "`") {
|
|
2354
|
+
let run = 1;
|
|
2355
|
+
while (line[at + run] === "`") run += 1;
|
|
2356
|
+
codeSpan = codeSpan === null ? at : null;
|
|
2357
|
+
at += run;
|
|
2358
|
+
continue;
|
|
2359
|
+
}
|
|
2360
|
+
if (codeSpan !== null) {
|
|
2361
|
+
at += 1;
|
|
2362
|
+
continue;
|
|
2363
|
+
}
|
|
2364
|
+
if (char === "[") {
|
|
2365
|
+
if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
|
|
2366
|
+
at += 1;
|
|
2367
|
+
continue;
|
|
2368
|
+
}
|
|
2369
|
+
if (char === "]") {
|
|
2370
|
+
if (link !== null && line[at + 1] !== "(") link = null;
|
|
2371
|
+
at += 1;
|
|
2372
|
+
continue;
|
|
2373
|
+
}
|
|
2374
|
+
if (char === ")") {
|
|
2375
|
+
link = null;
|
|
2376
|
+
at += 1;
|
|
2377
|
+
continue;
|
|
2378
|
+
}
|
|
2379
|
+
if (char === "*" || char === "_" || char === "~") {
|
|
2380
|
+
let run = 1;
|
|
2381
|
+
while (line[at + run] === char) run += 1;
|
|
2382
|
+
const start = at;
|
|
2383
|
+
at += run;
|
|
2384
|
+
const before = line[start - 1];
|
|
2385
|
+
const after = line[start + run];
|
|
2386
|
+
if (run < 2) continue;
|
|
2387
|
+
if (char === "_" && isWordCharacter(before) && isWordCharacter(after)) {
|
|
2388
|
+
continue;
|
|
2389
|
+
}
|
|
2390
|
+
const marker = char.repeat(run);
|
|
2391
|
+
const open2 = emphasis.findIndex((entry) => entry.marker === marker);
|
|
2392
|
+
if (open2 !== -1) {
|
|
2393
|
+
emphasis.splice(open2, 1);
|
|
2394
|
+
continue;
|
|
2395
|
+
}
|
|
2396
|
+
if (after !== void 0 && /\s/.test(after)) continue;
|
|
2397
|
+
emphasis.push({ marker, start });
|
|
2398
|
+
continue;
|
|
2399
|
+
}
|
|
2400
|
+
at += 1;
|
|
2401
|
+
}
|
|
2402
|
+
const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
|
|
2403
|
+
(value) => value !== null
|
|
2404
|
+
);
|
|
2405
|
+
return open.length === 0 ? line.length : Math.min(...open);
|
|
2406
|
+
}
|
|
2407
|
+
function revealBoundary(text, limit) {
|
|
2408
|
+
if (limit <= 0) return 0;
|
|
2409
|
+
const caughtUp = limit >= text.length;
|
|
2410
|
+
const head = caughtUp ? text : text.slice(0, limit);
|
|
2411
|
+
const lineStart = head.lastIndexOf("\n") + 1;
|
|
2412
|
+
const line = head.slice(lineStart);
|
|
2413
|
+
let fenceStart = null;
|
|
2414
|
+
let tableStart = null;
|
|
2415
|
+
let tableRows = 0;
|
|
2416
|
+
let at = 0;
|
|
2417
|
+
while (at < lineStart) {
|
|
2418
|
+
const breakAt = head.indexOf("\n", at);
|
|
2419
|
+
const end = breakAt === -1 ? lineStart : breakAt;
|
|
2420
|
+
const row = head.slice(at, end);
|
|
2421
|
+
if (fenceStart !== null) {
|
|
2422
|
+
if (FENCE.test(row)) fenceStart = null;
|
|
2423
|
+
} else if (FENCE.test(row)) {
|
|
2424
|
+
fenceStart = at;
|
|
2425
|
+
} else if (TABLE_ROW.test(row)) {
|
|
2426
|
+
if (tableStart === null) {
|
|
2427
|
+
tableStart = at;
|
|
2428
|
+
tableRows = 0;
|
|
2429
|
+
}
|
|
2430
|
+
tableRows += 1;
|
|
2431
|
+
} else if (tableStart !== null) {
|
|
2432
|
+
tableStart = null;
|
|
2433
|
+
tableRows = 0;
|
|
2434
|
+
}
|
|
2435
|
+
at = end + 1;
|
|
2436
|
+
}
|
|
2437
|
+
if (fenceStart === null) {
|
|
2438
|
+
const lastIsRow = TABLE_ROW.test(line);
|
|
2439
|
+
if (lastIsRow && tableStart === null) {
|
|
2440
|
+
tableStart = lineStart;
|
|
2441
|
+
tableRows = 0;
|
|
2442
|
+
} else if (!lastIsRow && line !== "") {
|
|
2443
|
+
tableStart = null;
|
|
2444
|
+
tableRows = 0;
|
|
2445
|
+
}
|
|
2446
|
+
if (tableStart !== null && tableRows < 2) return tableStart;
|
|
2447
|
+
}
|
|
2448
|
+
if (caughtUp) return text.length;
|
|
2449
|
+
if (fenceStart !== null) return fenceStart;
|
|
2450
|
+
if (TABLE_ROW.test(line)) return lineStart;
|
|
2451
|
+
if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;
|
|
2452
|
+
const cut = lineStart + safeInlineEnd(line);
|
|
2453
|
+
return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;
|
|
2454
|
+
}
|
|
2455
|
+
function prefersReducedMotion() {
|
|
2456
|
+
if (typeof window === "undefined") return true;
|
|
2457
|
+
try {
|
|
2458
|
+
return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
2459
|
+
} catch {
|
|
2460
|
+
return false;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
function useProgressiveReveal(text, settled) {
|
|
2464
|
+
const [, setFrame] = useState(0);
|
|
2465
|
+
const [reduced] = useState(prefersReducedMotion);
|
|
2466
|
+
const immediate = settled || reduced;
|
|
2467
|
+
const textRef = useRef(text);
|
|
2468
|
+
const cursorRef = useRef(immediate ? text.length : 0);
|
|
2469
|
+
const cutRef = useRef(immediate ? text.length : 0);
|
|
2470
|
+
const committedRef = useRef(0);
|
|
2471
|
+
if (text !== textRef.current) {
|
|
2472
|
+
const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));
|
|
2473
|
+
if (revealed !== "" && !text.startsWith(revealed)) {
|
|
2474
|
+
cursorRef.current = text.length;
|
|
2475
|
+
}
|
|
2476
|
+
cursorRef.current = Math.min(cursorRef.current, text.length);
|
|
2477
|
+
cutRef.current = Math.min(cutRef.current, text.length);
|
|
2478
|
+
textRef.current = text;
|
|
2479
|
+
}
|
|
2480
|
+
if (immediate) cursorRef.current = text.length;
|
|
2481
|
+
const cut = immediate ? text.length : Math.max(
|
|
2482
|
+
cutRef.current,
|
|
2483
|
+
revealBoundary(text, Math.floor(cursorRef.current))
|
|
2484
|
+
);
|
|
2485
|
+
cutRef.current = cut;
|
|
2486
|
+
useEffect(() => {
|
|
2487
|
+
if (immediate) return;
|
|
2488
|
+
if (cursorRef.current >= text.length) return;
|
|
2489
|
+
let cancelled = false;
|
|
2490
|
+
let cancelStep = null;
|
|
2491
|
+
let previous = now();
|
|
2492
|
+
const commit = (at, force) => {
|
|
2493
|
+
if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;
|
|
2494
|
+
committedRef.current = at;
|
|
2495
|
+
setFrame((value) => value + 1);
|
|
2496
|
+
};
|
|
2497
|
+
const step = () => {
|
|
2498
|
+
cancelStep = null;
|
|
2499
|
+
if (cancelled) return;
|
|
2500
|
+
const at = now();
|
|
2501
|
+
const elapsed = clamp(at - previous, 0, MAX_STEP_MS);
|
|
2502
|
+
previous = at;
|
|
2503
|
+
const length = textRef.current.length;
|
|
2504
|
+
const pending = length - cursorRef.current;
|
|
2505
|
+
if (pending <= 0) {
|
|
2506
|
+
commit(at, true);
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
const perSecond = clamp(
|
|
2510
|
+
pending / (DRAIN_TARGET_MS / 1e3),
|
|
2511
|
+
MIN_CHARS_PER_SECOND,
|
|
2512
|
+
MAX_CHARS_PER_SECOND
|
|
2513
|
+
);
|
|
2514
|
+
cursorRef.current = Math.min(
|
|
2515
|
+
length,
|
|
2516
|
+
cursorRef.current + perSecond * elapsed / 1e3
|
|
2517
|
+
);
|
|
2518
|
+
const done = cursorRef.current >= length;
|
|
2519
|
+
commit(at, done);
|
|
2520
|
+
if (!done) schedule();
|
|
2521
|
+
};
|
|
2522
|
+
const schedule = () => {
|
|
2523
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
2524
|
+
const id2 = window.requestAnimationFrame(step);
|
|
2525
|
+
cancelStep = () => window.cancelAnimationFrame(id2);
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
const id = setTimeout(step, FALLBACK_STEP_MS);
|
|
2529
|
+
cancelStep = () => clearTimeout(id);
|
|
2530
|
+
};
|
|
2531
|
+
schedule();
|
|
2532
|
+
return () => {
|
|
2533
|
+
cancelled = true;
|
|
2534
|
+
cancelStep?.();
|
|
2535
|
+
};
|
|
2536
|
+
}, [text, immediate]);
|
|
2537
|
+
return text.slice(0, cut);
|
|
2538
|
+
}
|
|
2155
2539
|
function getRecognitionCtor() {
|
|
2156
2540
|
if (typeof window === "undefined") return null;
|
|
2157
2541
|
const w = window;
|
|
@@ -2370,6 +2754,141 @@ function useSpeechInput({
|
|
|
2370
2754
|
);
|
|
2371
2755
|
return { supported, listening, interim, error, start, stop, toggle };
|
|
2372
2756
|
}
|
|
2757
|
+
var SILENT_FAILURE_MS = 1500;
|
|
2758
|
+
function speechPlatform() {
|
|
2759
|
+
if (typeof window === "undefined") return null;
|
|
2760
|
+
const scope = window;
|
|
2761
|
+
const synth = scope.speechSynthesis;
|
|
2762
|
+
const Utterance = scope.SpeechSynthesisUtterance;
|
|
2763
|
+
if (!synth || typeof synth.speak !== "function") return null;
|
|
2764
|
+
if (typeof Utterance !== "function") return null;
|
|
2765
|
+
return { synth, Utterance };
|
|
2766
|
+
}
|
|
2767
|
+
function normaliseTag(tag) {
|
|
2768
|
+
return tag.trim().toLowerCase().replace(/_/g, "-");
|
|
2769
|
+
}
|
|
2770
|
+
function primaryOf(tag) {
|
|
2771
|
+
return normaliseTag(tag).split("-")[0] ?? "";
|
|
2772
|
+
}
|
|
2773
|
+
function pickVoice(voices, lang) {
|
|
2774
|
+
const wanted = normaliseTag(lang);
|
|
2775
|
+
if (wanted === "") return null;
|
|
2776
|
+
const primary = primaryOf(wanted);
|
|
2777
|
+
const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);
|
|
2778
|
+
const related = voices.filter((voice) => primaryOf(voice.lang) === primary);
|
|
2779
|
+
return exact.find((voice) => voice.default) ?? exact[0] ?? related.find((voice) => voice.default) ?? related[0] ?? null;
|
|
2780
|
+
}
|
|
2781
|
+
var activeOwner = null;
|
|
2782
|
+
var activeStop = null;
|
|
2783
|
+
function stopSpeaking() {
|
|
2784
|
+
activeStop?.();
|
|
2785
|
+
}
|
|
2786
|
+
function useSpeechOutput({
|
|
2787
|
+
lang
|
|
2788
|
+
} = {}) {
|
|
2789
|
+
const [available, setAvailable] = useState(() => speechPlatform() !== null);
|
|
2790
|
+
const [speaking, setSpeaking] = useState(false);
|
|
2791
|
+
const utteranceRef = useRef(null);
|
|
2792
|
+
const watchdogRef = useRef(null);
|
|
2793
|
+
const ownerRef = useRef({});
|
|
2794
|
+
const stopRef = useRef(() => void 0);
|
|
2795
|
+
const langRef = useRef(lang);
|
|
2796
|
+
langRef.current = lang;
|
|
2797
|
+
const clearWatchdog = useCallback(() => {
|
|
2798
|
+
if (watchdogRef.current === null) return;
|
|
2799
|
+
clearTimeout(watchdogRef.current);
|
|
2800
|
+
watchdogRef.current = null;
|
|
2801
|
+
}, []);
|
|
2802
|
+
const settle = useCallback(() => {
|
|
2803
|
+
clearWatchdog();
|
|
2804
|
+
utteranceRef.current = null;
|
|
2805
|
+
if (activeOwner === ownerRef.current) {
|
|
2806
|
+
activeOwner = null;
|
|
2807
|
+
activeStop = null;
|
|
2808
|
+
}
|
|
2809
|
+
setSpeaking(false);
|
|
2810
|
+
}, [clearWatchdog]);
|
|
2811
|
+
const stop = useCallback(() => {
|
|
2812
|
+
const owned = utteranceRef.current !== null;
|
|
2813
|
+
settle();
|
|
2814
|
+
if (owned) speechPlatform()?.synth.cancel();
|
|
2815
|
+
}, [settle]);
|
|
2816
|
+
stopRef.current = stop;
|
|
2817
|
+
const speak = useCallback(
|
|
2818
|
+
(text) => {
|
|
2819
|
+
const platform = speechPlatform();
|
|
2820
|
+
if (!platform) {
|
|
2821
|
+
setAvailable(false);
|
|
2822
|
+
return;
|
|
2823
|
+
}
|
|
2824
|
+
const spoken = text.trim();
|
|
2825
|
+
if (spoken === "") return;
|
|
2826
|
+
if (activeStop && activeOwner !== ownerRef.current) activeStop();
|
|
2827
|
+
platform.synth.cancel();
|
|
2828
|
+
const utterance = new platform.Utterance(spoken);
|
|
2829
|
+
const wanted = langRef.current;
|
|
2830
|
+
if (wanted !== void 0 && wanted !== "") {
|
|
2831
|
+
const voice = pickVoice(platform.synth.getVoices(), wanted);
|
|
2832
|
+
utterance.lang = voice?.lang ?? wanted;
|
|
2833
|
+
if (voice) utterance.voice = voice;
|
|
2834
|
+
}
|
|
2835
|
+
const mine = () => utteranceRef.current === utterance;
|
|
2836
|
+
utterance.onstart = () => {
|
|
2837
|
+
if (!mine()) return;
|
|
2838
|
+
clearWatchdog();
|
|
2839
|
+
setSpeaking(true);
|
|
2840
|
+
};
|
|
2841
|
+
utterance.onend = () => {
|
|
2842
|
+
if (!mine()) return;
|
|
2843
|
+
settle();
|
|
2844
|
+
};
|
|
2845
|
+
utterance.onerror = (event) => {
|
|
2846
|
+
if (!mine()) return;
|
|
2847
|
+
const code = typeof event?.error === "string" ? event.error : "";
|
|
2848
|
+
settle();
|
|
2849
|
+
if (code !== "interrupted" && code !== "canceled") setAvailable(false);
|
|
2850
|
+
};
|
|
2851
|
+
utteranceRef.current = utterance;
|
|
2852
|
+
activeOwner = ownerRef.current;
|
|
2853
|
+
activeStop = () => {
|
|
2854
|
+
stopRef.current();
|
|
2855
|
+
};
|
|
2856
|
+
setSpeaking(true);
|
|
2857
|
+
platform.synth.speak(utterance);
|
|
2858
|
+
clearWatchdog();
|
|
2859
|
+
watchdogRef.current = setTimeout(() => {
|
|
2860
|
+
watchdogRef.current = null;
|
|
2861
|
+
if (!mine()) return;
|
|
2862
|
+
if (platform.synth.speaking) return;
|
|
2863
|
+
settle();
|
|
2864
|
+
setAvailable(false);
|
|
2865
|
+
}, SILENT_FAILURE_MS);
|
|
2866
|
+
},
|
|
2867
|
+
[clearWatchdog, settle]
|
|
2868
|
+
);
|
|
2869
|
+
const toggle = useCallback(
|
|
2870
|
+
(text) => {
|
|
2871
|
+
if (speaking) stop();
|
|
2872
|
+
else speak(text);
|
|
2873
|
+
},
|
|
2874
|
+
[speak, speaking, stop]
|
|
2875
|
+
);
|
|
2876
|
+
useEffect(() => {
|
|
2877
|
+
if (!speaking || typeof window === "undefined") return;
|
|
2878
|
+
const onHide = () => {
|
|
2879
|
+
stopRef.current();
|
|
2880
|
+
};
|
|
2881
|
+
window.addEventListener("pagehide", onHide);
|
|
2882
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
2883
|
+
}, [speaking]);
|
|
2884
|
+
useEffect(
|
|
2885
|
+
() => () => {
|
|
2886
|
+
stopRef.current();
|
|
2887
|
+
},
|
|
2888
|
+
[]
|
|
2889
|
+
);
|
|
2890
|
+
return { supported: available, speaking, speak, stop, toggle };
|
|
2891
|
+
}
|
|
2373
2892
|
var SWIPE_THRESHOLD = 40;
|
|
2374
2893
|
function useSwipe({
|
|
2375
2894
|
onSwipeLeft,
|
|
@@ -2765,6 +3284,115 @@ var EXPLORE_CSS = `
|
|
|
2765
3284
|
height: auto;
|
|
2766
3285
|
}
|
|
2767
3286
|
|
|
3287
|
+
/* ---- Comparison and pricing tables ----------------------------------------------------
|
|
3288
|
+
The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
|
|
3289
|
+
the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
|
|
3290
|
+
the page must not scroll sideways because of it, and it has to be readable on a 320px
|
|
3291
|
+
phone, which a four-column grid of 1px borders is not.
|
|
3292
|
+
|
|
3293
|
+
Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
|
|
3294
|
+
\`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
|
|
3295
|
+
cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
|
|
3296
|
+
of table from chaining to the page \u2014 the same rule the transcript needed. */
|
|
3297
|
+
.boff-explore-body .boff-explore-table {
|
|
3298
|
+
max-width: 100%;
|
|
3299
|
+
overflow-x: auto;
|
|
3300
|
+
overscroll-behavior-x: contain;
|
|
3301
|
+
margin: 0.75rem 0;
|
|
3302
|
+
border: 1px solid hsl(var(--border));
|
|
3303
|
+
border-radius: 0.75rem;
|
|
3304
|
+
background-color: hsl(var(--card));
|
|
3305
|
+
}
|
|
3306
|
+
.boff-explore-body .boff-explore-table:focus-visible {
|
|
3307
|
+
outline: 2px solid hsl(var(--ring));
|
|
3308
|
+
outline-offset: 2px;
|
|
3309
|
+
}
|
|
3310
|
+
.boff-explore-body .boff-explore-table table {
|
|
3311
|
+
/* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
|
|
3312
|
+
display: table;
|
|
3313
|
+
width: 100%;
|
|
3314
|
+
max-width: none;
|
|
3315
|
+
margin: 0;
|
|
3316
|
+
border-collapse: collapse;
|
|
3317
|
+
font-size: 0.8125rem;
|
|
3318
|
+
}
|
|
3319
|
+
.boff-explore-body .boff-explore-table th,
|
|
3320
|
+
.boff-explore-body .boff-explore-table td {
|
|
3321
|
+
border: 0;
|
|
3322
|
+
border-top: 1px solid hsl(var(--border));
|
|
3323
|
+
padding: 0.5rem 0.75rem;
|
|
3324
|
+
/* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
|
|
3325
|
+
table there reads against the grain of every other line on the page. */
|
|
3326
|
+
text-align: start;
|
|
3327
|
+
vertical-align: top;
|
|
3328
|
+
overflow-wrap: anywhere;
|
|
3329
|
+
/* Prices in a column line up under each other instead of drifting by digit width. */
|
|
3330
|
+
font-variant-numeric: tabular-nums;
|
|
3331
|
+
min-width: 6rem;
|
|
3332
|
+
}
|
|
3333
|
+
.boff-explore-body .boff-explore-table thead th {
|
|
3334
|
+
border-top: 0;
|
|
3335
|
+
background-color: hsl(var(--muted));
|
|
3336
|
+
color: hsl(var(--foreground));
|
|
3337
|
+
font-weight: 600;
|
|
3338
|
+
white-space: nowrap;
|
|
3339
|
+
}
|
|
3340
|
+
/* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
|
|
3341
|
+
the row's heading is what makes a price list scannable rather than a grid of strings. */
|
|
3342
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
3343
|
+
font-weight: 600;
|
|
3344
|
+
color: hsl(var(--card-foreground));
|
|
3345
|
+
}
|
|
3346
|
+
|
|
3347
|
+
/* Narrow: one card per row. Each cell prints its own column heading, so a value is never
|
|
3348
|
+
stranded without the question it answers, and the subject cell becomes the card's title.
|
|
3349
|
+
A media query rather than a measured width \u2014 these pages are serialised post-hydration,
|
|
3350
|
+
so anything measured here would ship to every phone as static HTML. */
|
|
3351
|
+
@media (max-width: 30rem) {
|
|
3352
|
+
.boff-explore-body .boff-explore-table {
|
|
3353
|
+
overflow-x: visible;
|
|
3354
|
+
}
|
|
3355
|
+
.boff-explore-body .boff-explore-table table,
|
|
3356
|
+
.boff-explore-body .boff-explore-table tbody,
|
|
3357
|
+
.boff-explore-body .boff-explore-table tr,
|
|
3358
|
+
.boff-explore-body .boff-explore-table td {
|
|
3359
|
+
display: block;
|
|
3360
|
+
width: 100%;
|
|
3361
|
+
min-width: 0;
|
|
3362
|
+
}
|
|
3363
|
+
.boff-explore-body .boff-explore-table thead {
|
|
3364
|
+
display: none;
|
|
3365
|
+
}
|
|
3366
|
+
.boff-explore-body .boff-explore-table tr {
|
|
3367
|
+
border-top: 1px solid hsl(var(--border));
|
|
3368
|
+
padding: 0.5rem 0;
|
|
3369
|
+
}
|
|
3370
|
+
.boff-explore-body .boff-explore-table tr:first-child {
|
|
3371
|
+
border-top: 0;
|
|
3372
|
+
}
|
|
3373
|
+
.boff-explore-body .boff-explore-table td {
|
|
3374
|
+
border: 0;
|
|
3375
|
+
padding: 0.125rem 0.75rem;
|
|
3376
|
+
}
|
|
3377
|
+
.boff-explore-body .boff-explore-table td[data-label]::before {
|
|
3378
|
+
content: attr(data-label);
|
|
3379
|
+
display: block;
|
|
3380
|
+
font-size: 0.6875rem;
|
|
3381
|
+
font-weight: 600;
|
|
3382
|
+
text-transform: uppercase;
|
|
3383
|
+
letter-spacing: 0.04em;
|
|
3384
|
+
color: hsl(var(--muted-foreground));
|
|
3385
|
+
}
|
|
3386
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
3387
|
+
font-size: 0.9375rem;
|
|
3388
|
+
padding-bottom: 0.25rem;
|
|
3389
|
+
}
|
|
3390
|
+
.boff-explore-body .boff-explore-table tbody td:first-child::before {
|
|
3391
|
+
/* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
|
|
3392
|
+
display: none;
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
|
|
2768
3396
|
/* ---- Ambient backdrop -----------------------------------------------------------------
|
|
2769
3397
|
Two very low-opacity radial washes behind the welcome state, drifting slowly. Painted on
|
|
2770
3398
|
a pseudo-element with pointer-events:none so it can never intercept a click \u2014 a
|
|
@@ -2936,7 +3564,7 @@ function isOwnElement(el, root) {
|
|
|
2936
3564
|
function visualViewportHeight() {
|
|
2937
3565
|
return window.visualViewport?.height ?? window.innerHeight;
|
|
2938
3566
|
}
|
|
2939
|
-
function
|
|
3567
|
+
function clamp2(value, min, max) {
|
|
2940
3568
|
return Math.min(Math.max(value, min), max);
|
|
2941
3569
|
}
|
|
2942
3570
|
function isRestoredNavigation() {
|
|
@@ -3001,14 +3629,196 @@ function isInlineImageAllowed(src, origins) {
|
|
|
3001
3629
|
if (url.protocol !== "https:" || url.username || url.password) return false;
|
|
3002
3630
|
return origins.has(url.origin);
|
|
3003
3631
|
}
|
|
3004
|
-
var
|
|
3632
|
+
var REFERENCE_KIND_ORDER = [
|
|
3633
|
+
"CONCEPT",
|
|
3634
|
+
"DOCS",
|
|
3635
|
+
"PRICING",
|
|
3636
|
+
"USE_CASE",
|
|
3637
|
+
"PAGE"
|
|
3638
|
+
];
|
|
3639
|
+
var PRODUCT_SUBDOMAINS = [
|
|
3640
|
+
"alphadocs.",
|
|
3641
|
+
"alphaapp.",
|
|
3642
|
+
"docs.",
|
|
3643
|
+
"alpha.",
|
|
3644
|
+
"app.",
|
|
3645
|
+
"www."
|
|
3646
|
+
];
|
|
3647
|
+
function readCatalogUrl(raw) {
|
|
3648
|
+
let parsed;
|
|
3649
|
+
try {
|
|
3650
|
+
parsed = new URL(raw);
|
|
3651
|
+
} catch {
|
|
3652
|
+
return null;
|
|
3653
|
+
}
|
|
3654
|
+
if (parsed.protocol !== "https:") return null;
|
|
3655
|
+
const host = parsed.hostname.toLowerCase();
|
|
3656
|
+
const prefix = PRODUCT_SUBDOMAINS.find(
|
|
3657
|
+
(candidate) => host.startsWith(candidate)
|
|
3658
|
+
);
|
|
3659
|
+
const apex = prefix === void 0 ? host : host.slice(prefix.length);
|
|
3660
|
+
const port = parsed.port ? `:${parsed.port}` : "";
|
|
3661
|
+
return {
|
|
3662
|
+
origin: `https://${apex}${port}`,
|
|
3663
|
+
prefix,
|
|
3664
|
+
pathname: parsed.pathname
|
|
3665
|
+
};
|
|
3666
|
+
}
|
|
3667
|
+
function referenceKindOf(url, origins) {
|
|
3668
|
+
const parsed = readCatalogUrl(url);
|
|
3669
|
+
if (parsed === null || !origins.has(parsed.origin)) return null;
|
|
3670
|
+
if (parsed.prefix === "docs." || parsed.prefix === "alphadocs.")
|
|
3671
|
+
return "DOCS";
|
|
3672
|
+
const segment = parsed.pathname.toLowerCase().split("/").filter(Boolean)[0] ?? "";
|
|
3673
|
+
if (segment === "concept" || segment === "concepts") return "CONCEPT";
|
|
3674
|
+
if (segment === "docs") return "DOCS";
|
|
3675
|
+
if (segment === "pricing" || segment === "plans") return "PRICING";
|
|
3676
|
+
if (segment === "use-case" || segment === "use-cases" || segment === "solutions") {
|
|
3677
|
+
return "USE_CASE";
|
|
3678
|
+
}
|
|
3679
|
+
return "PAGE";
|
|
3680
|
+
}
|
|
3681
|
+
function productNamesByOrigin(products) {
|
|
3682
|
+
const names = /* @__PURE__ */ new Map();
|
|
3683
|
+
for (const product of products) {
|
|
3684
|
+
let url;
|
|
3685
|
+
try {
|
|
3686
|
+
url = new URL(product.website);
|
|
3687
|
+
} catch {
|
|
3688
|
+
continue;
|
|
3689
|
+
}
|
|
3690
|
+
if (url.protocol !== "https:") continue;
|
|
3691
|
+
const apex = url.hostname.replace(/^www\./, "");
|
|
3692
|
+
const port = url.port ? `:${url.port}` : "";
|
|
3693
|
+
const key = `https://${apex}${port}`;
|
|
3694
|
+
if (!names.has(key)) names.set(key, product.name);
|
|
3695
|
+
}
|
|
3696
|
+
return names;
|
|
3697
|
+
}
|
|
3698
|
+
function summariseReferences(references, origins, productNames) {
|
|
3699
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3700
|
+
const products = [];
|
|
3701
|
+
for (const reference of references) {
|
|
3702
|
+
const kind = referenceKindOf(reference.url, origins);
|
|
3703
|
+
if (kind !== null) counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
3704
|
+
const parsed = readCatalogUrl(reference.url);
|
|
3705
|
+
const name = (parsed === null ? void 0 : productNames.get(parsed.origin)) ?? (reference.product || null);
|
|
3706
|
+
if (name != null && !products.includes(name)) products.push(name);
|
|
3707
|
+
}
|
|
3708
|
+
return {
|
|
3709
|
+
total: references.length,
|
|
3710
|
+
kinds: REFERENCE_KIND_ORDER.filter((kind) => counts.has(kind)).map(
|
|
3711
|
+
(kind) => ({ kind, count: counts.get(kind) ?? 0 })
|
|
3712
|
+
),
|
|
3713
|
+
products
|
|
3714
|
+
};
|
|
3715
|
+
}
|
|
3716
|
+
var CODE_FENCE = /^\s*(?:```|~~~)/;
|
|
3717
|
+
var TABLE_ROW_LINE = /^\s{0,3}\|/;
|
|
3718
|
+
var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
|
|
3719
|
+
function readableLine(raw, mode) {
|
|
3720
|
+
let line = raw;
|
|
3721
|
+
line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
|
|
3722
|
+
line = mode === "speech" ? (
|
|
3723
|
+
// A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
|
|
3724
|
+
// prompt asks for two to five links in every answer. The label IS the words.
|
|
3725
|
+
line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
|
|
3726
|
+
) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
|
|
3727
|
+
line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
|
|
3728
|
+
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
3729
|
+
line = line.replace(/^\s{0,3}>\s?/, "");
|
|
3730
|
+
if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
|
|
3731
|
+
line = line.replace(/`([^`]+)`/g, "$1");
|
|
3732
|
+
line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
3733
|
+
line = line.replace(/__([^_]+)__/g, "$1");
|
|
3734
|
+
line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
|
|
3735
|
+
line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
|
|
3736
|
+
return line.replace(/[ \t]+$/, "");
|
|
3737
|
+
}
|
|
3738
|
+
function tableCells(row) {
|
|
3739
|
+
const trimmed = row.trim();
|
|
3740
|
+
const cells = [];
|
|
3741
|
+
let cell = "";
|
|
3742
|
+
for (let at = 0; at < trimmed.length; at += 1) {
|
|
3743
|
+
const char = trimmed[at] ?? "";
|
|
3744
|
+
if (char === "\\") {
|
|
3745
|
+
cell += char + (trimmed[at + 1] ?? "");
|
|
3746
|
+
at += 1;
|
|
3747
|
+
continue;
|
|
3748
|
+
}
|
|
3749
|
+
if (char === "|") {
|
|
3750
|
+
cells.push(cell.trim());
|
|
3751
|
+
cell = "";
|
|
3752
|
+
continue;
|
|
3753
|
+
}
|
|
3754
|
+
cell += char;
|
|
3755
|
+
}
|
|
3756
|
+
cells.push(cell.trim());
|
|
3757
|
+
if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
|
|
3758
|
+
if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
|
|
3759
|
+
return cells;
|
|
3760
|
+
}
|
|
3761
|
+
function asSentence(text) {
|
|
3762
|
+
return /[.!?]$/.test(text) ? text : `${text}.`;
|
|
3763
|
+
}
|
|
3764
|
+
function spokenTable(rows) {
|
|
3765
|
+
if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
|
|
3766
|
+
const labels = tableCells(rows[0] ?? "");
|
|
3767
|
+
const spoken = [];
|
|
3768
|
+
for (const row of rows.slice(2)) {
|
|
3769
|
+
const parts = [];
|
|
3770
|
+
tableCells(row).forEach((value, column) => {
|
|
3771
|
+
if (value === "") return;
|
|
3772
|
+
const label = column === 0 ? "" : labels[column] ?? "";
|
|
3773
|
+
parts.push(label === "" ? value : `${label}: ${value}`);
|
|
3774
|
+
});
|
|
3775
|
+
if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
|
|
3776
|
+
}
|
|
3777
|
+
return spoken;
|
|
3778
|
+
}
|
|
3779
|
+
function readableText(markdown, mode) {
|
|
3780
|
+
const lines = [];
|
|
3781
|
+
let inFence = false;
|
|
3782
|
+
let table = [];
|
|
3783
|
+
const flushTable = () => {
|
|
3784
|
+
if (table.length === 0) return;
|
|
3785
|
+
for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
|
|
3786
|
+
table = [];
|
|
3787
|
+
};
|
|
3788
|
+
for (const raw of markdown.split("\n")) {
|
|
3789
|
+
if (CODE_FENCE.test(raw)) {
|
|
3790
|
+
flushTable();
|
|
3791
|
+
inFence = !inFence;
|
|
3792
|
+
continue;
|
|
3793
|
+
}
|
|
3794
|
+
if (inFence) {
|
|
3795
|
+
lines.push(raw);
|
|
3796
|
+
continue;
|
|
3797
|
+
}
|
|
3798
|
+
if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
|
|
3799
|
+
table.push(raw);
|
|
3800
|
+
continue;
|
|
3801
|
+
}
|
|
3802
|
+
flushTable();
|
|
3803
|
+
lines.push(readableLine(raw, mode));
|
|
3804
|
+
}
|
|
3805
|
+
flushTable();
|
|
3806
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
3807
|
+
}
|
|
3808
|
+
function answerPlainText(markdown) {
|
|
3809
|
+
return readableText(markdown, "clipboard");
|
|
3810
|
+
}
|
|
3811
|
+
function answerSpeechText(markdown) {
|
|
3812
|
+
return readableText(markdown, "speech");
|
|
3813
|
+
}
|
|
3814
|
+
var AnswerScopeContext = createContext(null);
|
|
3005
3815
|
var InsideLinkContext = createContext(false);
|
|
3006
3816
|
function AnswerLink(props) {
|
|
3007
3817
|
return /* @__PURE__ */ jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx(MarkdownLink, { ...props }) });
|
|
3008
3818
|
}
|
|
3009
3819
|
var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
|
|
3010
3820
|
function InlineConceptImage({ src, alt }) {
|
|
3011
|
-
const scope = useContext(
|
|
3821
|
+
const scope = useContext(AnswerScopeContext);
|
|
3012
3822
|
const insideLink = useContext(InsideLinkContext);
|
|
3013
3823
|
if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
|
|
3014
3824
|
return null;
|
|
@@ -3056,9 +3866,70 @@ function InlineConceptImage({ src, alt }) {
|
|
|
3056
3866
|
}
|
|
3057
3867
|
) });
|
|
3058
3868
|
}
|
|
3869
|
+
function markdownText(node) {
|
|
3870
|
+
if (!node) return "";
|
|
3871
|
+
if (typeof node.value === "string") return node.value;
|
|
3872
|
+
return (node.children ?? []).map(markdownText).join("");
|
|
3873
|
+
}
|
|
3874
|
+
function hasHeaderCells(node) {
|
|
3875
|
+
return (node?.children ?? []).some((cell) => cell.tagName === "th");
|
|
3876
|
+
}
|
|
3877
|
+
function findHeaderRow(node) {
|
|
3878
|
+
for (const child of node?.children ?? []) {
|
|
3879
|
+
if (child.tagName === "tr" && hasHeaderCells(child)) return child;
|
|
3880
|
+
const nested = findHeaderRow(child);
|
|
3881
|
+
if (nested !== null) return nested;
|
|
3882
|
+
}
|
|
3883
|
+
return null;
|
|
3884
|
+
}
|
|
3885
|
+
function tableColumnLabels(node) {
|
|
3886
|
+
const row = findHeaderRow(node);
|
|
3887
|
+
if (row === null) return [];
|
|
3888
|
+
return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
|
|
3889
|
+
}
|
|
3890
|
+
var TableColumnsContext = createContext([]);
|
|
3891
|
+
function AnswerTable({ node, children }) {
|
|
3892
|
+
const scope = useContext(AnswerScopeContext);
|
|
3893
|
+
const table = /* @__PURE__ */ jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsx("table", { children }) });
|
|
3894
|
+
if (!scope) return table;
|
|
3895
|
+
return /* @__PURE__ */ jsx(
|
|
3896
|
+
"div",
|
|
3897
|
+
{
|
|
3898
|
+
"data-boff-explore": "table",
|
|
3899
|
+
role: "region",
|
|
3900
|
+
"aria-label": scope.strings.answerTable,
|
|
3901
|
+
tabIndex: 0,
|
|
3902
|
+
className: "boff-explore-table",
|
|
3903
|
+
children: table
|
|
3904
|
+
}
|
|
3905
|
+
);
|
|
3906
|
+
}
|
|
3907
|
+
function AnswerTableRow({
|
|
3908
|
+
node,
|
|
3909
|
+
children,
|
|
3910
|
+
...props
|
|
3911
|
+
}) {
|
|
3912
|
+
const labels = useContext(TableColumnsContext);
|
|
3913
|
+
if (labels.length === 0 || hasHeaderCells(node)) {
|
|
3914
|
+
return /* @__PURE__ */ jsx("tr", { ...props, children });
|
|
3915
|
+
}
|
|
3916
|
+
let column = 0;
|
|
3917
|
+
const cells = Children.map(children, (cell) => {
|
|
3918
|
+
if (!isValidElement(cell)) return cell;
|
|
3919
|
+
const label = labels[column] ?? "";
|
|
3920
|
+
column += 1;
|
|
3921
|
+
if (label === "") return cell;
|
|
3922
|
+
return cloneElement(cell, {
|
|
3923
|
+
"data-label": label
|
|
3924
|
+
});
|
|
3925
|
+
});
|
|
3926
|
+
return /* @__PURE__ */ jsx("tr", { ...props, children: cells });
|
|
3927
|
+
}
|
|
3059
3928
|
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
3060
3929
|
a: AnswerLink,
|
|
3061
|
-
img: InlineConceptImage
|
|
3930
|
+
img: InlineConceptImage,
|
|
3931
|
+
table: AnswerTable,
|
|
3932
|
+
tr: AnswerTableRow
|
|
3062
3933
|
};
|
|
3063
3934
|
function ReferenceCard({
|
|
3064
3935
|
reference,
|
|
@@ -3398,6 +4269,148 @@ function AnswerFeedback({
|
|
|
3398
4269
|
}
|
|
3399
4270
|
);
|
|
3400
4271
|
}
|
|
4272
|
+
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";
|
|
4273
|
+
function ProvenanceChips({
|
|
4274
|
+
strings: T,
|
|
4275
|
+
provenance
|
|
4276
|
+
}) {
|
|
4277
|
+
if (provenance.total === 0) return null;
|
|
4278
|
+
const kindLabels = {
|
|
4279
|
+
CONCEPT: T.sourceKindConcept,
|
|
4280
|
+
DOCS: T.sourceKindDocs,
|
|
4281
|
+
PRICING: T.sourceKindPricing,
|
|
4282
|
+
USE_CASE: T.sourceKindUseCase,
|
|
4283
|
+
PAGE: T.sourceKindPage
|
|
4284
|
+
};
|
|
4285
|
+
return /* @__PURE__ */ jsxs(
|
|
4286
|
+
"ul",
|
|
4287
|
+
{
|
|
4288
|
+
"data-boff-explore": "provenance",
|
|
4289
|
+
"aria-label": T.groundedIn,
|
|
4290
|
+
className: "flex flex-wrap items-center gap-1.5",
|
|
4291
|
+
children: [
|
|
4292
|
+
/* @__PURE__ */ jsxs("li", { "data-boff-explore": "provenance-count", className: PROVENANCE_CHIP, children: [
|
|
4293
|
+
/* @__PURE__ */ jsx(BookOpenText, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
4294
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: provenance.total === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(provenance.total) }) })
|
|
4295
|
+
] }),
|
|
4296
|
+
provenance.kinds.map(({ kind, count }) => /* @__PURE__ */ jsx(
|
|
4297
|
+
"li",
|
|
4298
|
+
{
|
|
4299
|
+
"data-boff-explore": "provenance-kind",
|
|
4300
|
+
"data-kind": kind,
|
|
4301
|
+
"data-count": count,
|
|
4302
|
+
className: PROVENANCE_CHIP,
|
|
4303
|
+
children: /* @__PURE__ */ jsx("span", { className: "truncate", children: count > 1 ? `${kindLabels[kind]} \xB7 ${count}` : kindLabels[kind] })
|
|
4304
|
+
},
|
|
4305
|
+
kind
|
|
4306
|
+
)),
|
|
4307
|
+
provenance.products.map((name) => /* @__PURE__ */ jsx(
|
|
4308
|
+
"li",
|
|
4309
|
+
{
|
|
4310
|
+
"data-boff-explore": "provenance-product",
|
|
4311
|
+
className: PROVENANCE_CHIP,
|
|
4312
|
+
children: /* @__PURE__ */ jsx("span", { className: "truncate", children: name })
|
|
4313
|
+
},
|
|
4314
|
+
name
|
|
4315
|
+
))
|
|
4316
|
+
]
|
|
4317
|
+
}
|
|
4318
|
+
);
|
|
4319
|
+
}
|
|
4320
|
+
var COPY_FEEDBACK_MS = 2500;
|
|
4321
|
+
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";
|
|
4322
|
+
var ANSWER_ACTION_IDLE = "border-border bg-card text-muted-foreground hover:border-primary/40 hover:bg-accent hover:text-foreground";
|
|
4323
|
+
var ANSWER_ACTION_ACTIVE = "border-primary/40 bg-accent text-primary";
|
|
4324
|
+
function ReadAloudButton({
|
|
4325
|
+
strings: T,
|
|
4326
|
+
text,
|
|
4327
|
+
lang
|
|
4328
|
+
}) {
|
|
4329
|
+
const speech = useSpeechOutput({ lang });
|
|
4330
|
+
if (!speech.supported) return null;
|
|
4331
|
+
const label = speech.speaking ? T.stopReading : T.readAloud;
|
|
4332
|
+
return /* @__PURE__ */ jsxs(
|
|
4333
|
+
"button",
|
|
4334
|
+
{
|
|
4335
|
+
"data-boff-explore": "read-aloud",
|
|
4336
|
+
"data-speaking": speech.speaking ? "true" : "false",
|
|
4337
|
+
type: "button",
|
|
4338
|
+
title: label,
|
|
4339
|
+
"aria-pressed": speech.speaking,
|
|
4340
|
+
onClick: () => speech.toggle(text),
|
|
4341
|
+
className: cn(
|
|
4342
|
+
ANSWER_ACTION_PILL,
|
|
4343
|
+
speech.speaking ? ANSWER_ACTION_ACTIVE : ANSWER_ACTION_IDLE
|
|
4344
|
+
),
|
|
4345
|
+
children: [
|
|
4346
|
+
/* @__PURE__ */ jsx(Volume2, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
4347
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: label })
|
|
4348
|
+
]
|
|
4349
|
+
}
|
|
4350
|
+
);
|
|
4351
|
+
}
|
|
4352
|
+
function AnswerActions({
|
|
4353
|
+
strings: T,
|
|
4354
|
+
text,
|
|
4355
|
+
speechText,
|
|
4356
|
+
speechLang
|
|
4357
|
+
}) {
|
|
4358
|
+
const [outcome, setOutcome] = useState("idle");
|
|
4359
|
+
const resetRef = useRef(null);
|
|
4360
|
+
useEffect(
|
|
4361
|
+
() => () => {
|
|
4362
|
+
if (resetRef.current !== null) clearTimeout(resetRef.current);
|
|
4363
|
+
},
|
|
4364
|
+
[]
|
|
4365
|
+
);
|
|
4366
|
+
const copy = useCallback(async () => {
|
|
4367
|
+
let copied = false;
|
|
4368
|
+
try {
|
|
4369
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
4370
|
+
if (typeof clipboard?.writeText === "function") {
|
|
4371
|
+
await clipboard.writeText(text);
|
|
4372
|
+
copied = true;
|
|
4373
|
+
}
|
|
4374
|
+
} catch {
|
|
4375
|
+
copied = false;
|
|
4376
|
+
}
|
|
4377
|
+
setOutcome(copied ? "copied" : "failed");
|
|
4378
|
+
if (resetRef.current !== null) clearTimeout(resetRef.current);
|
|
4379
|
+
resetRef.current = setTimeout(() => setOutcome("idle"), COPY_FEEDBACK_MS);
|
|
4380
|
+
}, [text]);
|
|
4381
|
+
const label = outcome === "copied" ? T.copied : outcome === "failed" ? T.copyFailed : T.copyAnswer;
|
|
4382
|
+
return /* @__PURE__ */ jsxs(
|
|
4383
|
+
"div",
|
|
4384
|
+
{
|
|
4385
|
+
"data-boff-explore": "answer-actions",
|
|
4386
|
+
className: "flex flex-wrap items-center gap-2 pt-0.5",
|
|
4387
|
+
children: [
|
|
4388
|
+
/* @__PURE__ */ jsxs(
|
|
4389
|
+
"button",
|
|
4390
|
+
{
|
|
4391
|
+
"data-boff-explore": "copy-answer",
|
|
4392
|
+
"data-outcome": outcome,
|
|
4393
|
+
type: "button",
|
|
4394
|
+
title: label,
|
|
4395
|
+
onClick: () => {
|
|
4396
|
+
void copy();
|
|
4397
|
+
},
|
|
4398
|
+
className: cn(
|
|
4399
|
+
ANSWER_ACTION_PILL,
|
|
4400
|
+
outcome === "failed" ? "border-destructive/40 bg-card text-destructive" : ANSWER_ACTION_IDLE
|
|
4401
|
+
),
|
|
4402
|
+
children: [
|
|
4403
|
+
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" }),
|
|
4404
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: label })
|
|
4405
|
+
]
|
|
4406
|
+
}
|
|
4407
|
+
),
|
|
4408
|
+
/* @__PURE__ */ jsx(ReadAloudButton, { strings: T, text: speechText, lang: speechLang }),
|
|
4409
|
+
/* @__PURE__ */ jsx("span", { "data-boff-explore": "copy-status", role: "status", className: "sr-only", children: outcome === "idle" ? "" : label })
|
|
4410
|
+
]
|
|
4411
|
+
}
|
|
4412
|
+
);
|
|
4413
|
+
}
|
|
3401
4414
|
function UserBubble({
|
|
3402
4415
|
strings: T,
|
|
3403
4416
|
message
|
|
@@ -3443,6 +4456,8 @@ function AssistantBubble({
|
|
|
3443
4456
|
message,
|
|
3444
4457
|
activity,
|
|
3445
4458
|
imageOrigins,
|
|
4459
|
+
productNames,
|
|
4460
|
+
speechLang,
|
|
3446
4461
|
onNavigate,
|
|
3447
4462
|
anchorRef,
|
|
3448
4463
|
onFollow,
|
|
@@ -3453,6 +4468,7 @@ function AssistantBubble({
|
|
|
3453
4468
|
const body = message.content || message.partialContent || "";
|
|
3454
4469
|
const failed = message.status === "FAILED";
|
|
3455
4470
|
const completed = message.status === "COMPLETED";
|
|
4471
|
+
const visible = useProgressiveReveal(body, !streaming);
|
|
3456
4472
|
const [failedImages, setFailedImages] = useState(
|
|
3457
4473
|
() => /* @__PURE__ */ new Set()
|
|
3458
4474
|
);
|
|
@@ -3461,7 +4477,7 @@ function AssistantBubble({
|
|
|
3461
4477
|
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
3462
4478
|
);
|
|
3463
4479
|
}, []);
|
|
3464
|
-
const
|
|
4480
|
+
const answerScope = useMemo(
|
|
3465
4481
|
() => ({
|
|
3466
4482
|
strings: T,
|
|
3467
4483
|
enabled: completed,
|
|
@@ -3471,6 +4487,19 @@ function AssistantBubble({
|
|
|
3471
4487
|
}),
|
|
3472
4488
|
[T, completed, imageOrigins, failedImages, markImageFailed]
|
|
3473
4489
|
);
|
|
4490
|
+
const provenance = useMemo(
|
|
4491
|
+
() => summariseReferences(message.references, imageOrigins, productNames),
|
|
4492
|
+
[message.references, imageOrigins, productNames]
|
|
4493
|
+
);
|
|
4494
|
+
const copyText = useMemo(
|
|
4495
|
+
() => answerPlainText(message.content),
|
|
4496
|
+
[message.content]
|
|
4497
|
+
);
|
|
4498
|
+
const speechText = useMemo(
|
|
4499
|
+
() => answerSpeechText(message.content),
|
|
4500
|
+
[message.content]
|
|
4501
|
+
);
|
|
4502
|
+
const grounded = completed && message.answered !== false;
|
|
3474
4503
|
return /* @__PURE__ */ jsxs(
|
|
3475
4504
|
"div",
|
|
3476
4505
|
{
|
|
@@ -3516,10 +4545,11 @@ function AssistantBubble({
|
|
|
3516
4545
|
]
|
|
3517
4546
|
}
|
|
3518
4547
|
) : null,
|
|
3519
|
-
|
|
3520
|
-
/* @__PURE__ */ jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsx(
|
|
4548
|
+
visible ? /* @__PURE__ */ jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
|
|
4549
|
+
/* @__PURE__ */ jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsx(AnswerScopeContext.Provider, { value: answerScope, children: /* @__PURE__ */ jsx(Markdown, { components: ANSWER_MARKDOWN_COMPONENTS, children: visible }) }) }),
|
|
3521
4550
|
streaming ? /* @__PURE__ */ jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
|
|
3522
4551
|
] }) : null,
|
|
4552
|
+
grounded && provenance.total > 0 ? /* @__PURE__ */ jsx(ProvenanceChips, { strings: T, provenance }) : null,
|
|
3523
4553
|
streaming ? /* @__PURE__ */ jsx(ThinkingIndicator, { activity }) : null,
|
|
3524
4554
|
failed && !body ? /* @__PURE__ */ jsx("div", { className: "rounded-2xl rounded-tl-md border border-border bg-muted px-4 py-3 text-sm text-muted-foreground", children: T.answerDidNotArrive }) : null,
|
|
3525
4555
|
message.references.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -3559,6 +4589,15 @@ function AssistantBubble({
|
|
|
3559
4589
|
question
|
|
3560
4590
|
)) })
|
|
3561
4591
|
] }) : null,
|
|
4592
|
+
completed && copyText !== "" ? /* @__PURE__ */ jsx(
|
|
4593
|
+
AnswerActions,
|
|
4594
|
+
{
|
|
4595
|
+
strings: T,
|
|
4596
|
+
text: copyText,
|
|
4597
|
+
speechText,
|
|
4598
|
+
speechLang
|
|
4599
|
+
}
|
|
4600
|
+
) : null,
|
|
3562
4601
|
completed && onFeedback ? /* @__PURE__ */ jsx(
|
|
3563
4602
|
AnswerFeedback,
|
|
3564
4603
|
{
|
|
@@ -3771,6 +4810,96 @@ function ProductStrip({
|
|
|
3771
4810
|
}
|
|
3772
4811
|
);
|
|
3773
4812
|
}
|
|
4813
|
+
var MAX_CONCEPT_CARDS = 6;
|
|
4814
|
+
function ConceptStrip({
|
|
4815
|
+
strings: T,
|
|
4816
|
+
highlights,
|
|
4817
|
+
onAsk,
|
|
4818
|
+
disabled
|
|
4819
|
+
}) {
|
|
4820
|
+
const [failed, setFailed] = useState(() => /* @__PURE__ */ new Set());
|
|
4821
|
+
const markFailed = useCallback((src) => {
|
|
4822
|
+
setFailed(
|
|
4823
|
+
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
4824
|
+
);
|
|
4825
|
+
}, []);
|
|
4826
|
+
const visible = highlights.filter(
|
|
4827
|
+
(highlight) => highlight.imageUrl !== "" && !failed.has(highlight.imageUrl)
|
|
4828
|
+
).slice(0, MAX_CONCEPT_CARDS);
|
|
4829
|
+
if (visible.length === 0) return null;
|
|
4830
|
+
return /* @__PURE__ */ jsxs(
|
|
4831
|
+
"div",
|
|
4832
|
+
{
|
|
4833
|
+
"data-boff-explore": "concept-strip-block",
|
|
4834
|
+
className: "boff-explore-rise boff-explore-rise-1 mt-6",
|
|
4835
|
+
children: [
|
|
4836
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: T.conceptStripTitle }),
|
|
4837
|
+
/* @__PURE__ */ jsx(
|
|
4838
|
+
"ul",
|
|
4839
|
+
{
|
|
4840
|
+
"data-boff-explore": "concept-strip",
|
|
4841
|
+
"aria-label": T.conceptStripLabel,
|
|
4842
|
+
className: "boff-explore-strip flex min-w-0 snap-x gap-3 overflow-x-auto pb-1",
|
|
4843
|
+
children: visible.map((highlight) => /* @__PURE__ */ jsx(
|
|
4844
|
+
"li",
|
|
4845
|
+
{
|
|
4846
|
+
className: "shrink-0 snap-start",
|
|
4847
|
+
children: /* @__PURE__ */ jsxs(
|
|
4848
|
+
"button",
|
|
4849
|
+
{
|
|
4850
|
+
"data-boff-explore": "concept-card",
|
|
4851
|
+
"data-product": highlight.product,
|
|
4852
|
+
type: "button",
|
|
4853
|
+
disabled,
|
|
4854
|
+
"aria-label": fill(T.conceptAsk, {
|
|
4855
|
+
title: highlight.title,
|
|
4856
|
+
product: highlight.productName
|
|
4857
|
+
}),
|
|
4858
|
+
onClick: () => onAsk(
|
|
4859
|
+
fill(T.conceptAskQuestion, {
|
|
4860
|
+
title: highlight.title,
|
|
4861
|
+
product: highlight.productName
|
|
4862
|
+
})
|
|
4863
|
+
),
|
|
4864
|
+
className: "group flex w-40 flex-col overflow-hidden rounded-xl border border-border bg-card text-left shadow-sm transition-colors hover:border-primary/50 hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:w-48",
|
|
4865
|
+
children: [
|
|
4866
|
+
/* @__PURE__ */ jsxs("span", { className: "relative block aspect-[16/9] w-full overflow-hidden bg-muted", children: [
|
|
4867
|
+
/* @__PURE__ */ jsx(
|
|
4868
|
+
"img",
|
|
4869
|
+
{
|
|
4870
|
+
src: highlight.imageUrl,
|
|
4871
|
+
alt: highlight.alt,
|
|
4872
|
+
loading: "lazy",
|
|
4873
|
+
decoding: "async",
|
|
4874
|
+
onError: () => markFailed(highlight.imageUrl),
|
|
4875
|
+
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
4876
|
+
}
|
|
4877
|
+
),
|
|
4878
|
+
/* @__PURE__ */ jsx(
|
|
4879
|
+
"span",
|
|
4880
|
+
{
|
|
4881
|
+
"aria-hidden": "true",
|
|
4882
|
+
className: "pointer-events-none absolute left-1.5 top-1.5 rounded-full border border-border bg-background/90 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-foreground",
|
|
4883
|
+
children: T.conceptIllustration
|
|
4884
|
+
}
|
|
4885
|
+
)
|
|
4886
|
+
] }),
|
|
4887
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5 p-2.5", children: [
|
|
4888
|
+
/* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-xs font-semibold leading-snug text-card-foreground", children: highlight.title }),
|
|
4889
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: highlight.productName })
|
|
4890
|
+
] })
|
|
4891
|
+
]
|
|
4892
|
+
}
|
|
4893
|
+
)
|
|
4894
|
+
},
|
|
4895
|
+
`${highlight.product}:${highlight.url}`
|
|
4896
|
+
))
|
|
4897
|
+
}
|
|
4898
|
+
)
|
|
4899
|
+
]
|
|
4900
|
+
}
|
|
4901
|
+
);
|
|
4902
|
+
}
|
|
3774
4903
|
function DisclaimerDialog({
|
|
3775
4904
|
open,
|
|
3776
4905
|
onClose,
|
|
@@ -3877,6 +5006,7 @@ function WelcomeState({
|
|
|
3877
5006
|
body,
|
|
3878
5007
|
prompts,
|
|
3879
5008
|
products,
|
|
5009
|
+
conceptHighlights,
|
|
3880
5010
|
focusProduct,
|
|
3881
5011
|
onPrompt,
|
|
3882
5012
|
onFocus,
|
|
@@ -3891,6 +5021,15 @@ function WelcomeState({
|
|
|
3891
5021
|
/* @__PURE__ */ jsx("h1", { className: "mt-4 text-balance break-words text-2xl font-bold tracking-tight sm:text-4xl", children: title }),
|
|
3892
5022
|
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-2.5 max-w-2xl text-pretty text-sm leading-relaxed text-muted-foreground sm:text-lg", children: body })
|
|
3893
5023
|
] }),
|
|
5024
|
+
/* @__PURE__ */ jsx(
|
|
5025
|
+
ConceptStrip,
|
|
5026
|
+
{
|
|
5027
|
+
strings: T,
|
|
5028
|
+
highlights: conceptHighlights,
|
|
5029
|
+
onAsk: onPrompt,
|
|
5030
|
+
disabled
|
|
5031
|
+
}
|
|
5032
|
+
),
|
|
3894
5033
|
/* @__PURE__ */ jsx("ul", { className: "boff-explore-rise boff-explore-rise-1 mt-6 grid grid-cols-3 gap-2 sm:mt-7 sm:gap-2.5", children: CAPABILITIES.map(({ icon: Icon, body: capBody }, i) => /* @__PURE__ */ jsxs(
|
|
3895
5034
|
"li",
|
|
3896
5035
|
{
|
|
@@ -3952,6 +5091,7 @@ function ExplorePage({
|
|
|
3952
5091
|
);
|
|
3953
5092
|
const T = useMemo(() => exploreStrings(activeLocale), [activeLocale]);
|
|
3954
5093
|
const rtl = isRtlLocale(activeLocale);
|
|
5094
|
+
const speechLang = speechLocaleOf(activeLocale);
|
|
3955
5095
|
const {
|
|
3956
5096
|
phase,
|
|
3957
5097
|
catalog,
|
|
@@ -4036,6 +5176,10 @@ function ExplorePage({
|
|
|
4036
5176
|
() => inlineImageOrigins(catalog.products),
|
|
4037
5177
|
[catalog.products]
|
|
4038
5178
|
);
|
|
5179
|
+
const productNames = useMemo(
|
|
5180
|
+
() => productNamesByOrigin(catalog.products),
|
|
5181
|
+
[catalog.products]
|
|
5182
|
+
);
|
|
4039
5183
|
useEffect(() => {
|
|
4040
5184
|
const el = textareaRef.current;
|
|
4041
5185
|
if (!el) return;
|
|
@@ -4052,11 +5196,11 @@ function ExplorePage({
|
|
|
4052
5196
|
const layoutHeight = window.innerHeight;
|
|
4053
5197
|
if (layoutHeight === 0) return;
|
|
4054
5198
|
const top = Math.round(
|
|
4055
|
-
|
|
5199
|
+
clamp2(el.getBoundingClientRect().top, 0, layoutHeight)
|
|
4056
5200
|
);
|
|
4057
5201
|
const viewport = window.visualViewport;
|
|
4058
5202
|
const inset = viewport ? Math.round(
|
|
4059
|
-
|
|
5203
|
+
clamp2(
|
|
4060
5204
|
layoutHeight - viewport.height - viewport.offsetTop,
|
|
4061
5205
|
0,
|
|
4062
5206
|
layoutHeight
|
|
@@ -4337,6 +5481,7 @@ function ExplorePage({
|
|
|
4337
5481
|
const text = draft.trim();
|
|
4338
5482
|
if (!text || overLimit || busy || !canSend) return;
|
|
4339
5483
|
stopDictation();
|
|
5484
|
+
stopSpeaking();
|
|
4340
5485
|
setDraft("");
|
|
4341
5486
|
stickToBottomRef.current = true;
|
|
4342
5487
|
void send(text, toPayload(stagedRef.current));
|
|
@@ -4347,6 +5492,7 @@ function ExplorePage({
|
|
|
4347
5492
|
const sendPrompt = useCallback(
|
|
4348
5493
|
(prompt) => {
|
|
4349
5494
|
if (!canSend || busy) return;
|
|
5495
|
+
stopSpeaking();
|
|
4350
5496
|
setDraft("");
|
|
4351
5497
|
stickToBottomRef.current = true;
|
|
4352
5498
|
void send(prompt, toPayload(stagedRef.current));
|
|
@@ -4567,6 +5713,7 @@ function ExplorePage({
|
|
|
4567
5713
|
body: resolvedBody,
|
|
4568
5714
|
prompts,
|
|
4569
5715
|
products: catalog.products,
|
|
5716
|
+
conceptHighlights: catalog.conceptHighlights,
|
|
4570
5717
|
focusProduct,
|
|
4571
5718
|
onPrompt: sendPrompt,
|
|
4572
5719
|
onFocus: setFocusProduct,
|
|
@@ -4580,6 +5727,8 @@ function ExplorePage({
|
|
|
4580
5727
|
message,
|
|
4581
5728
|
activity,
|
|
4582
5729
|
imageOrigins,
|
|
5730
|
+
productNames,
|
|
5731
|
+
speechLang,
|
|
4583
5732
|
onNavigate,
|
|
4584
5733
|
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
|
|
4585
5734
|
onFollow: (kind, url) => recordClick(message.id, kind, url),
|
|
@@ -4882,6 +6031,6 @@ function ExplorePage({
|
|
|
4882
6031
|
);
|
|
4883
6032
|
}
|
|
4884
6033
|
|
|
4885
|
-
export { ExplorePage, inlineImageOrigins, isInlineImageAllowed, logoUrlOf };
|
|
6034
|
+
export { ExplorePage, answerPlainText, answerSpeechText, inlineImageOrigins, isInlineImageAllowed, logoUrlOf, productNamesByOrigin, referenceKindOf, summariseReferences };
|
|
4886
6035
|
//# sourceMappingURL=explore.mjs.map
|
|
4887
6036
|
//# sourceMappingURL=explore.mjs.map
|