@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
|
@@ -103,6 +103,23 @@ var EN = {
|
|
|
103
103
|
nextSuggestion: "Next suggestion",
|
|
104
104
|
conceptIllustration: "Concept illustration",
|
|
105
105
|
opensInNewTab: "opens in a new tab",
|
|
106
|
+
conceptStripTitle: "Or start from a concept",
|
|
107
|
+
conceptStripLabel: "Concepts you can ask about",
|
|
108
|
+
conceptAsk: "Ask about {title} in {product}",
|
|
109
|
+
conceptAskQuestion: "How does {title} work in {product}?",
|
|
110
|
+
copyAnswer: "Copy answer",
|
|
111
|
+
copyFailed: "Couldn't copy",
|
|
112
|
+
readAloud: "Read aloud",
|
|
113
|
+
stopReading: "Stop reading",
|
|
114
|
+
answerTable: "Table",
|
|
115
|
+
groundedIn: "What this answer is based on",
|
|
116
|
+
sourceCountOne: "1 source",
|
|
117
|
+
sourceCountMany: "{count} sources",
|
|
118
|
+
sourceKindConcept: "Concept",
|
|
119
|
+
sourceKindDocs: "Documentation",
|
|
120
|
+
sourceKindPricing: "Pricing",
|
|
121
|
+
sourceKindUseCase: "Use case",
|
|
122
|
+
sourceKindPage: "Product page",
|
|
106
123
|
seoTitle: "Describe a problem, get an answer",
|
|
107
124
|
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.",
|
|
108
125
|
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.",
|
|
@@ -171,6 +188,23 @@ var ZH = {
|
|
|
171
188
|
nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
|
|
172
189
|
conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
|
|
173
190
|
opensInNewTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00",
|
|
191
|
+
conceptStripTitle: "\u6216\u8005\u4ECE\u4E00\u4E2A\u6982\u5FF5\u5F00\u59CB",
|
|
192
|
+
conceptStripLabel: "\u4F60\u53EF\u4EE5\u8BE2\u95EE\u7684\u6982\u5FF5",
|
|
193
|
+
conceptAsk: "\u8BE2\u95EE {product} \u4E2D\u7684 {title}",
|
|
194
|
+
conceptAskQuestion: "{product} \u7684 {title} \u662F\u600E\u4E48\u8FD0\u4F5C\u7684\uFF1F",
|
|
195
|
+
copyAnswer: "\u590D\u5236\u56DE\u7B54",
|
|
196
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
197
|
+
readAloud: "\u6717\u8BFB",
|
|
198
|
+
stopReading: "\u505C\u6B62\u6717\u8BFB",
|
|
199
|
+
answerTable: "\u8868\u683C",
|
|
200
|
+
groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
|
|
201
|
+
sourceCountOne: "1 \u4E2A\u6765\u6E90",
|
|
202
|
+
sourceCountMany: "{count} \u4E2A\u6765\u6E90",
|
|
203
|
+
sourceKindConcept: "\u6982\u5FF5",
|
|
204
|
+
sourceKindDocs: "\u6587\u6863",
|
|
205
|
+
sourceKindPricing: "\u4EF7\u683C",
|
|
206
|
+
sourceKindUseCase: "\u5E94\u7528\u573A\u666F",
|
|
207
|
+
sourceKindPage: "\u4EA7\u54C1\u9875\u9762",
|
|
174
208
|
seoTitle: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u83B7\u5F97\u7B54\u6848",
|
|
175
209
|
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",
|
|
176
210
|
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",
|
|
@@ -235,6 +269,23 @@ var HI = {
|
|
|
235
269
|
nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
|
|
236
270
|
conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
|
|
237
271
|
opensInNewTab: "\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948",
|
|
272
|
+
conceptStripTitle: "\u092F\u093E \u0915\u093F\u0938\u0940 \u0905\u0935\u0927\u093E\u0930\u0923\u093E \u0938\u0947 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
|
|
273
|
+
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",
|
|
274
|
+
conceptAsk: "{product} \u092E\u0947\u0902 {title} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902",
|
|
275
|
+
conceptAskQuestion: "{product} \u092E\u0947\u0902 {title} \u0915\u0948\u0938\u0947 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948?",
|
|
276
|
+
copyAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
|
|
277
|
+
copyFailed: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E",
|
|
278
|
+
readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
|
|
279
|
+
stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
280
|
+
answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
|
|
281
|
+
groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
|
|
282
|
+
sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
|
|
283
|
+
sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
|
|
284
|
+
sourceKindConcept: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E",
|
|
285
|
+
sourceKindDocs: "\u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C",
|
|
286
|
+
sourceKindPricing: "\u092E\u0942\u0932\u094D\u092F",
|
|
287
|
+
sourceKindUseCase: "\u0909\u092A\u092F\u094B\u0917 \u0915\u093E \u0909\u0926\u093E\u0939\u0930\u0923",
|
|
288
|
+
sourceKindPage: "\u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0947\u091C",
|
|
238
289
|
seoTitle: "\u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902, \u091C\u0935\u093E\u092C \u092A\u093E\u090F\u0902",
|
|
239
290
|
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",
|
|
240
291
|
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",
|
|
@@ -303,6 +354,23 @@ var ES = {
|
|
|
303
354
|
nextSuggestion: "Sugerencia siguiente",
|
|
304
355
|
conceptIllustration: "Ilustraci\xF3n del concepto",
|
|
305
356
|
opensInNewTab: "se abre en una pesta\xF1a nueva",
|
|
357
|
+
conceptStripTitle: "O empieza por un concepto",
|
|
358
|
+
conceptStripLabel: "Conceptos sobre los que puedes preguntar",
|
|
359
|
+
conceptAsk: "Preguntar por {title} en {product}",
|
|
360
|
+
conceptAskQuestion: "\xBFC\xF3mo funciona {title} en {product}?",
|
|
361
|
+
copyAnswer: "Copiar respuesta",
|
|
362
|
+
copyFailed: "No se pudo copiar",
|
|
363
|
+
readAloud: "Leer en voz alta",
|
|
364
|
+
stopReading: "Dejar de leer",
|
|
365
|
+
answerTable: "Tabla",
|
|
366
|
+
groundedIn: "En qu\xE9 se basa esta respuesta",
|
|
367
|
+
sourceCountOne: "1 fuente",
|
|
368
|
+
sourceCountMany: "{count} fuentes",
|
|
369
|
+
sourceKindConcept: "Concepto",
|
|
370
|
+
sourceKindDocs: "Documentaci\xF3n",
|
|
371
|
+
sourceKindPricing: "Precios",
|
|
372
|
+
sourceKindUseCase: "Caso de uso",
|
|
373
|
+
sourceKindPage: "P\xE1gina de producto",
|
|
306
374
|
seoTitle: "Describe un problema y obt\xE9n una respuesta",
|
|
307
375
|
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.",
|
|
308
376
|
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.",
|
|
@@ -371,6 +439,23 @@ var AR = {
|
|
|
371
439
|
nextSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u062A\u0627\u0644\u064A",
|
|
372
440
|
conceptIllustration: "\u0631\u0633\u0645 \u062A\u0648\u0636\u064A\u062D\u064A \u0644\u0644\u0645\u0641\u0647\u0648\u0645",
|
|
373
441
|
opensInNewTab: "\u064A\u064F\u0641\u062A\u062D \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u062C\u062F\u064A\u062F\u0629",
|
|
442
|
+
conceptStripTitle: "\u0623\u0648 \u0627\u0628\u062F\u0623 \u0645\u0646 \u0645\u0641\u0647\u0648\u0645",
|
|
443
|
+
conceptStripLabel: "\u0645\u0641\u0627\u0647\u064A\u0645 \u064A\u0645\u0643\u0646\u0643 \u0627\u0644\u0633\u0624\u0627\u0644 \u0639\u0646\u0647\u0627",
|
|
444
|
+
conceptAsk: "\u0627\u0633\u0623\u0644 \u0639\u0646 {title} \u0641\u064A {product}",
|
|
445
|
+
conceptAskQuestion: "\u0643\u064A\u0641 \u064A\u0639\u0645\u0644 {title} \u0641\u064A {product}\u061F",
|
|
446
|
+
copyAnswer: "\u0646\u0633\u062E \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
447
|
+
copyFailed: "\u062A\u0639\u0630\u0651\u0631 \u0627\u0644\u0646\u0633\u062E",
|
|
448
|
+
readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
|
|
449
|
+
stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
|
|
450
|
+
answerTable: "\u062C\u062F\u0648\u0644",
|
|
451
|
+
groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
452
|
+
sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
|
|
453
|
+
sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
|
|
454
|
+
sourceKindConcept: "\u0645\u0641\u0647\u0648\u0645",
|
|
455
|
+
sourceKindDocs: "\u0627\u0644\u062A\u0648\u062B\u064A\u0642",
|
|
456
|
+
sourceKindPricing: "\u0627\u0644\u0623\u0633\u0639\u0627\u0631",
|
|
457
|
+
sourceKindUseCase: "\u062D\u0627\u0644\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645",
|
|
458
|
+
sourceKindPage: "\u0635\u0641\u062D\u0629 \u0627\u0644\u0645\u0646\u062A\u062C",
|
|
374
459
|
seoTitle: "\u0635\u0641 \u0645\u0634\u0643\u0644\u062A\u0643 \u0648\u0627\u062D\u0635\u0644 \u0639\u0644\u0649 \u0625\u062C\u0627\u0628\u0629",
|
|
375
460
|
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.",
|
|
376
461
|
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.",
|
|
@@ -439,6 +524,23 @@ var TA = {
|
|
|
439
524
|
nextSuggestion: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
|
|
440
525
|
conceptIllustration: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BB5\u0BBF\u0BB3\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BAE\u0BCD",
|
|
441
526
|
opensInNewTab: "\u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0BA4\u0BBE\u0BB5\u0BB2\u0BBF\u0BB2\u0BCD \u0BA4\u0BBF\u0BB1\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD",
|
|
527
|
+
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",
|
|
528
|
+
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",
|
|
529
|
+
conceptAsk: "{product} \u0B87\u0BB2\u0BCD {title} \u0BAA\u0BB1\u0BCD\u0BB1\u0BBF\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
530
|
+
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?",
|
|
531
|
+
copyAnswer: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1",
|
|
532
|
+
copyFailed: "\u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8",
|
|
533
|
+
readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
|
|
534
|
+
stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
535
|
+
answerTable: "\u0B85\u0B9F\u0BCD\u0B9F\u0BB5\u0BA3\u0BC8",
|
|
536
|
+
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",
|
|
537
|
+
sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
|
|
538
|
+
sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
539
|
+
sourceKindConcept: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
540
|
+
sourceKindDocs: "\u0B86\u0BB5\u0BA3\u0BAE\u0BCD",
|
|
541
|
+
sourceKindPricing: "\u0BB5\u0BBF\u0BB2\u0BC8",
|
|
542
|
+
sourceKindUseCase: "\u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1 \u0BA8\u0BBF\u0BB2\u0BC8",
|
|
543
|
+
sourceKindPage: "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1\u0BAA\u0BCD \u0BAA\u0B95\u0BCD\u0B95\u0BAE\u0BCD",
|
|
442
544
|
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",
|
|
443
545
|
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.",
|
|
444
546
|
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.",
|
|
@@ -462,6 +564,17 @@ var STRINGS = {
|
|
|
462
564
|
ar: AR,
|
|
463
565
|
ta: TA
|
|
464
566
|
};
|
|
567
|
+
var SPEECH_TAGS = {
|
|
568
|
+
en: "en-US",
|
|
569
|
+
zh: "zh-CN",
|
|
570
|
+
hi: "hi-IN",
|
|
571
|
+
es: "es-ES",
|
|
572
|
+
ar: "ar-SA",
|
|
573
|
+
ta: "ta-IN"
|
|
574
|
+
};
|
|
575
|
+
function speechLocaleOf(locale) {
|
|
576
|
+
return SPEECH_TAGS[locale];
|
|
577
|
+
}
|
|
465
578
|
function resolveExploreLocale(explicit) {
|
|
466
579
|
const candidates = [
|
|
467
580
|
explicit,
|
|
@@ -540,11 +653,9 @@ var EXPLORE_OWN_MESSAGE_FIELDS = (
|
|
|
540
653
|
}
|
|
541
654
|
`
|
|
542
655
|
);
|
|
543
|
-
var
|
|
656
|
+
var EXPLORE_CATALOG_ESTABLISHED_FIELDS = (
|
|
544
657
|
/* GraphQL */
|
|
545
658
|
`
|
|
546
|
-
query ExploreCatalog($productSlug: String) {
|
|
547
|
-
exploreCatalog(productSlug: $productSlug) {
|
|
548
659
|
enabled
|
|
549
660
|
products {
|
|
550
661
|
slug
|
|
@@ -562,11 +673,35 @@ var EXPLORE_CATALOG_QUERY = (
|
|
|
562
673
|
historyTurns
|
|
563
674
|
}
|
|
564
675
|
captchaRequired
|
|
565
|
-
recaptchaSiteKey
|
|
676
|
+
recaptchaSiteKey`
|
|
677
|
+
);
|
|
678
|
+
var EXPLORE_CATALOG_HIGHLIGHT_FIELD = (
|
|
679
|
+
/* GraphQL */
|
|
680
|
+
`
|
|
681
|
+
conceptHighlights {
|
|
682
|
+
product
|
|
683
|
+
productName
|
|
684
|
+
title
|
|
685
|
+
url
|
|
686
|
+
imageUrl
|
|
687
|
+
alt
|
|
688
|
+
}`
|
|
689
|
+
);
|
|
690
|
+
function exploreCatalogDocument(extraFields) {
|
|
691
|
+
return (
|
|
692
|
+
/* GraphQL */
|
|
693
|
+
`
|
|
694
|
+
query ExploreCatalog($productSlug: String) {
|
|
695
|
+
exploreCatalog(productSlug: $productSlug) {${EXPLORE_CATALOG_ESTABLISHED_FIELDS}${extraFields}
|
|
566
696
|
}
|
|
567
697
|
}
|
|
568
698
|
`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
var EXPLORE_CATALOG_QUERY = exploreCatalogDocument(
|
|
702
|
+
EXPLORE_CATALOG_HIGHLIGHT_FIELD
|
|
569
703
|
);
|
|
704
|
+
var EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS = exploreCatalogDocument("");
|
|
570
705
|
var EXPLORE_MESSAGE_QUERY = (
|
|
571
706
|
/* GraphQL */
|
|
572
707
|
`
|
|
@@ -1369,7 +1504,12 @@ function buildFallbackCatalog(examplePrompts, welcome) {
|
|
|
1369
1504
|
welcomeBody: welcome?.body ?? FALLBACK_WELCOME_BODY,
|
|
1370
1505
|
limits: FALLBACK_EXPLORE_LIMITS,
|
|
1371
1506
|
captchaRequired: false,
|
|
1372
|
-
recaptchaSiteKey: null
|
|
1507
|
+
recaptchaSiteKey: null,
|
|
1508
|
+
// Empty, and deliberately not synthesised from `ECOSYSTEM_PRODUCTS`: a concept highlight
|
|
1509
|
+
// needs a real published illustration URL, and this SDK has no registry of them. An
|
|
1510
|
+
// invented path would render 38 broken frames on the one screen every prerender captures.
|
|
1511
|
+
// The strip renders nothing at all when the list is empty, which is the honest fallback.
|
|
1512
|
+
conceptHighlights: []
|
|
1373
1513
|
};
|
|
1374
1514
|
}
|
|
1375
1515
|
function normalizeCatalog(raw, fallback) {
|
|
@@ -1386,9 +1526,27 @@ function normalizeCatalog(raw, fallback) {
|
|
|
1386
1526
|
historyTurns: raw.limits?.historyTurns || fallback.limits.historyTurns
|
|
1387
1527
|
},
|
|
1388
1528
|
captchaRequired: raw.captchaRequired === true,
|
|
1389
|
-
recaptchaSiteKey: raw.recaptchaSiteKey ?? null
|
|
1529
|
+
recaptchaSiteKey: raw.recaptchaSiteKey ?? null,
|
|
1530
|
+
// `[ExploreConceptHighlight!]!` on the wire, so a gateway that KNOWS the field always
|
|
1531
|
+
// sends an array. The one response that arrives without it is the retry against an older
|
|
1532
|
+
// supergraph (`EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS`) — that gateway does not answer
|
|
1533
|
+
// the full document at all, it refuses it. Either way
|
|
1534
|
+
// `catalog.conceptHighlights.filter(...)` on `undefined` would throw inside render,
|
|
1535
|
+
// blanking the welcome screen over a decorative strip.
|
|
1536
|
+
conceptHighlights: raw.conceptHighlights?.length ? raw.conceptHighlights : fallback.conceptHighlights
|
|
1390
1537
|
};
|
|
1391
1538
|
}
|
|
1539
|
+
var CATALOG_DOCUMENT_REJECTED_CODES = /* @__PURE__ */ new Set([
|
|
1540
|
+
"GRAPHQL_VALIDATION_FAILED",
|
|
1541
|
+
"HTTP_400"
|
|
1542
|
+
]);
|
|
1543
|
+
function catalogRejectedTheHighlightField(res) {
|
|
1544
|
+
if (res.data?.exploreCatalog) return false;
|
|
1545
|
+
return (res.errors ?? []).some((error) => {
|
|
1546
|
+
const code = error.extensions?.code;
|
|
1547
|
+
return typeof code === "string" && CATALOG_DOCUMENT_REJECTED_CODES.has(code) || error.message.includes("conceptHighlights");
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1392
1550
|
var RATE_LIMIT_CODES = /* @__PURE__ */ new Set([
|
|
1393
1551
|
"RATE_LIMITED",
|
|
1394
1552
|
"HTTP_429",
|
|
@@ -1728,11 +1886,19 @@ function useExploreChat(options = {}) {
|
|
|
1728
1886
|
React.useEffect(() => {
|
|
1729
1887
|
let cancelled = false;
|
|
1730
1888
|
const load = async () => {
|
|
1731
|
-
const
|
|
1889
|
+
const variables = { productSlug: productSlug ?? null };
|
|
1890
|
+
let res = await client.query(
|
|
1732
1891
|
EXPLORE_CATALOG_QUERY,
|
|
1733
|
-
|
|
1892
|
+
variables
|
|
1734
1893
|
);
|
|
1735
1894
|
if (cancelled || !mountedRef.current) return;
|
|
1895
|
+
if (catalogRejectedTheHighlightField(res)) {
|
|
1896
|
+
res = await client.query(
|
|
1897
|
+
EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,
|
|
1898
|
+
variables
|
|
1899
|
+
);
|
|
1900
|
+
if (cancelled || !mountedRef.current) return;
|
|
1901
|
+
}
|
|
1736
1902
|
const raw = res.data?.exploreCatalog;
|
|
1737
1903
|
if (res.errors?.length || !raw) {
|
|
1738
1904
|
console.warn(
|
|
@@ -2178,6 +2344,224 @@ function makeOptimisticId() {
|
|
|
2178
2344
|
optimisticCounter += 1;
|
|
2179
2345
|
return `boff-explore-local-${Date.now()}-${optimisticCounter}`;
|
|
2180
2346
|
}
|
|
2347
|
+
var DRAIN_TARGET_MS = 1200;
|
|
2348
|
+
var MIN_CHARS_PER_SECOND = 90;
|
|
2349
|
+
var MAX_CHARS_PER_SECOND = 2200;
|
|
2350
|
+
var MAX_STEP_MS = 250;
|
|
2351
|
+
var COMMIT_INTERVAL_MS = 45;
|
|
2352
|
+
var FALLBACK_STEP_MS = 50;
|
|
2353
|
+
var FENCE = /^ {0,3}(?:```|~~~)/;
|
|
2354
|
+
var TABLE_ROW = /^ {0,3}\|/;
|
|
2355
|
+
var EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\d{1,9}[.)])\s*$/;
|
|
2356
|
+
function isWordCharacter(char) {
|
|
2357
|
+
return char !== void 0 && /[\p{L}\p{N}]/u.test(char);
|
|
2358
|
+
}
|
|
2359
|
+
function clamp(value, min, max) {
|
|
2360
|
+
return Math.min(Math.max(value, min), max);
|
|
2361
|
+
}
|
|
2362
|
+
function now() {
|
|
2363
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
2364
|
+
}
|
|
2365
|
+
function isHighSurrogate(code) {
|
|
2366
|
+
return code >= 55296 && code <= 56319;
|
|
2367
|
+
}
|
|
2368
|
+
function safeInlineEnd(line) {
|
|
2369
|
+
let codeSpan = null;
|
|
2370
|
+
let link = null;
|
|
2371
|
+
const emphasis = [];
|
|
2372
|
+
let at = 0;
|
|
2373
|
+
while (at < line.length) {
|
|
2374
|
+
const char = line[at] ?? "";
|
|
2375
|
+
if (char === "\\") {
|
|
2376
|
+
at += 2;
|
|
2377
|
+
continue;
|
|
2378
|
+
}
|
|
2379
|
+
if (char === "`") {
|
|
2380
|
+
let run = 1;
|
|
2381
|
+
while (line[at + run] === "`") run += 1;
|
|
2382
|
+
codeSpan = codeSpan === null ? at : null;
|
|
2383
|
+
at += run;
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
if (codeSpan !== null) {
|
|
2387
|
+
at += 1;
|
|
2388
|
+
continue;
|
|
2389
|
+
}
|
|
2390
|
+
if (char === "[") {
|
|
2391
|
+
if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
|
|
2392
|
+
at += 1;
|
|
2393
|
+
continue;
|
|
2394
|
+
}
|
|
2395
|
+
if (char === "]") {
|
|
2396
|
+
if (link !== null && line[at + 1] !== "(") link = null;
|
|
2397
|
+
at += 1;
|
|
2398
|
+
continue;
|
|
2399
|
+
}
|
|
2400
|
+
if (char === ")") {
|
|
2401
|
+
link = null;
|
|
2402
|
+
at += 1;
|
|
2403
|
+
continue;
|
|
2404
|
+
}
|
|
2405
|
+
if (char === "*" || char === "_" || char === "~") {
|
|
2406
|
+
let run = 1;
|
|
2407
|
+
while (line[at + run] === char) run += 1;
|
|
2408
|
+
const start = at;
|
|
2409
|
+
at += run;
|
|
2410
|
+
const before = line[start - 1];
|
|
2411
|
+
const after = line[start + run];
|
|
2412
|
+
if (run < 2) continue;
|
|
2413
|
+
if (char === "_" && isWordCharacter(before) && isWordCharacter(after)) {
|
|
2414
|
+
continue;
|
|
2415
|
+
}
|
|
2416
|
+
const marker = char.repeat(run);
|
|
2417
|
+
const open2 = emphasis.findIndex((entry) => entry.marker === marker);
|
|
2418
|
+
if (open2 !== -1) {
|
|
2419
|
+
emphasis.splice(open2, 1);
|
|
2420
|
+
continue;
|
|
2421
|
+
}
|
|
2422
|
+
if (after !== void 0 && /\s/.test(after)) continue;
|
|
2423
|
+
emphasis.push({ marker, start });
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
at += 1;
|
|
2427
|
+
}
|
|
2428
|
+
const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
|
|
2429
|
+
(value) => value !== null
|
|
2430
|
+
);
|
|
2431
|
+
return open.length === 0 ? line.length : Math.min(...open);
|
|
2432
|
+
}
|
|
2433
|
+
function revealBoundary(text, limit) {
|
|
2434
|
+
if (limit <= 0) return 0;
|
|
2435
|
+
const caughtUp = limit >= text.length;
|
|
2436
|
+
const head = caughtUp ? text : text.slice(0, limit);
|
|
2437
|
+
const lineStart = head.lastIndexOf("\n") + 1;
|
|
2438
|
+
const line = head.slice(lineStart);
|
|
2439
|
+
let fenceStart = null;
|
|
2440
|
+
let tableStart = null;
|
|
2441
|
+
let tableRows = 0;
|
|
2442
|
+
let at = 0;
|
|
2443
|
+
while (at < lineStart) {
|
|
2444
|
+
const breakAt = head.indexOf("\n", at);
|
|
2445
|
+
const end = breakAt === -1 ? lineStart : breakAt;
|
|
2446
|
+
const row = head.slice(at, end);
|
|
2447
|
+
if (fenceStart !== null) {
|
|
2448
|
+
if (FENCE.test(row)) fenceStart = null;
|
|
2449
|
+
} else if (FENCE.test(row)) {
|
|
2450
|
+
fenceStart = at;
|
|
2451
|
+
} else if (TABLE_ROW.test(row)) {
|
|
2452
|
+
if (tableStart === null) {
|
|
2453
|
+
tableStart = at;
|
|
2454
|
+
tableRows = 0;
|
|
2455
|
+
}
|
|
2456
|
+
tableRows += 1;
|
|
2457
|
+
} else if (tableStart !== null) {
|
|
2458
|
+
tableStart = null;
|
|
2459
|
+
tableRows = 0;
|
|
2460
|
+
}
|
|
2461
|
+
at = end + 1;
|
|
2462
|
+
}
|
|
2463
|
+
if (fenceStart === null) {
|
|
2464
|
+
const lastIsRow = TABLE_ROW.test(line);
|
|
2465
|
+
if (lastIsRow && tableStart === null) {
|
|
2466
|
+
tableStart = lineStart;
|
|
2467
|
+
tableRows = 0;
|
|
2468
|
+
} else if (!lastIsRow && line !== "") {
|
|
2469
|
+
tableStart = null;
|
|
2470
|
+
tableRows = 0;
|
|
2471
|
+
}
|
|
2472
|
+
if (tableStart !== null && tableRows < 2) return tableStart;
|
|
2473
|
+
}
|
|
2474
|
+
if (caughtUp) return text.length;
|
|
2475
|
+
if (fenceStart !== null) return fenceStart;
|
|
2476
|
+
if (TABLE_ROW.test(line)) return lineStart;
|
|
2477
|
+
if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;
|
|
2478
|
+
const cut = lineStart + safeInlineEnd(line);
|
|
2479
|
+
return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;
|
|
2480
|
+
}
|
|
2481
|
+
function prefersReducedMotion() {
|
|
2482
|
+
if (typeof window === "undefined") return true;
|
|
2483
|
+
try {
|
|
2484
|
+
return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
2485
|
+
} catch {
|
|
2486
|
+
return false;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
function useProgressiveReveal(text, settled) {
|
|
2490
|
+
const [, setFrame] = React.useState(0);
|
|
2491
|
+
const [reduced] = React.useState(prefersReducedMotion);
|
|
2492
|
+
const immediate = settled || reduced;
|
|
2493
|
+
const textRef = React.useRef(text);
|
|
2494
|
+
const cursorRef = React.useRef(immediate ? text.length : 0);
|
|
2495
|
+
const cutRef = React.useRef(immediate ? text.length : 0);
|
|
2496
|
+
const committedRef = React.useRef(0);
|
|
2497
|
+
if (text !== textRef.current) {
|
|
2498
|
+
const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));
|
|
2499
|
+
if (revealed !== "" && !text.startsWith(revealed)) {
|
|
2500
|
+
cursorRef.current = text.length;
|
|
2501
|
+
}
|
|
2502
|
+
cursorRef.current = Math.min(cursorRef.current, text.length);
|
|
2503
|
+
cutRef.current = Math.min(cutRef.current, text.length);
|
|
2504
|
+
textRef.current = text;
|
|
2505
|
+
}
|
|
2506
|
+
if (immediate) cursorRef.current = text.length;
|
|
2507
|
+
const cut = immediate ? text.length : Math.max(
|
|
2508
|
+
cutRef.current,
|
|
2509
|
+
revealBoundary(text, Math.floor(cursorRef.current))
|
|
2510
|
+
);
|
|
2511
|
+
cutRef.current = cut;
|
|
2512
|
+
React.useEffect(() => {
|
|
2513
|
+
if (immediate) return;
|
|
2514
|
+
if (cursorRef.current >= text.length) return;
|
|
2515
|
+
let cancelled = false;
|
|
2516
|
+
let cancelStep = null;
|
|
2517
|
+
let previous = now();
|
|
2518
|
+
const commit = (at, force) => {
|
|
2519
|
+
if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;
|
|
2520
|
+
committedRef.current = at;
|
|
2521
|
+
setFrame((value) => value + 1);
|
|
2522
|
+
};
|
|
2523
|
+
const step = () => {
|
|
2524
|
+
cancelStep = null;
|
|
2525
|
+
if (cancelled) return;
|
|
2526
|
+
const at = now();
|
|
2527
|
+
const elapsed = clamp(at - previous, 0, MAX_STEP_MS);
|
|
2528
|
+
previous = at;
|
|
2529
|
+
const length = textRef.current.length;
|
|
2530
|
+
const pending = length - cursorRef.current;
|
|
2531
|
+
if (pending <= 0) {
|
|
2532
|
+
commit(at, true);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const perSecond = clamp(
|
|
2536
|
+
pending / (DRAIN_TARGET_MS / 1e3),
|
|
2537
|
+
MIN_CHARS_PER_SECOND,
|
|
2538
|
+
MAX_CHARS_PER_SECOND
|
|
2539
|
+
);
|
|
2540
|
+
cursorRef.current = Math.min(
|
|
2541
|
+
length,
|
|
2542
|
+
cursorRef.current + perSecond * elapsed / 1e3
|
|
2543
|
+
);
|
|
2544
|
+
const done = cursorRef.current >= length;
|
|
2545
|
+
commit(at, done);
|
|
2546
|
+
if (!done) schedule();
|
|
2547
|
+
};
|
|
2548
|
+
const schedule = () => {
|
|
2549
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
2550
|
+
const id2 = window.requestAnimationFrame(step);
|
|
2551
|
+
cancelStep = () => window.cancelAnimationFrame(id2);
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
const id = setTimeout(step, FALLBACK_STEP_MS);
|
|
2555
|
+
cancelStep = () => clearTimeout(id);
|
|
2556
|
+
};
|
|
2557
|
+
schedule();
|
|
2558
|
+
return () => {
|
|
2559
|
+
cancelled = true;
|
|
2560
|
+
cancelStep?.();
|
|
2561
|
+
};
|
|
2562
|
+
}, [text, immediate]);
|
|
2563
|
+
return text.slice(0, cut);
|
|
2564
|
+
}
|
|
2181
2565
|
function getRecognitionCtor() {
|
|
2182
2566
|
if (typeof window === "undefined") return null;
|
|
2183
2567
|
const w = window;
|
|
@@ -2396,6 +2780,141 @@ function useSpeechInput({
|
|
|
2396
2780
|
);
|
|
2397
2781
|
return { supported, listening, interim, error, start, stop, toggle };
|
|
2398
2782
|
}
|
|
2783
|
+
var SILENT_FAILURE_MS = 1500;
|
|
2784
|
+
function speechPlatform() {
|
|
2785
|
+
if (typeof window === "undefined") return null;
|
|
2786
|
+
const scope = window;
|
|
2787
|
+
const synth = scope.speechSynthesis;
|
|
2788
|
+
const Utterance = scope.SpeechSynthesisUtterance;
|
|
2789
|
+
if (!synth || typeof synth.speak !== "function") return null;
|
|
2790
|
+
if (typeof Utterance !== "function") return null;
|
|
2791
|
+
return { synth, Utterance };
|
|
2792
|
+
}
|
|
2793
|
+
function normaliseTag(tag) {
|
|
2794
|
+
return tag.trim().toLowerCase().replace(/_/g, "-");
|
|
2795
|
+
}
|
|
2796
|
+
function primaryOf(tag) {
|
|
2797
|
+
return normaliseTag(tag).split("-")[0] ?? "";
|
|
2798
|
+
}
|
|
2799
|
+
function pickVoice(voices, lang) {
|
|
2800
|
+
const wanted = normaliseTag(lang);
|
|
2801
|
+
if (wanted === "") return null;
|
|
2802
|
+
const primary = primaryOf(wanted);
|
|
2803
|
+
const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);
|
|
2804
|
+
const related = voices.filter((voice) => primaryOf(voice.lang) === primary);
|
|
2805
|
+
return exact.find((voice) => voice.default) ?? exact[0] ?? related.find((voice) => voice.default) ?? related[0] ?? null;
|
|
2806
|
+
}
|
|
2807
|
+
var activeOwner = null;
|
|
2808
|
+
var activeStop = null;
|
|
2809
|
+
function stopSpeaking() {
|
|
2810
|
+
activeStop?.();
|
|
2811
|
+
}
|
|
2812
|
+
function useSpeechOutput({
|
|
2813
|
+
lang
|
|
2814
|
+
} = {}) {
|
|
2815
|
+
const [available, setAvailable] = React.useState(() => speechPlatform() !== null);
|
|
2816
|
+
const [speaking, setSpeaking] = React.useState(false);
|
|
2817
|
+
const utteranceRef = React.useRef(null);
|
|
2818
|
+
const watchdogRef = React.useRef(null);
|
|
2819
|
+
const ownerRef = React.useRef({});
|
|
2820
|
+
const stopRef = React.useRef(() => void 0);
|
|
2821
|
+
const langRef = React.useRef(lang);
|
|
2822
|
+
langRef.current = lang;
|
|
2823
|
+
const clearWatchdog = React.useCallback(() => {
|
|
2824
|
+
if (watchdogRef.current === null) return;
|
|
2825
|
+
clearTimeout(watchdogRef.current);
|
|
2826
|
+
watchdogRef.current = null;
|
|
2827
|
+
}, []);
|
|
2828
|
+
const settle = React.useCallback(() => {
|
|
2829
|
+
clearWatchdog();
|
|
2830
|
+
utteranceRef.current = null;
|
|
2831
|
+
if (activeOwner === ownerRef.current) {
|
|
2832
|
+
activeOwner = null;
|
|
2833
|
+
activeStop = null;
|
|
2834
|
+
}
|
|
2835
|
+
setSpeaking(false);
|
|
2836
|
+
}, [clearWatchdog]);
|
|
2837
|
+
const stop = React.useCallback(() => {
|
|
2838
|
+
const owned = utteranceRef.current !== null;
|
|
2839
|
+
settle();
|
|
2840
|
+
if (owned) speechPlatform()?.synth.cancel();
|
|
2841
|
+
}, [settle]);
|
|
2842
|
+
stopRef.current = stop;
|
|
2843
|
+
const speak = React.useCallback(
|
|
2844
|
+
(text) => {
|
|
2845
|
+
const platform = speechPlatform();
|
|
2846
|
+
if (!platform) {
|
|
2847
|
+
setAvailable(false);
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
const spoken = text.trim();
|
|
2851
|
+
if (spoken === "") return;
|
|
2852
|
+
if (activeStop && activeOwner !== ownerRef.current) activeStop();
|
|
2853
|
+
platform.synth.cancel();
|
|
2854
|
+
const utterance = new platform.Utterance(spoken);
|
|
2855
|
+
const wanted = langRef.current;
|
|
2856
|
+
if (wanted !== void 0 && wanted !== "") {
|
|
2857
|
+
const voice = pickVoice(platform.synth.getVoices(), wanted);
|
|
2858
|
+
utterance.lang = voice?.lang ?? wanted;
|
|
2859
|
+
if (voice) utterance.voice = voice;
|
|
2860
|
+
}
|
|
2861
|
+
const mine = () => utteranceRef.current === utterance;
|
|
2862
|
+
utterance.onstart = () => {
|
|
2863
|
+
if (!mine()) return;
|
|
2864
|
+
clearWatchdog();
|
|
2865
|
+
setSpeaking(true);
|
|
2866
|
+
};
|
|
2867
|
+
utterance.onend = () => {
|
|
2868
|
+
if (!mine()) return;
|
|
2869
|
+
settle();
|
|
2870
|
+
};
|
|
2871
|
+
utterance.onerror = (event) => {
|
|
2872
|
+
if (!mine()) return;
|
|
2873
|
+
const code = typeof event?.error === "string" ? event.error : "";
|
|
2874
|
+
settle();
|
|
2875
|
+
if (code !== "interrupted" && code !== "canceled") setAvailable(false);
|
|
2876
|
+
};
|
|
2877
|
+
utteranceRef.current = utterance;
|
|
2878
|
+
activeOwner = ownerRef.current;
|
|
2879
|
+
activeStop = () => {
|
|
2880
|
+
stopRef.current();
|
|
2881
|
+
};
|
|
2882
|
+
setSpeaking(true);
|
|
2883
|
+
platform.synth.speak(utterance);
|
|
2884
|
+
clearWatchdog();
|
|
2885
|
+
watchdogRef.current = setTimeout(() => {
|
|
2886
|
+
watchdogRef.current = null;
|
|
2887
|
+
if (!mine()) return;
|
|
2888
|
+
if (platform.synth.speaking) return;
|
|
2889
|
+
settle();
|
|
2890
|
+
setAvailable(false);
|
|
2891
|
+
}, SILENT_FAILURE_MS);
|
|
2892
|
+
},
|
|
2893
|
+
[clearWatchdog, settle]
|
|
2894
|
+
);
|
|
2895
|
+
const toggle = React.useCallback(
|
|
2896
|
+
(text) => {
|
|
2897
|
+
if (speaking) stop();
|
|
2898
|
+
else speak(text);
|
|
2899
|
+
},
|
|
2900
|
+
[speak, speaking, stop]
|
|
2901
|
+
);
|
|
2902
|
+
React.useEffect(() => {
|
|
2903
|
+
if (!speaking || typeof window === "undefined") return;
|
|
2904
|
+
const onHide = () => {
|
|
2905
|
+
stopRef.current();
|
|
2906
|
+
};
|
|
2907
|
+
window.addEventListener("pagehide", onHide);
|
|
2908
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
2909
|
+
}, [speaking]);
|
|
2910
|
+
React.useEffect(
|
|
2911
|
+
() => () => {
|
|
2912
|
+
stopRef.current();
|
|
2913
|
+
},
|
|
2914
|
+
[]
|
|
2915
|
+
);
|
|
2916
|
+
return { supported: available, speaking, speak, stop, toggle };
|
|
2917
|
+
}
|
|
2399
2918
|
var SWIPE_THRESHOLD = 40;
|
|
2400
2919
|
function useSwipe({
|
|
2401
2920
|
onSwipeLeft,
|
|
@@ -2791,6 +3310,115 @@ var EXPLORE_CSS = `
|
|
|
2791
3310
|
height: auto;
|
|
2792
3311
|
}
|
|
2793
3312
|
|
|
3313
|
+
/* ---- Comparison and pricing tables ----------------------------------------------------
|
|
3314
|
+
The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
|
|
3315
|
+
the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
|
|
3316
|
+
the page must not scroll sideways because of it, and it has to be readable on a 320px
|
|
3317
|
+
phone, which a four-column grid of 1px borders is not.
|
|
3318
|
+
|
|
3319
|
+
Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
|
|
3320
|
+
\`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
|
|
3321
|
+
cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
|
|
3322
|
+
of table from chaining to the page \u2014 the same rule the transcript needed. */
|
|
3323
|
+
.boff-explore-body .boff-explore-table {
|
|
3324
|
+
max-width: 100%;
|
|
3325
|
+
overflow-x: auto;
|
|
3326
|
+
overscroll-behavior-x: contain;
|
|
3327
|
+
margin: 0.75rem 0;
|
|
3328
|
+
border: 1px solid hsl(var(--border));
|
|
3329
|
+
border-radius: 0.75rem;
|
|
3330
|
+
background-color: hsl(var(--card));
|
|
3331
|
+
}
|
|
3332
|
+
.boff-explore-body .boff-explore-table:focus-visible {
|
|
3333
|
+
outline: 2px solid hsl(var(--ring));
|
|
3334
|
+
outline-offset: 2px;
|
|
3335
|
+
}
|
|
3336
|
+
.boff-explore-body .boff-explore-table table {
|
|
3337
|
+
/* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
|
|
3338
|
+
display: table;
|
|
3339
|
+
width: 100%;
|
|
3340
|
+
max-width: none;
|
|
3341
|
+
margin: 0;
|
|
3342
|
+
border-collapse: collapse;
|
|
3343
|
+
font-size: 0.8125rem;
|
|
3344
|
+
}
|
|
3345
|
+
.boff-explore-body .boff-explore-table th,
|
|
3346
|
+
.boff-explore-body .boff-explore-table td {
|
|
3347
|
+
border: 0;
|
|
3348
|
+
border-top: 1px solid hsl(var(--border));
|
|
3349
|
+
padding: 0.5rem 0.75rem;
|
|
3350
|
+
/* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
|
|
3351
|
+
table there reads against the grain of every other line on the page. */
|
|
3352
|
+
text-align: start;
|
|
3353
|
+
vertical-align: top;
|
|
3354
|
+
overflow-wrap: anywhere;
|
|
3355
|
+
/* Prices in a column line up under each other instead of drifting by digit width. */
|
|
3356
|
+
font-variant-numeric: tabular-nums;
|
|
3357
|
+
min-width: 6rem;
|
|
3358
|
+
}
|
|
3359
|
+
.boff-explore-body .boff-explore-table thead th {
|
|
3360
|
+
border-top: 0;
|
|
3361
|
+
background-color: hsl(var(--muted));
|
|
3362
|
+
color: hsl(var(--foreground));
|
|
3363
|
+
font-weight: 600;
|
|
3364
|
+
white-space: nowrap;
|
|
3365
|
+
}
|
|
3366
|
+
/* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
|
|
3367
|
+
the row's heading is what makes a price list scannable rather than a grid of strings. */
|
|
3368
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
3369
|
+
font-weight: 600;
|
|
3370
|
+
color: hsl(var(--card-foreground));
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
/* Narrow: one card per row. Each cell prints its own column heading, so a value is never
|
|
3374
|
+
stranded without the question it answers, and the subject cell becomes the card's title.
|
|
3375
|
+
A media query rather than a measured width \u2014 these pages are serialised post-hydration,
|
|
3376
|
+
so anything measured here would ship to every phone as static HTML. */
|
|
3377
|
+
@media (max-width: 30rem) {
|
|
3378
|
+
.boff-explore-body .boff-explore-table {
|
|
3379
|
+
overflow-x: visible;
|
|
3380
|
+
}
|
|
3381
|
+
.boff-explore-body .boff-explore-table table,
|
|
3382
|
+
.boff-explore-body .boff-explore-table tbody,
|
|
3383
|
+
.boff-explore-body .boff-explore-table tr,
|
|
3384
|
+
.boff-explore-body .boff-explore-table td {
|
|
3385
|
+
display: block;
|
|
3386
|
+
width: 100%;
|
|
3387
|
+
min-width: 0;
|
|
3388
|
+
}
|
|
3389
|
+
.boff-explore-body .boff-explore-table thead {
|
|
3390
|
+
display: none;
|
|
3391
|
+
}
|
|
3392
|
+
.boff-explore-body .boff-explore-table tr {
|
|
3393
|
+
border-top: 1px solid hsl(var(--border));
|
|
3394
|
+
padding: 0.5rem 0;
|
|
3395
|
+
}
|
|
3396
|
+
.boff-explore-body .boff-explore-table tr:first-child {
|
|
3397
|
+
border-top: 0;
|
|
3398
|
+
}
|
|
3399
|
+
.boff-explore-body .boff-explore-table td {
|
|
3400
|
+
border: 0;
|
|
3401
|
+
padding: 0.125rem 0.75rem;
|
|
3402
|
+
}
|
|
3403
|
+
.boff-explore-body .boff-explore-table td[data-label]::before {
|
|
3404
|
+
content: attr(data-label);
|
|
3405
|
+
display: block;
|
|
3406
|
+
font-size: 0.6875rem;
|
|
3407
|
+
font-weight: 600;
|
|
3408
|
+
text-transform: uppercase;
|
|
3409
|
+
letter-spacing: 0.04em;
|
|
3410
|
+
color: hsl(var(--muted-foreground));
|
|
3411
|
+
}
|
|
3412
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
3413
|
+
font-size: 0.9375rem;
|
|
3414
|
+
padding-bottom: 0.25rem;
|
|
3415
|
+
}
|
|
3416
|
+
.boff-explore-body .boff-explore-table tbody td:first-child::before {
|
|
3417
|
+
/* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
|
|
3418
|
+
display: none;
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
|
|
2794
3422
|
/* ---- Ambient backdrop -----------------------------------------------------------------
|
|
2795
3423
|
Two very low-opacity radial washes behind the welcome state, drifting slowly. Painted on
|
|
2796
3424
|
a pseudo-element with pointer-events:none so it can never intercept a click \u2014 a
|
|
@@ -2962,7 +3590,7 @@ function isOwnElement(el, root) {
|
|
|
2962
3590
|
function visualViewportHeight() {
|
|
2963
3591
|
return window.visualViewport?.height ?? window.innerHeight;
|
|
2964
3592
|
}
|
|
2965
|
-
function
|
|
3593
|
+
function clamp2(value, min, max) {
|
|
2966
3594
|
return Math.min(Math.max(value, min), max);
|
|
2967
3595
|
}
|
|
2968
3596
|
function isRestoredNavigation() {
|
|
@@ -3027,14 +3655,196 @@ function isInlineImageAllowed(src, origins) {
|
|
|
3027
3655
|
if (url.protocol !== "https:" || url.username || url.password) return false;
|
|
3028
3656
|
return origins.has(url.origin);
|
|
3029
3657
|
}
|
|
3030
|
-
var
|
|
3658
|
+
var REFERENCE_KIND_ORDER = [
|
|
3659
|
+
"CONCEPT",
|
|
3660
|
+
"DOCS",
|
|
3661
|
+
"PRICING",
|
|
3662
|
+
"USE_CASE",
|
|
3663
|
+
"PAGE"
|
|
3664
|
+
];
|
|
3665
|
+
var PRODUCT_SUBDOMAINS = [
|
|
3666
|
+
"alphadocs.",
|
|
3667
|
+
"alphaapp.",
|
|
3668
|
+
"docs.",
|
|
3669
|
+
"alpha.",
|
|
3670
|
+
"app.",
|
|
3671
|
+
"www."
|
|
3672
|
+
];
|
|
3673
|
+
function readCatalogUrl(raw) {
|
|
3674
|
+
let parsed;
|
|
3675
|
+
try {
|
|
3676
|
+
parsed = new URL(raw);
|
|
3677
|
+
} catch {
|
|
3678
|
+
return null;
|
|
3679
|
+
}
|
|
3680
|
+
if (parsed.protocol !== "https:") return null;
|
|
3681
|
+
const host = parsed.hostname.toLowerCase();
|
|
3682
|
+
const prefix = PRODUCT_SUBDOMAINS.find(
|
|
3683
|
+
(candidate) => host.startsWith(candidate)
|
|
3684
|
+
);
|
|
3685
|
+
const apex = prefix === void 0 ? host : host.slice(prefix.length);
|
|
3686
|
+
const port = parsed.port ? `:${parsed.port}` : "";
|
|
3687
|
+
return {
|
|
3688
|
+
origin: `https://${apex}${port}`,
|
|
3689
|
+
prefix,
|
|
3690
|
+
pathname: parsed.pathname
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function referenceKindOf(url, origins) {
|
|
3694
|
+
const parsed = readCatalogUrl(url);
|
|
3695
|
+
if (parsed === null || !origins.has(parsed.origin)) return null;
|
|
3696
|
+
if (parsed.prefix === "docs." || parsed.prefix === "alphadocs.")
|
|
3697
|
+
return "DOCS";
|
|
3698
|
+
const segment = parsed.pathname.toLowerCase().split("/").filter(Boolean)[0] ?? "";
|
|
3699
|
+
if (segment === "concept" || segment === "concepts") return "CONCEPT";
|
|
3700
|
+
if (segment === "docs") return "DOCS";
|
|
3701
|
+
if (segment === "pricing" || segment === "plans") return "PRICING";
|
|
3702
|
+
if (segment === "use-case" || segment === "use-cases" || segment === "solutions") {
|
|
3703
|
+
return "USE_CASE";
|
|
3704
|
+
}
|
|
3705
|
+
return "PAGE";
|
|
3706
|
+
}
|
|
3707
|
+
function productNamesByOrigin(products) {
|
|
3708
|
+
const names = /* @__PURE__ */ new Map();
|
|
3709
|
+
for (const product of products) {
|
|
3710
|
+
let url;
|
|
3711
|
+
try {
|
|
3712
|
+
url = new URL(product.website);
|
|
3713
|
+
} catch {
|
|
3714
|
+
continue;
|
|
3715
|
+
}
|
|
3716
|
+
if (url.protocol !== "https:") continue;
|
|
3717
|
+
const apex = url.hostname.replace(/^www\./, "");
|
|
3718
|
+
const port = url.port ? `:${url.port}` : "";
|
|
3719
|
+
const key = `https://${apex}${port}`;
|
|
3720
|
+
if (!names.has(key)) names.set(key, product.name);
|
|
3721
|
+
}
|
|
3722
|
+
return names;
|
|
3723
|
+
}
|
|
3724
|
+
function summariseReferences(references, origins, productNames) {
|
|
3725
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3726
|
+
const products = [];
|
|
3727
|
+
for (const reference of references) {
|
|
3728
|
+
const kind = referenceKindOf(reference.url, origins);
|
|
3729
|
+
if (kind !== null) counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
3730
|
+
const parsed = readCatalogUrl(reference.url);
|
|
3731
|
+
const name = (parsed === null ? void 0 : productNames.get(parsed.origin)) ?? (reference.product || null);
|
|
3732
|
+
if (name != null && !products.includes(name)) products.push(name);
|
|
3733
|
+
}
|
|
3734
|
+
return {
|
|
3735
|
+
total: references.length,
|
|
3736
|
+
kinds: REFERENCE_KIND_ORDER.filter((kind) => counts.has(kind)).map(
|
|
3737
|
+
(kind) => ({ kind, count: counts.get(kind) ?? 0 })
|
|
3738
|
+
),
|
|
3739
|
+
products
|
|
3740
|
+
};
|
|
3741
|
+
}
|
|
3742
|
+
var CODE_FENCE = /^\s*(?:```|~~~)/;
|
|
3743
|
+
var TABLE_ROW_LINE = /^\s{0,3}\|/;
|
|
3744
|
+
var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
|
|
3745
|
+
function readableLine(raw, mode) {
|
|
3746
|
+
let line = raw;
|
|
3747
|
+
line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
|
|
3748
|
+
line = mode === "speech" ? (
|
|
3749
|
+
// A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
|
|
3750
|
+
// prompt asks for two to five links in every answer. The label IS the words.
|
|
3751
|
+
line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
|
|
3752
|
+
) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
|
|
3753
|
+
line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
|
|
3754
|
+
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
3755
|
+
line = line.replace(/^\s{0,3}>\s?/, "");
|
|
3756
|
+
if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
|
|
3757
|
+
line = line.replace(/`([^`]+)`/g, "$1");
|
|
3758
|
+
line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
3759
|
+
line = line.replace(/__([^_]+)__/g, "$1");
|
|
3760
|
+
line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
|
|
3761
|
+
line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
|
|
3762
|
+
return line.replace(/[ \t]+$/, "");
|
|
3763
|
+
}
|
|
3764
|
+
function tableCells(row) {
|
|
3765
|
+
const trimmed = row.trim();
|
|
3766
|
+
const cells = [];
|
|
3767
|
+
let cell = "";
|
|
3768
|
+
for (let at = 0; at < trimmed.length; at += 1) {
|
|
3769
|
+
const char = trimmed[at] ?? "";
|
|
3770
|
+
if (char === "\\") {
|
|
3771
|
+
cell += char + (trimmed[at + 1] ?? "");
|
|
3772
|
+
at += 1;
|
|
3773
|
+
continue;
|
|
3774
|
+
}
|
|
3775
|
+
if (char === "|") {
|
|
3776
|
+
cells.push(cell.trim());
|
|
3777
|
+
cell = "";
|
|
3778
|
+
continue;
|
|
3779
|
+
}
|
|
3780
|
+
cell += char;
|
|
3781
|
+
}
|
|
3782
|
+
cells.push(cell.trim());
|
|
3783
|
+
if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
|
|
3784
|
+
if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
|
|
3785
|
+
return cells;
|
|
3786
|
+
}
|
|
3787
|
+
function asSentence(text) {
|
|
3788
|
+
return /[.!?]$/.test(text) ? text : `${text}.`;
|
|
3789
|
+
}
|
|
3790
|
+
function spokenTable(rows) {
|
|
3791
|
+
if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
|
|
3792
|
+
const labels = tableCells(rows[0] ?? "");
|
|
3793
|
+
const spoken = [];
|
|
3794
|
+
for (const row of rows.slice(2)) {
|
|
3795
|
+
const parts = [];
|
|
3796
|
+
tableCells(row).forEach((value, column) => {
|
|
3797
|
+
if (value === "") return;
|
|
3798
|
+
const label = column === 0 ? "" : labels[column] ?? "";
|
|
3799
|
+
parts.push(label === "" ? value : `${label}: ${value}`);
|
|
3800
|
+
});
|
|
3801
|
+
if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
|
|
3802
|
+
}
|
|
3803
|
+
return spoken;
|
|
3804
|
+
}
|
|
3805
|
+
function readableText(markdown, mode) {
|
|
3806
|
+
const lines = [];
|
|
3807
|
+
let inFence = false;
|
|
3808
|
+
let table = [];
|
|
3809
|
+
const flushTable = () => {
|
|
3810
|
+
if (table.length === 0) return;
|
|
3811
|
+
for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
|
|
3812
|
+
table = [];
|
|
3813
|
+
};
|
|
3814
|
+
for (const raw of markdown.split("\n")) {
|
|
3815
|
+
if (CODE_FENCE.test(raw)) {
|
|
3816
|
+
flushTable();
|
|
3817
|
+
inFence = !inFence;
|
|
3818
|
+
continue;
|
|
3819
|
+
}
|
|
3820
|
+
if (inFence) {
|
|
3821
|
+
lines.push(raw);
|
|
3822
|
+
continue;
|
|
3823
|
+
}
|
|
3824
|
+
if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
|
|
3825
|
+
table.push(raw);
|
|
3826
|
+
continue;
|
|
3827
|
+
}
|
|
3828
|
+
flushTable();
|
|
3829
|
+
lines.push(readableLine(raw, mode));
|
|
3830
|
+
}
|
|
3831
|
+
flushTable();
|
|
3832
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
3833
|
+
}
|
|
3834
|
+
function answerPlainText(markdown) {
|
|
3835
|
+
return readableText(markdown, "clipboard");
|
|
3836
|
+
}
|
|
3837
|
+
function answerSpeechText(markdown) {
|
|
3838
|
+
return readableText(markdown, "speech");
|
|
3839
|
+
}
|
|
3840
|
+
var AnswerScopeContext = React.createContext(null);
|
|
3031
3841
|
var InsideLinkContext = React.createContext(false);
|
|
3032
3842
|
function AnswerLink(props) {
|
|
3033
3843
|
return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
|
|
3034
3844
|
}
|
|
3035
3845
|
var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
|
|
3036
3846
|
function InlineConceptImage({ src, alt }) {
|
|
3037
|
-
const scope = React.useContext(
|
|
3847
|
+
const scope = React.useContext(AnswerScopeContext);
|
|
3038
3848
|
const insideLink = React.useContext(InsideLinkContext);
|
|
3039
3849
|
if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
|
|
3040
3850
|
return null;
|
|
@@ -3082,9 +3892,70 @@ function InlineConceptImage({ src, alt }) {
|
|
|
3082
3892
|
}
|
|
3083
3893
|
) });
|
|
3084
3894
|
}
|
|
3895
|
+
function markdownText(node) {
|
|
3896
|
+
if (!node) return "";
|
|
3897
|
+
if (typeof node.value === "string") return node.value;
|
|
3898
|
+
return (node.children ?? []).map(markdownText).join("");
|
|
3899
|
+
}
|
|
3900
|
+
function hasHeaderCells(node) {
|
|
3901
|
+
return (node?.children ?? []).some((cell) => cell.tagName === "th");
|
|
3902
|
+
}
|
|
3903
|
+
function findHeaderRow(node) {
|
|
3904
|
+
for (const child of node?.children ?? []) {
|
|
3905
|
+
if (child.tagName === "tr" && hasHeaderCells(child)) return child;
|
|
3906
|
+
const nested = findHeaderRow(child);
|
|
3907
|
+
if (nested !== null) return nested;
|
|
3908
|
+
}
|
|
3909
|
+
return null;
|
|
3910
|
+
}
|
|
3911
|
+
function tableColumnLabels(node) {
|
|
3912
|
+
const row = findHeaderRow(node);
|
|
3913
|
+
if (row === null) return [];
|
|
3914
|
+
return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
|
|
3915
|
+
}
|
|
3916
|
+
var TableColumnsContext = React.createContext([]);
|
|
3917
|
+
function AnswerTable({ node, children }) {
|
|
3918
|
+
const scope = React.useContext(AnswerScopeContext);
|
|
3919
|
+
const table = /* @__PURE__ */ jsxRuntime.jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsxRuntime.jsx("table", { children }) });
|
|
3920
|
+
if (!scope) return table;
|
|
3921
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3922
|
+
"div",
|
|
3923
|
+
{
|
|
3924
|
+
"data-boff-explore": "table",
|
|
3925
|
+
role: "region",
|
|
3926
|
+
"aria-label": scope.strings.answerTable,
|
|
3927
|
+
tabIndex: 0,
|
|
3928
|
+
className: "boff-explore-table",
|
|
3929
|
+
children: table
|
|
3930
|
+
}
|
|
3931
|
+
);
|
|
3932
|
+
}
|
|
3933
|
+
function AnswerTableRow({
|
|
3934
|
+
node,
|
|
3935
|
+
children,
|
|
3936
|
+
...props
|
|
3937
|
+
}) {
|
|
3938
|
+
const labels = React.useContext(TableColumnsContext);
|
|
3939
|
+
if (labels.length === 0 || hasHeaderCells(node)) {
|
|
3940
|
+
return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children });
|
|
3941
|
+
}
|
|
3942
|
+
let column = 0;
|
|
3943
|
+
const cells = React.Children.map(children, (cell) => {
|
|
3944
|
+
if (!React.isValidElement(cell)) return cell;
|
|
3945
|
+
const label = labels[column] ?? "";
|
|
3946
|
+
column += 1;
|
|
3947
|
+
if (label === "") return cell;
|
|
3948
|
+
return React.cloneElement(cell, {
|
|
3949
|
+
"data-label": label
|
|
3950
|
+
});
|
|
3951
|
+
});
|
|
3952
|
+
return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
|
|
3953
|
+
}
|
|
3085
3954
|
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
3086
3955
|
a: AnswerLink,
|
|
3087
|
-
img: InlineConceptImage
|
|
3956
|
+
img: InlineConceptImage,
|
|
3957
|
+
table: AnswerTable,
|
|
3958
|
+
tr: AnswerTableRow
|
|
3088
3959
|
};
|
|
3089
3960
|
function ReferenceCard({
|
|
3090
3961
|
reference,
|
|
@@ -3424,6 +4295,148 @@ function AnswerFeedback({
|
|
|
3424
4295
|
}
|
|
3425
4296
|
);
|
|
3426
4297
|
}
|
|
4298
|
+
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";
|
|
4299
|
+
function ProvenanceChips({
|
|
4300
|
+
strings: T,
|
|
4301
|
+
provenance
|
|
4302
|
+
}) {
|
|
4303
|
+
if (provenance.total === 0) return null;
|
|
4304
|
+
const kindLabels = {
|
|
4305
|
+
CONCEPT: T.sourceKindConcept,
|
|
4306
|
+
DOCS: T.sourceKindDocs,
|
|
4307
|
+
PRICING: T.sourceKindPricing,
|
|
4308
|
+
USE_CASE: T.sourceKindUseCase,
|
|
4309
|
+
PAGE: T.sourceKindPage
|
|
4310
|
+
};
|
|
4311
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4312
|
+
"ul",
|
|
4313
|
+
{
|
|
4314
|
+
"data-boff-explore": "provenance",
|
|
4315
|
+
"aria-label": T.groundedIn,
|
|
4316
|
+
className: "flex flex-wrap items-center gap-1.5",
|
|
4317
|
+
children: [
|
|
4318
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { "data-boff-explore": "provenance-count", className: PROVENANCE_CHIP, children: [
|
|
4319
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpenText, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
4320
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: provenance.total === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(provenance.total) }) })
|
|
4321
|
+
] }),
|
|
4322
|
+
provenance.kinds.map(({ kind, count }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4323
|
+
"li",
|
|
4324
|
+
{
|
|
4325
|
+
"data-boff-explore": "provenance-kind",
|
|
4326
|
+
"data-kind": kind,
|
|
4327
|
+
"data-count": count,
|
|
4328
|
+
className: PROVENANCE_CHIP,
|
|
4329
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: count > 1 ? `${kindLabels[kind]} \xB7 ${count}` : kindLabels[kind] })
|
|
4330
|
+
},
|
|
4331
|
+
kind
|
|
4332
|
+
)),
|
|
4333
|
+
provenance.products.map((name) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4334
|
+
"li",
|
|
4335
|
+
{
|
|
4336
|
+
"data-boff-explore": "provenance-product",
|
|
4337
|
+
className: PROVENANCE_CHIP,
|
|
4338
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: name })
|
|
4339
|
+
},
|
|
4340
|
+
name
|
|
4341
|
+
))
|
|
4342
|
+
]
|
|
4343
|
+
}
|
|
4344
|
+
);
|
|
4345
|
+
}
|
|
4346
|
+
var COPY_FEEDBACK_MS = 2500;
|
|
4347
|
+
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";
|
|
4348
|
+
var ANSWER_ACTION_IDLE = "border-border bg-card text-muted-foreground hover:border-primary/40 hover:bg-accent hover:text-foreground";
|
|
4349
|
+
var ANSWER_ACTION_ACTIVE = "border-primary/40 bg-accent text-primary";
|
|
4350
|
+
function ReadAloudButton({
|
|
4351
|
+
strings: T,
|
|
4352
|
+
text,
|
|
4353
|
+
lang
|
|
4354
|
+
}) {
|
|
4355
|
+
const speech = useSpeechOutput({ lang });
|
|
4356
|
+
if (!speech.supported) return null;
|
|
4357
|
+
const label = speech.speaking ? T.stopReading : T.readAloud;
|
|
4358
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4359
|
+
"button",
|
|
4360
|
+
{
|
|
4361
|
+
"data-boff-explore": "read-aloud",
|
|
4362
|
+
"data-speaking": speech.speaking ? "true" : "false",
|
|
4363
|
+
type: "button",
|
|
4364
|
+
title: label,
|
|
4365
|
+
"aria-pressed": speech.speaking,
|
|
4366
|
+
onClick: () => speech.toggle(text),
|
|
4367
|
+
className: cn(
|
|
4368
|
+
ANSWER_ACTION_PILL,
|
|
4369
|
+
speech.speaking ? ANSWER_ACTION_ACTIVE : ANSWER_ACTION_IDLE
|
|
4370
|
+
),
|
|
4371
|
+
children: [
|
|
4372
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Volume2, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
4373
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label })
|
|
4374
|
+
]
|
|
4375
|
+
}
|
|
4376
|
+
);
|
|
4377
|
+
}
|
|
4378
|
+
function AnswerActions({
|
|
4379
|
+
strings: T,
|
|
4380
|
+
text,
|
|
4381
|
+
speechText,
|
|
4382
|
+
speechLang
|
|
4383
|
+
}) {
|
|
4384
|
+
const [outcome, setOutcome] = React.useState("idle");
|
|
4385
|
+
const resetRef = React.useRef(null);
|
|
4386
|
+
React.useEffect(
|
|
4387
|
+
() => () => {
|
|
4388
|
+
if (resetRef.current !== null) clearTimeout(resetRef.current);
|
|
4389
|
+
},
|
|
4390
|
+
[]
|
|
4391
|
+
);
|
|
4392
|
+
const copy = React.useCallback(async () => {
|
|
4393
|
+
let copied = false;
|
|
4394
|
+
try {
|
|
4395
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
4396
|
+
if (typeof clipboard?.writeText === "function") {
|
|
4397
|
+
await clipboard.writeText(text);
|
|
4398
|
+
copied = true;
|
|
4399
|
+
}
|
|
4400
|
+
} catch {
|
|
4401
|
+
copied = false;
|
|
4402
|
+
}
|
|
4403
|
+
setOutcome(copied ? "copied" : "failed");
|
|
4404
|
+
if (resetRef.current !== null) clearTimeout(resetRef.current);
|
|
4405
|
+
resetRef.current = setTimeout(() => setOutcome("idle"), COPY_FEEDBACK_MS);
|
|
4406
|
+
}, [text]);
|
|
4407
|
+
const label = outcome === "copied" ? T.copied : outcome === "failed" ? T.copyFailed : T.copyAnswer;
|
|
4408
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4409
|
+
"div",
|
|
4410
|
+
{
|
|
4411
|
+
"data-boff-explore": "answer-actions",
|
|
4412
|
+
className: "flex flex-wrap items-center gap-2 pt-0.5",
|
|
4413
|
+
children: [
|
|
4414
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4415
|
+
"button",
|
|
4416
|
+
{
|
|
4417
|
+
"data-boff-explore": "copy-answer",
|
|
4418
|
+
"data-outcome": outcome,
|
|
4419
|
+
type: "button",
|
|
4420
|
+
title: label,
|
|
4421
|
+
onClick: () => {
|
|
4422
|
+
void copy();
|
|
4423
|
+
},
|
|
4424
|
+
className: cn(
|
|
4425
|
+
ANSWER_ACTION_PILL,
|
|
4426
|
+
outcome === "failed" ? "border-destructive/40 bg-card text-destructive" : ANSWER_ACTION_IDLE
|
|
4427
|
+
),
|
|
4428
|
+
children: [
|
|
4429
|
+
outcome === "copied" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
4430
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label })
|
|
4431
|
+
]
|
|
4432
|
+
}
|
|
4433
|
+
),
|
|
4434
|
+
/* @__PURE__ */ jsxRuntime.jsx(ReadAloudButton, { strings: T, text: speechText, lang: speechLang }),
|
|
4435
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-boff-explore": "copy-status", role: "status", className: "sr-only", children: outcome === "idle" ? "" : label })
|
|
4436
|
+
]
|
|
4437
|
+
}
|
|
4438
|
+
);
|
|
4439
|
+
}
|
|
3427
4440
|
function UserBubble({
|
|
3428
4441
|
strings: T,
|
|
3429
4442
|
message
|
|
@@ -3469,6 +4482,8 @@ function AssistantBubble({
|
|
|
3469
4482
|
message,
|
|
3470
4483
|
activity,
|
|
3471
4484
|
imageOrigins,
|
|
4485
|
+
productNames,
|
|
4486
|
+
speechLang,
|
|
3472
4487
|
onNavigate,
|
|
3473
4488
|
anchorRef,
|
|
3474
4489
|
onFollow,
|
|
@@ -3479,6 +4494,7 @@ function AssistantBubble({
|
|
|
3479
4494
|
const body = message.content || message.partialContent || "";
|
|
3480
4495
|
const failed = message.status === "FAILED";
|
|
3481
4496
|
const completed = message.status === "COMPLETED";
|
|
4497
|
+
const visible = useProgressiveReveal(body, !streaming);
|
|
3482
4498
|
const [failedImages, setFailedImages] = React.useState(
|
|
3483
4499
|
() => /* @__PURE__ */ new Set()
|
|
3484
4500
|
);
|
|
@@ -3487,7 +4503,7 @@ function AssistantBubble({
|
|
|
3487
4503
|
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
3488
4504
|
);
|
|
3489
4505
|
}, []);
|
|
3490
|
-
const
|
|
4506
|
+
const answerScope = React.useMemo(
|
|
3491
4507
|
() => ({
|
|
3492
4508
|
strings: T,
|
|
3493
4509
|
enabled: completed,
|
|
@@ -3497,6 +4513,19 @@ function AssistantBubble({
|
|
|
3497
4513
|
}),
|
|
3498
4514
|
[T, completed, imageOrigins, failedImages, markImageFailed]
|
|
3499
4515
|
);
|
|
4516
|
+
const provenance = React.useMemo(
|
|
4517
|
+
() => summariseReferences(message.references, imageOrigins, productNames),
|
|
4518
|
+
[message.references, imageOrigins, productNames]
|
|
4519
|
+
);
|
|
4520
|
+
const copyText = React.useMemo(
|
|
4521
|
+
() => answerPlainText(message.content),
|
|
4522
|
+
[message.content]
|
|
4523
|
+
);
|
|
4524
|
+
const speechText = React.useMemo(
|
|
4525
|
+
() => answerSpeechText(message.content),
|
|
4526
|
+
[message.content]
|
|
4527
|
+
);
|
|
4528
|
+
const grounded = completed && message.answered !== false;
|
|
3500
4529
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3501
4530
|
"div",
|
|
3502
4531
|
{
|
|
@@ -3542,10 +4571,11 @@ function AssistantBubble({
|
|
|
3542
4571
|
]
|
|
3543
4572
|
}
|
|
3544
4573
|
) : null,
|
|
3545
|
-
|
|
3546
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4574
|
+
visible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
|
|
4575
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(AnswerScopeContext.Provider, { value: answerScope, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { components: ANSWER_MARKDOWN_COMPONENTS, children: visible }) }) }),
|
|
3547
4576
|
streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
|
|
3548
4577
|
] }) : null,
|
|
4578
|
+
grounded && provenance.total > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ProvenanceChips, { strings: T, provenance }) : null,
|
|
3549
4579
|
streaming ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingIndicator, { activity }) : null,
|
|
3550
4580
|
failed && !body ? /* @__PURE__ */ jsxRuntime.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,
|
|
3551
4581
|
message.references.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
@@ -3585,6 +4615,15 @@ function AssistantBubble({
|
|
|
3585
4615
|
question
|
|
3586
4616
|
)) })
|
|
3587
4617
|
] }) : null,
|
|
4618
|
+
completed && copyText !== "" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4619
|
+
AnswerActions,
|
|
4620
|
+
{
|
|
4621
|
+
strings: T,
|
|
4622
|
+
text: copyText,
|
|
4623
|
+
speechText,
|
|
4624
|
+
speechLang
|
|
4625
|
+
}
|
|
4626
|
+
) : null,
|
|
3588
4627
|
completed && onFeedback ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3589
4628
|
AnswerFeedback,
|
|
3590
4629
|
{
|
|
@@ -3797,6 +4836,96 @@ function ProductStrip({
|
|
|
3797
4836
|
}
|
|
3798
4837
|
);
|
|
3799
4838
|
}
|
|
4839
|
+
var MAX_CONCEPT_CARDS = 6;
|
|
4840
|
+
function ConceptStrip({
|
|
4841
|
+
strings: T,
|
|
4842
|
+
highlights,
|
|
4843
|
+
onAsk,
|
|
4844
|
+
disabled
|
|
4845
|
+
}) {
|
|
4846
|
+
const [failed, setFailed] = React.useState(() => /* @__PURE__ */ new Set());
|
|
4847
|
+
const markFailed = React.useCallback((src) => {
|
|
4848
|
+
setFailed(
|
|
4849
|
+
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
4850
|
+
);
|
|
4851
|
+
}, []);
|
|
4852
|
+
const visible = highlights.filter(
|
|
4853
|
+
(highlight) => highlight.imageUrl !== "" && !failed.has(highlight.imageUrl)
|
|
4854
|
+
).slice(0, MAX_CONCEPT_CARDS);
|
|
4855
|
+
if (visible.length === 0) return null;
|
|
4856
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4857
|
+
"div",
|
|
4858
|
+
{
|
|
4859
|
+
"data-boff-explore": "concept-strip-block",
|
|
4860
|
+
className: "boff-explore-rise boff-explore-rise-1 mt-6",
|
|
4861
|
+
children: [
|
|
4862
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: T.conceptStripTitle }),
|
|
4863
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4864
|
+
"ul",
|
|
4865
|
+
{
|
|
4866
|
+
"data-boff-explore": "concept-strip",
|
|
4867
|
+
"aria-label": T.conceptStripLabel,
|
|
4868
|
+
className: "boff-explore-strip flex min-w-0 snap-x gap-3 overflow-x-auto pb-1",
|
|
4869
|
+
children: visible.map((highlight) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4870
|
+
"li",
|
|
4871
|
+
{
|
|
4872
|
+
className: "shrink-0 snap-start",
|
|
4873
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4874
|
+
"button",
|
|
4875
|
+
{
|
|
4876
|
+
"data-boff-explore": "concept-card",
|
|
4877
|
+
"data-product": highlight.product,
|
|
4878
|
+
type: "button",
|
|
4879
|
+
disabled,
|
|
4880
|
+
"aria-label": fill(T.conceptAsk, {
|
|
4881
|
+
title: highlight.title,
|
|
4882
|
+
product: highlight.productName
|
|
4883
|
+
}),
|
|
4884
|
+
onClick: () => onAsk(
|
|
4885
|
+
fill(T.conceptAskQuestion, {
|
|
4886
|
+
title: highlight.title,
|
|
4887
|
+
product: highlight.productName
|
|
4888
|
+
})
|
|
4889
|
+
),
|
|
4890
|
+
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",
|
|
4891
|
+
children: [
|
|
4892
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative block aspect-[16/9] w-full overflow-hidden bg-muted", children: [
|
|
4893
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4894
|
+
"img",
|
|
4895
|
+
{
|
|
4896
|
+
src: highlight.imageUrl,
|
|
4897
|
+
alt: highlight.alt,
|
|
4898
|
+
loading: "lazy",
|
|
4899
|
+
decoding: "async",
|
|
4900
|
+
onError: () => markFailed(highlight.imageUrl),
|
|
4901
|
+
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
4902
|
+
}
|
|
4903
|
+
),
|
|
4904
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4905
|
+
"span",
|
|
4906
|
+
{
|
|
4907
|
+
"aria-hidden": "true",
|
|
4908
|
+
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",
|
|
4909
|
+
children: T.conceptIllustration
|
|
4910
|
+
}
|
|
4911
|
+
)
|
|
4912
|
+
] }),
|
|
4913
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col gap-0.5 p-2.5", children: [
|
|
4914
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-xs font-semibold leading-snug text-card-foreground", children: highlight.title }),
|
|
4915
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: highlight.productName })
|
|
4916
|
+
] })
|
|
4917
|
+
]
|
|
4918
|
+
}
|
|
4919
|
+
)
|
|
4920
|
+
},
|
|
4921
|
+
`${highlight.product}:${highlight.url}`
|
|
4922
|
+
))
|
|
4923
|
+
}
|
|
4924
|
+
)
|
|
4925
|
+
]
|
|
4926
|
+
}
|
|
4927
|
+
);
|
|
4928
|
+
}
|
|
3800
4929
|
function DisclaimerDialog({
|
|
3801
4930
|
open,
|
|
3802
4931
|
onClose,
|
|
@@ -3903,6 +5032,7 @@ function WelcomeState({
|
|
|
3903
5032
|
body,
|
|
3904
5033
|
prompts,
|
|
3905
5034
|
products,
|
|
5035
|
+
conceptHighlights,
|
|
3906
5036
|
focusProduct,
|
|
3907
5037
|
onPrompt,
|
|
3908
5038
|
onFocus,
|
|
@@ -3917,6 +5047,15 @@ function WelcomeState({
|
|
|
3917
5047
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-4 text-balance break-words text-2xl font-bold tracking-tight sm:text-4xl", children: title }),
|
|
3918
5048
|
/* @__PURE__ */ jsxRuntime.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 })
|
|
3919
5049
|
] }),
|
|
5050
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5051
|
+
ConceptStrip,
|
|
5052
|
+
{
|
|
5053
|
+
strings: T,
|
|
5054
|
+
highlights: conceptHighlights,
|
|
5055
|
+
onAsk: onPrompt,
|
|
5056
|
+
disabled
|
|
5057
|
+
}
|
|
5058
|
+
),
|
|
3920
5059
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(
|
|
3921
5060
|
"li",
|
|
3922
5061
|
{
|
|
@@ -3978,6 +5117,7 @@ function ExplorePage({
|
|
|
3978
5117
|
);
|
|
3979
5118
|
const T = React.useMemo(() => exploreStrings(activeLocale), [activeLocale]);
|
|
3980
5119
|
const rtl = isRtlLocale(activeLocale);
|
|
5120
|
+
const speechLang = speechLocaleOf(activeLocale);
|
|
3981
5121
|
const {
|
|
3982
5122
|
phase,
|
|
3983
5123
|
catalog,
|
|
@@ -4062,6 +5202,10 @@ function ExplorePage({
|
|
|
4062
5202
|
() => inlineImageOrigins(catalog.products),
|
|
4063
5203
|
[catalog.products]
|
|
4064
5204
|
);
|
|
5205
|
+
const productNames = React.useMemo(
|
|
5206
|
+
() => productNamesByOrigin(catalog.products),
|
|
5207
|
+
[catalog.products]
|
|
5208
|
+
);
|
|
4065
5209
|
React.useEffect(() => {
|
|
4066
5210
|
const el = textareaRef.current;
|
|
4067
5211
|
if (!el) return;
|
|
@@ -4078,11 +5222,11 @@ function ExplorePage({
|
|
|
4078
5222
|
const layoutHeight = window.innerHeight;
|
|
4079
5223
|
if (layoutHeight === 0) return;
|
|
4080
5224
|
const top = Math.round(
|
|
4081
|
-
|
|
5225
|
+
clamp2(el.getBoundingClientRect().top, 0, layoutHeight)
|
|
4082
5226
|
);
|
|
4083
5227
|
const viewport = window.visualViewport;
|
|
4084
5228
|
const inset = viewport ? Math.round(
|
|
4085
|
-
|
|
5229
|
+
clamp2(
|
|
4086
5230
|
layoutHeight - viewport.height - viewport.offsetTop,
|
|
4087
5231
|
0,
|
|
4088
5232
|
layoutHeight
|
|
@@ -4363,6 +5507,7 @@ function ExplorePage({
|
|
|
4363
5507
|
const text = draft.trim();
|
|
4364
5508
|
if (!text || overLimit || busy || !canSend) return;
|
|
4365
5509
|
stopDictation();
|
|
5510
|
+
stopSpeaking();
|
|
4366
5511
|
setDraft("");
|
|
4367
5512
|
stickToBottomRef.current = true;
|
|
4368
5513
|
void send(text, toPayload(stagedRef.current));
|
|
@@ -4373,6 +5518,7 @@ function ExplorePage({
|
|
|
4373
5518
|
const sendPrompt = React.useCallback(
|
|
4374
5519
|
(prompt) => {
|
|
4375
5520
|
if (!canSend || busy) return;
|
|
5521
|
+
stopSpeaking();
|
|
4376
5522
|
setDraft("");
|
|
4377
5523
|
stickToBottomRef.current = true;
|
|
4378
5524
|
void send(prompt, toPayload(stagedRef.current));
|
|
@@ -4593,6 +5739,7 @@ function ExplorePage({
|
|
|
4593
5739
|
body: resolvedBody,
|
|
4594
5740
|
prompts,
|
|
4595
5741
|
products: catalog.products,
|
|
5742
|
+
conceptHighlights: catalog.conceptHighlights,
|
|
4596
5743
|
focusProduct,
|
|
4597
5744
|
onPrompt: sendPrompt,
|
|
4598
5745
|
onFocus: setFocusProduct,
|
|
@@ -4606,6 +5753,8 @@ function ExplorePage({
|
|
|
4606
5753
|
message,
|
|
4607
5754
|
activity,
|
|
4608
5755
|
imageOrigins,
|
|
5756
|
+
productNames,
|
|
5757
|
+
speechLang,
|
|
4609
5758
|
onNavigate,
|
|
4610
5759
|
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
|
|
4611
5760
|
onFollow: (kind, url) => recordClick(message.id, kind, url),
|
|
@@ -4909,8 +6058,13 @@ function ExplorePage({
|
|
|
4909
6058
|
}
|
|
4910
6059
|
|
|
4911
6060
|
exports.ExplorePage = ExplorePage;
|
|
6061
|
+
exports.answerPlainText = answerPlainText;
|
|
6062
|
+
exports.answerSpeechText = answerSpeechText;
|
|
4912
6063
|
exports.inlineImageOrigins = inlineImageOrigins;
|
|
4913
6064
|
exports.isInlineImageAllowed = isInlineImageAllowed;
|
|
4914
6065
|
exports.logoUrlOf = logoUrlOf;
|
|
6066
|
+
exports.productNamesByOrigin = productNamesByOrigin;
|
|
6067
|
+
exports.referenceKindOf = referenceKindOf;
|
|
6068
|
+
exports.summariseReferences = summariseReferences;
|
|
4915
6069
|
//# sourceMappingURL=explore.js.map
|
|
4916
6070
|
//# sourceMappingURL=explore.js.map
|