@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
package/dist/index.js
CHANGED
|
@@ -2438,16 +2438,16 @@ function useRateLimit(maxAttempts, windowMinutes) {
|
|
|
2438
2438
|
}
|
|
2439
2439
|
}, [maxAttempts]);
|
|
2440
2440
|
function recordAttempt() {
|
|
2441
|
-
const
|
|
2441
|
+
const now2 = Date.now();
|
|
2442
2442
|
const state = rateLimitRef.current;
|
|
2443
|
-
if (state &&
|
|
2443
|
+
if (state && now2 > state.resetTime) {
|
|
2444
2444
|
rateLimitRef.current = null;
|
|
2445
2445
|
}
|
|
2446
2446
|
const current = rateLimitRef.current;
|
|
2447
2447
|
if (!current) {
|
|
2448
2448
|
rateLimitRef.current = {
|
|
2449
2449
|
attempts: 1,
|
|
2450
|
-
resetTime:
|
|
2450
|
+
resetTime: now2 + windowMinutes * 60 * 1e3
|
|
2451
2451
|
};
|
|
2452
2452
|
return false;
|
|
2453
2453
|
}
|
|
@@ -3542,16 +3542,16 @@ function useRateLimit2(maxAttempts, windowMinutes) {
|
|
|
3542
3542
|
const [isLimited, setIsLimited] = React.useState(false);
|
|
3543
3543
|
const [resetTime, setResetTime] = React.useState(null);
|
|
3544
3544
|
function recordAttempt() {
|
|
3545
|
-
const
|
|
3545
|
+
const now2 = Date.now();
|
|
3546
3546
|
const state = rateLimitRef.current;
|
|
3547
|
-
if (state &&
|
|
3547
|
+
if (state && now2 > state.resetTime) {
|
|
3548
3548
|
rateLimitRef.current = null;
|
|
3549
3549
|
}
|
|
3550
3550
|
const current = rateLimitRef.current;
|
|
3551
3551
|
if (!current) {
|
|
3552
3552
|
rateLimitRef.current = {
|
|
3553
3553
|
attempts: 1,
|
|
3554
|
-
resetTime:
|
|
3554
|
+
resetTime: now2 + windowMinutes * 60 * 1e3
|
|
3555
3555
|
};
|
|
3556
3556
|
return false;
|
|
3557
3557
|
}
|
|
@@ -4585,8 +4585,8 @@ function LaunchCountdown({
|
|
|
4585
4585
|
launchedMessage = "We have launched!",
|
|
4586
4586
|
className
|
|
4587
4587
|
}) {
|
|
4588
|
-
const [
|
|
4589
|
-
const remainingMs = launchDate -
|
|
4588
|
+
const [now2, setNow] = React.useState(() => Date.now());
|
|
4589
|
+
const remainingMs = launchDate - now2;
|
|
4590
4590
|
const hasLaunched = remainingMs <= 0;
|
|
4591
4591
|
React.useEffect(() => {
|
|
4592
4592
|
if (hasLaunched) return;
|
|
@@ -6004,6 +6004,23 @@ var EN = {
|
|
|
6004
6004
|
nextSuggestion: "Next suggestion",
|
|
6005
6005
|
conceptIllustration: "Concept illustration",
|
|
6006
6006
|
opensInNewTab: "opens in a new tab",
|
|
6007
|
+
conceptStripTitle: "Or start from a concept",
|
|
6008
|
+
conceptStripLabel: "Concepts you can ask about",
|
|
6009
|
+
conceptAsk: "Ask about {title} in {product}",
|
|
6010
|
+
conceptAskQuestion: "How does {title} work in {product}?",
|
|
6011
|
+
copyAnswer: "Copy answer",
|
|
6012
|
+
copyFailed: "Couldn't copy",
|
|
6013
|
+
readAloud: "Read aloud",
|
|
6014
|
+
stopReading: "Stop reading",
|
|
6015
|
+
answerTable: "Table",
|
|
6016
|
+
groundedIn: "What this answer is based on",
|
|
6017
|
+
sourceCountOne: "1 source",
|
|
6018
|
+
sourceCountMany: "{count} sources",
|
|
6019
|
+
sourceKindConcept: "Concept",
|
|
6020
|
+
sourceKindDocs: "Documentation",
|
|
6021
|
+
sourceKindPricing: "Pricing",
|
|
6022
|
+
sourceKindUseCase: "Use case",
|
|
6023
|
+
sourceKindPage: "Product page",
|
|
6007
6024
|
seoTitle: "Describe a problem, get an answer",
|
|
6008
6025
|
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.",
|
|
6009
6026
|
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.",
|
|
@@ -6072,6 +6089,23 @@ var ZH = {
|
|
|
6072
6089
|
nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
|
|
6073
6090
|
conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
|
|
6074
6091
|
opensInNewTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00",
|
|
6092
|
+
conceptStripTitle: "\u6216\u8005\u4ECE\u4E00\u4E2A\u6982\u5FF5\u5F00\u59CB",
|
|
6093
|
+
conceptStripLabel: "\u4F60\u53EF\u4EE5\u8BE2\u95EE\u7684\u6982\u5FF5",
|
|
6094
|
+
conceptAsk: "\u8BE2\u95EE {product} \u4E2D\u7684 {title}",
|
|
6095
|
+
conceptAskQuestion: "{product} \u7684 {title} \u662F\u600E\u4E48\u8FD0\u4F5C\u7684\uFF1F",
|
|
6096
|
+
copyAnswer: "\u590D\u5236\u56DE\u7B54",
|
|
6097
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
6098
|
+
readAloud: "\u6717\u8BFB",
|
|
6099
|
+
stopReading: "\u505C\u6B62\u6717\u8BFB",
|
|
6100
|
+
answerTable: "\u8868\u683C",
|
|
6101
|
+
groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
|
|
6102
|
+
sourceCountOne: "1 \u4E2A\u6765\u6E90",
|
|
6103
|
+
sourceCountMany: "{count} \u4E2A\u6765\u6E90",
|
|
6104
|
+
sourceKindConcept: "\u6982\u5FF5",
|
|
6105
|
+
sourceKindDocs: "\u6587\u6863",
|
|
6106
|
+
sourceKindPricing: "\u4EF7\u683C",
|
|
6107
|
+
sourceKindUseCase: "\u5E94\u7528\u573A\u666F",
|
|
6108
|
+
sourceKindPage: "\u4EA7\u54C1\u9875\u9762",
|
|
6075
6109
|
seoTitle: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u83B7\u5F97\u7B54\u6848",
|
|
6076
6110
|
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",
|
|
6077
6111
|
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",
|
|
@@ -6136,6 +6170,23 @@ var HI = {
|
|
|
6136
6170
|
nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
|
|
6137
6171
|
conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
|
|
6138
6172
|
opensInNewTab: "\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948",
|
|
6173
|
+
conceptStripTitle: "\u092F\u093E \u0915\u093F\u0938\u0940 \u0905\u0935\u0927\u093E\u0930\u0923\u093E \u0938\u0947 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
|
|
6174
|
+
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",
|
|
6175
|
+
conceptAsk: "{product} \u092E\u0947\u0902 {title} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902",
|
|
6176
|
+
conceptAskQuestion: "{product} \u092E\u0947\u0902 {title} \u0915\u0948\u0938\u0947 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948?",
|
|
6177
|
+
copyAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
|
|
6178
|
+
copyFailed: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E",
|
|
6179
|
+
readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
|
|
6180
|
+
stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
6181
|
+
answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
|
|
6182
|
+
groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
|
|
6183
|
+
sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
|
|
6184
|
+
sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
|
|
6185
|
+
sourceKindConcept: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E",
|
|
6186
|
+
sourceKindDocs: "\u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C",
|
|
6187
|
+
sourceKindPricing: "\u092E\u0942\u0932\u094D\u092F",
|
|
6188
|
+
sourceKindUseCase: "\u0909\u092A\u092F\u094B\u0917 \u0915\u093E \u0909\u0926\u093E\u0939\u0930\u0923",
|
|
6189
|
+
sourceKindPage: "\u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0947\u091C",
|
|
6139
6190
|
seoTitle: "\u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902, \u091C\u0935\u093E\u092C \u092A\u093E\u090F\u0902",
|
|
6140
6191
|
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",
|
|
6141
6192
|
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",
|
|
@@ -6204,6 +6255,23 @@ var ES = {
|
|
|
6204
6255
|
nextSuggestion: "Sugerencia siguiente",
|
|
6205
6256
|
conceptIllustration: "Ilustraci\xF3n del concepto",
|
|
6206
6257
|
opensInNewTab: "se abre en una pesta\xF1a nueva",
|
|
6258
|
+
conceptStripTitle: "O empieza por un concepto",
|
|
6259
|
+
conceptStripLabel: "Conceptos sobre los que puedes preguntar",
|
|
6260
|
+
conceptAsk: "Preguntar por {title} en {product}",
|
|
6261
|
+
conceptAskQuestion: "\xBFC\xF3mo funciona {title} en {product}?",
|
|
6262
|
+
copyAnswer: "Copiar respuesta",
|
|
6263
|
+
copyFailed: "No se pudo copiar",
|
|
6264
|
+
readAloud: "Leer en voz alta",
|
|
6265
|
+
stopReading: "Dejar de leer",
|
|
6266
|
+
answerTable: "Tabla",
|
|
6267
|
+
groundedIn: "En qu\xE9 se basa esta respuesta",
|
|
6268
|
+
sourceCountOne: "1 fuente",
|
|
6269
|
+
sourceCountMany: "{count} fuentes",
|
|
6270
|
+
sourceKindConcept: "Concepto",
|
|
6271
|
+
sourceKindDocs: "Documentaci\xF3n",
|
|
6272
|
+
sourceKindPricing: "Precios",
|
|
6273
|
+
sourceKindUseCase: "Caso de uso",
|
|
6274
|
+
sourceKindPage: "P\xE1gina de producto",
|
|
6207
6275
|
seoTitle: "Describe un problema y obt\xE9n una respuesta",
|
|
6208
6276
|
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.",
|
|
6209
6277
|
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.",
|
|
@@ -6272,6 +6340,23 @@ var AR = {
|
|
|
6272
6340
|
nextSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u062A\u0627\u0644\u064A",
|
|
6273
6341
|
conceptIllustration: "\u0631\u0633\u0645 \u062A\u0648\u0636\u064A\u062D\u064A \u0644\u0644\u0645\u0641\u0647\u0648\u0645",
|
|
6274
6342
|
opensInNewTab: "\u064A\u064F\u0641\u062A\u062D \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u062C\u062F\u064A\u062F\u0629",
|
|
6343
|
+
conceptStripTitle: "\u0623\u0648 \u0627\u0628\u062F\u0623 \u0645\u0646 \u0645\u0641\u0647\u0648\u0645",
|
|
6344
|
+
conceptStripLabel: "\u0645\u0641\u0627\u0647\u064A\u0645 \u064A\u0645\u0643\u0646\u0643 \u0627\u0644\u0633\u0624\u0627\u0644 \u0639\u0646\u0647\u0627",
|
|
6345
|
+
conceptAsk: "\u0627\u0633\u0623\u0644 \u0639\u0646 {title} \u0641\u064A {product}",
|
|
6346
|
+
conceptAskQuestion: "\u0643\u064A\u0641 \u064A\u0639\u0645\u0644 {title} \u0641\u064A {product}\u061F",
|
|
6347
|
+
copyAnswer: "\u0646\u0633\u062E \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
6348
|
+
copyFailed: "\u062A\u0639\u0630\u0651\u0631 \u0627\u0644\u0646\u0633\u062E",
|
|
6349
|
+
readAloud: "\u0627\u0642\u0631\u0623 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D",
|
|
6350
|
+
stopReading: "\u0625\u064A\u0642\u0627\u0641 \u0627\u0644\u0642\u0631\u0627\u0621\u0629",
|
|
6351
|
+
answerTable: "\u062C\u062F\u0648\u0644",
|
|
6352
|
+
groundedIn: "\u0639\u0644\u0649 \u0645\u0627\u0630\u0627 \u062A\u0633\u062A\u0646\u062F \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629",
|
|
6353
|
+
sourceCountOne: "\u0645\u0635\u062F\u0631 \u0648\u0627\u062D\u062F",
|
|
6354
|
+
sourceCountMany: "{count} \u0645\u0635\u0627\u062F\u0631",
|
|
6355
|
+
sourceKindConcept: "\u0645\u0641\u0647\u0648\u0645",
|
|
6356
|
+
sourceKindDocs: "\u0627\u0644\u062A\u0648\u062B\u064A\u0642",
|
|
6357
|
+
sourceKindPricing: "\u0627\u0644\u0623\u0633\u0639\u0627\u0631",
|
|
6358
|
+
sourceKindUseCase: "\u062D\u0627\u0644\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645",
|
|
6359
|
+
sourceKindPage: "\u0635\u0641\u062D\u0629 \u0627\u0644\u0645\u0646\u062A\u062C",
|
|
6275
6360
|
seoTitle: "\u0635\u0641 \u0645\u0634\u0643\u0644\u062A\u0643 \u0648\u0627\u062D\u0635\u0644 \u0639\u0644\u0649 \u0625\u062C\u0627\u0628\u0629",
|
|
6276
6361
|
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.",
|
|
6277
6362
|
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.",
|
|
@@ -6340,6 +6425,23 @@ var TA = {
|
|
|
6340
6425
|
nextSuggestion: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
|
|
6341
6426
|
conceptIllustration: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BB5\u0BBF\u0BB3\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BAE\u0BCD",
|
|
6342
6427
|
opensInNewTab: "\u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0BA4\u0BBE\u0BB5\u0BB2\u0BBF\u0BB2\u0BCD \u0BA4\u0BBF\u0BB1\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD",
|
|
6428
|
+
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",
|
|
6429
|
+
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",
|
|
6430
|
+
conceptAsk: "{product} \u0B87\u0BB2\u0BCD {title} \u0BAA\u0BB1\u0BCD\u0BB1\u0BBF\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
6431
|
+
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?",
|
|
6432
|
+
copyAnswer: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1",
|
|
6433
|
+
copyFailed: "\u0BA8\u0B95\u0BB2\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8",
|
|
6434
|
+
readAloud: "\u0B9A\u0BA4\u0BCD\u0BA4\u0BAE\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF",
|
|
6435
|
+
stopReading: "\u0BAA\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0BA4\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
6436
|
+
answerTable: "\u0B85\u0B9F\u0BCD\u0B9F\u0BB5\u0BA3\u0BC8",
|
|
6437
|
+
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",
|
|
6438
|
+
sourceCountOne: "1 \u0BAE\u0BC2\u0BB2\u0BAE\u0BCD",
|
|
6439
|
+
sourceCountMany: "{count} \u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
6440
|
+
sourceKindConcept: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
6441
|
+
sourceKindDocs: "\u0B86\u0BB5\u0BA3\u0BAE\u0BCD",
|
|
6442
|
+
sourceKindPricing: "\u0BB5\u0BBF\u0BB2\u0BC8",
|
|
6443
|
+
sourceKindUseCase: "\u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1 \u0BA8\u0BBF\u0BB2\u0BC8",
|
|
6444
|
+
sourceKindPage: "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1\u0BAA\u0BCD \u0BAA\u0B95\u0BCD\u0B95\u0BAE\u0BCD",
|
|
6343
6445
|
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",
|
|
6344
6446
|
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.",
|
|
6345
6447
|
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.",
|
|
@@ -6363,6 +6465,17 @@ var STRINGS = {
|
|
|
6363
6465
|
ar: AR,
|
|
6364
6466
|
ta: TA
|
|
6365
6467
|
};
|
|
6468
|
+
var SPEECH_TAGS = {
|
|
6469
|
+
en: "en-US",
|
|
6470
|
+
zh: "zh-CN",
|
|
6471
|
+
hi: "hi-IN",
|
|
6472
|
+
es: "es-ES",
|
|
6473
|
+
ar: "ar-SA",
|
|
6474
|
+
ta: "ta-IN"
|
|
6475
|
+
};
|
|
6476
|
+
function speechLocaleOf(locale) {
|
|
6477
|
+
return SPEECH_TAGS[locale];
|
|
6478
|
+
}
|
|
6366
6479
|
function resolveExploreLocale(explicit) {
|
|
6367
6480
|
const candidates = [
|
|
6368
6481
|
explicit,
|
|
@@ -6441,11 +6554,9 @@ var EXPLORE_OWN_MESSAGE_FIELDS = (
|
|
|
6441
6554
|
}
|
|
6442
6555
|
`
|
|
6443
6556
|
);
|
|
6444
|
-
var
|
|
6557
|
+
var EXPLORE_CATALOG_ESTABLISHED_FIELDS = (
|
|
6445
6558
|
/* GraphQL */
|
|
6446
6559
|
`
|
|
6447
|
-
query ExploreCatalog($productSlug: String) {
|
|
6448
|
-
exploreCatalog(productSlug: $productSlug) {
|
|
6449
6560
|
enabled
|
|
6450
6561
|
products {
|
|
6451
6562
|
slug
|
|
@@ -6463,11 +6574,35 @@ var EXPLORE_CATALOG_QUERY = (
|
|
|
6463
6574
|
historyTurns
|
|
6464
6575
|
}
|
|
6465
6576
|
captchaRequired
|
|
6466
|
-
recaptchaSiteKey
|
|
6577
|
+
recaptchaSiteKey`
|
|
6578
|
+
);
|
|
6579
|
+
var EXPLORE_CATALOG_HIGHLIGHT_FIELD = (
|
|
6580
|
+
/* GraphQL */
|
|
6581
|
+
`
|
|
6582
|
+
conceptHighlights {
|
|
6583
|
+
product
|
|
6584
|
+
productName
|
|
6585
|
+
title
|
|
6586
|
+
url
|
|
6587
|
+
imageUrl
|
|
6588
|
+
alt
|
|
6589
|
+
}`
|
|
6590
|
+
);
|
|
6591
|
+
function exploreCatalogDocument(extraFields) {
|
|
6592
|
+
return (
|
|
6593
|
+
/* GraphQL */
|
|
6594
|
+
`
|
|
6595
|
+
query ExploreCatalog($productSlug: String) {
|
|
6596
|
+
exploreCatalog(productSlug: $productSlug) {${EXPLORE_CATALOG_ESTABLISHED_FIELDS}${extraFields}
|
|
6467
6597
|
}
|
|
6468
6598
|
}
|
|
6469
6599
|
`
|
|
6600
|
+
);
|
|
6601
|
+
}
|
|
6602
|
+
var EXPLORE_CATALOG_QUERY = exploreCatalogDocument(
|
|
6603
|
+
EXPLORE_CATALOG_HIGHLIGHT_FIELD
|
|
6470
6604
|
);
|
|
6605
|
+
var EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS = exploreCatalogDocument("");
|
|
6471
6606
|
var EXPLORE_MESSAGE_QUERY = (
|
|
6472
6607
|
/* GraphQL */
|
|
6473
6608
|
`
|
|
@@ -7008,7 +7143,12 @@ function buildFallbackCatalog(examplePrompts, welcome) {
|
|
|
7008
7143
|
welcomeBody: welcome?.body ?? FALLBACK_WELCOME_BODY,
|
|
7009
7144
|
limits: FALLBACK_EXPLORE_LIMITS,
|
|
7010
7145
|
captchaRequired: false,
|
|
7011
|
-
recaptchaSiteKey: null
|
|
7146
|
+
recaptchaSiteKey: null,
|
|
7147
|
+
// Empty, and deliberately not synthesised from `ECOSYSTEM_PRODUCTS`: a concept highlight
|
|
7148
|
+
// needs a real published illustration URL, and this SDK has no registry of them. An
|
|
7149
|
+
// invented path would render 38 broken frames on the one screen every prerender captures.
|
|
7150
|
+
// The strip renders nothing at all when the list is empty, which is the honest fallback.
|
|
7151
|
+
conceptHighlights: []
|
|
7012
7152
|
};
|
|
7013
7153
|
}
|
|
7014
7154
|
function normalizeCatalog(raw, fallback) {
|
|
@@ -7025,9 +7165,27 @@ function normalizeCatalog(raw, fallback) {
|
|
|
7025
7165
|
historyTurns: raw.limits?.historyTurns || fallback.limits.historyTurns
|
|
7026
7166
|
},
|
|
7027
7167
|
captchaRequired: raw.captchaRequired === true,
|
|
7028
|
-
recaptchaSiteKey: raw.recaptchaSiteKey ?? null
|
|
7168
|
+
recaptchaSiteKey: raw.recaptchaSiteKey ?? null,
|
|
7169
|
+
// `[ExploreConceptHighlight!]!` on the wire, so a gateway that KNOWS the field always
|
|
7170
|
+
// sends an array. The one response that arrives without it is the retry against an older
|
|
7171
|
+
// supergraph (`EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS`) — that gateway does not answer
|
|
7172
|
+
// the full document at all, it refuses it. Either way
|
|
7173
|
+
// `catalog.conceptHighlights.filter(...)` on `undefined` would throw inside render,
|
|
7174
|
+
// blanking the welcome screen over a decorative strip.
|
|
7175
|
+
conceptHighlights: raw.conceptHighlights?.length ? raw.conceptHighlights : fallback.conceptHighlights
|
|
7029
7176
|
};
|
|
7030
7177
|
}
|
|
7178
|
+
var CATALOG_DOCUMENT_REJECTED_CODES = /* @__PURE__ */ new Set([
|
|
7179
|
+
"GRAPHQL_VALIDATION_FAILED",
|
|
7180
|
+
"HTTP_400"
|
|
7181
|
+
]);
|
|
7182
|
+
function catalogRejectedTheHighlightField(res) {
|
|
7183
|
+
if (res.data?.exploreCatalog) return false;
|
|
7184
|
+
return (res.errors ?? []).some((error) => {
|
|
7185
|
+
const code = error.extensions?.code;
|
|
7186
|
+
return typeof code === "string" && CATALOG_DOCUMENT_REJECTED_CODES.has(code) || error.message.includes("conceptHighlights");
|
|
7187
|
+
});
|
|
7188
|
+
}
|
|
7031
7189
|
var RATE_LIMIT_CODES = /* @__PURE__ */ new Set([
|
|
7032
7190
|
"RATE_LIMITED",
|
|
7033
7191
|
"HTTP_429",
|
|
@@ -7367,11 +7525,19 @@ function useExploreChat(options = {}) {
|
|
|
7367
7525
|
React.useEffect(() => {
|
|
7368
7526
|
let cancelled = false;
|
|
7369
7527
|
const load = async () => {
|
|
7370
|
-
const
|
|
7528
|
+
const variables = { productSlug: productSlug ?? null };
|
|
7529
|
+
let res = await client.query(
|
|
7371
7530
|
EXPLORE_CATALOG_QUERY,
|
|
7372
|
-
|
|
7531
|
+
variables
|
|
7373
7532
|
);
|
|
7374
7533
|
if (cancelled || !mountedRef.current) return;
|
|
7534
|
+
if (catalogRejectedTheHighlightField(res)) {
|
|
7535
|
+
res = await client.query(
|
|
7536
|
+
EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,
|
|
7537
|
+
variables
|
|
7538
|
+
);
|
|
7539
|
+
if (cancelled || !mountedRef.current) return;
|
|
7540
|
+
}
|
|
7375
7541
|
const raw = res.data?.exploreCatalog;
|
|
7376
7542
|
if (res.errors?.length || !raw) {
|
|
7377
7543
|
console.warn(
|
|
@@ -7817,6 +7983,224 @@ function makeOptimisticId() {
|
|
|
7817
7983
|
optimisticCounter += 1;
|
|
7818
7984
|
return `boff-explore-local-${Date.now()}-${optimisticCounter}`;
|
|
7819
7985
|
}
|
|
7986
|
+
var DRAIN_TARGET_MS = 1200;
|
|
7987
|
+
var MIN_CHARS_PER_SECOND = 90;
|
|
7988
|
+
var MAX_CHARS_PER_SECOND = 2200;
|
|
7989
|
+
var MAX_STEP_MS = 250;
|
|
7990
|
+
var COMMIT_INTERVAL_MS = 45;
|
|
7991
|
+
var FALLBACK_STEP_MS = 50;
|
|
7992
|
+
var FENCE = /^ {0,3}(?:```|~~~)/;
|
|
7993
|
+
var TABLE_ROW = /^ {0,3}\|/;
|
|
7994
|
+
var EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\d{1,9}[.)])\s*$/;
|
|
7995
|
+
function isWordCharacter(char) {
|
|
7996
|
+
return char !== void 0 && /[\p{L}\p{N}]/u.test(char);
|
|
7997
|
+
}
|
|
7998
|
+
function clamp(value, min, max) {
|
|
7999
|
+
return Math.min(Math.max(value, min), max);
|
|
8000
|
+
}
|
|
8001
|
+
function now() {
|
|
8002
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
8003
|
+
}
|
|
8004
|
+
function isHighSurrogate(code) {
|
|
8005
|
+
return code >= 55296 && code <= 56319;
|
|
8006
|
+
}
|
|
8007
|
+
function safeInlineEnd(line) {
|
|
8008
|
+
let codeSpan = null;
|
|
8009
|
+
let link = null;
|
|
8010
|
+
const emphasis = [];
|
|
8011
|
+
let at = 0;
|
|
8012
|
+
while (at < line.length) {
|
|
8013
|
+
const char = line[at] ?? "";
|
|
8014
|
+
if (char === "\\") {
|
|
8015
|
+
at += 2;
|
|
8016
|
+
continue;
|
|
8017
|
+
}
|
|
8018
|
+
if (char === "`") {
|
|
8019
|
+
let run = 1;
|
|
8020
|
+
while (line[at + run] === "`") run += 1;
|
|
8021
|
+
codeSpan = codeSpan === null ? at : null;
|
|
8022
|
+
at += run;
|
|
8023
|
+
continue;
|
|
8024
|
+
}
|
|
8025
|
+
if (codeSpan !== null) {
|
|
8026
|
+
at += 1;
|
|
8027
|
+
continue;
|
|
8028
|
+
}
|
|
8029
|
+
if (char === "[") {
|
|
8030
|
+
if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
|
|
8031
|
+
at += 1;
|
|
8032
|
+
continue;
|
|
8033
|
+
}
|
|
8034
|
+
if (char === "]") {
|
|
8035
|
+
if (link !== null && line[at + 1] !== "(") link = null;
|
|
8036
|
+
at += 1;
|
|
8037
|
+
continue;
|
|
8038
|
+
}
|
|
8039
|
+
if (char === ")") {
|
|
8040
|
+
link = null;
|
|
8041
|
+
at += 1;
|
|
8042
|
+
continue;
|
|
8043
|
+
}
|
|
8044
|
+
if (char === "*" || char === "_" || char === "~") {
|
|
8045
|
+
let run = 1;
|
|
8046
|
+
while (line[at + run] === char) run += 1;
|
|
8047
|
+
const start = at;
|
|
8048
|
+
at += run;
|
|
8049
|
+
const before = line[start - 1];
|
|
8050
|
+
const after = line[start + run];
|
|
8051
|
+
if (run < 2) continue;
|
|
8052
|
+
if (char === "_" && isWordCharacter(before) && isWordCharacter(after)) {
|
|
8053
|
+
continue;
|
|
8054
|
+
}
|
|
8055
|
+
const marker = char.repeat(run);
|
|
8056
|
+
const open2 = emphasis.findIndex((entry) => entry.marker === marker);
|
|
8057
|
+
if (open2 !== -1) {
|
|
8058
|
+
emphasis.splice(open2, 1);
|
|
8059
|
+
continue;
|
|
8060
|
+
}
|
|
8061
|
+
if (after !== void 0 && /\s/.test(after)) continue;
|
|
8062
|
+
emphasis.push({ marker, start });
|
|
8063
|
+
continue;
|
|
8064
|
+
}
|
|
8065
|
+
at += 1;
|
|
8066
|
+
}
|
|
8067
|
+
const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
|
|
8068
|
+
(value) => value !== null
|
|
8069
|
+
);
|
|
8070
|
+
return open.length === 0 ? line.length : Math.min(...open);
|
|
8071
|
+
}
|
|
8072
|
+
function revealBoundary(text, limit) {
|
|
8073
|
+
if (limit <= 0) return 0;
|
|
8074
|
+
const caughtUp = limit >= text.length;
|
|
8075
|
+
const head = caughtUp ? text : text.slice(0, limit);
|
|
8076
|
+
const lineStart = head.lastIndexOf("\n") + 1;
|
|
8077
|
+
const line = head.slice(lineStart);
|
|
8078
|
+
let fenceStart = null;
|
|
8079
|
+
let tableStart = null;
|
|
8080
|
+
let tableRows = 0;
|
|
8081
|
+
let at = 0;
|
|
8082
|
+
while (at < lineStart) {
|
|
8083
|
+
const breakAt = head.indexOf("\n", at);
|
|
8084
|
+
const end = breakAt === -1 ? lineStart : breakAt;
|
|
8085
|
+
const row = head.slice(at, end);
|
|
8086
|
+
if (fenceStart !== null) {
|
|
8087
|
+
if (FENCE.test(row)) fenceStart = null;
|
|
8088
|
+
} else if (FENCE.test(row)) {
|
|
8089
|
+
fenceStart = at;
|
|
8090
|
+
} else if (TABLE_ROW.test(row)) {
|
|
8091
|
+
if (tableStart === null) {
|
|
8092
|
+
tableStart = at;
|
|
8093
|
+
tableRows = 0;
|
|
8094
|
+
}
|
|
8095
|
+
tableRows += 1;
|
|
8096
|
+
} else if (tableStart !== null) {
|
|
8097
|
+
tableStart = null;
|
|
8098
|
+
tableRows = 0;
|
|
8099
|
+
}
|
|
8100
|
+
at = end + 1;
|
|
8101
|
+
}
|
|
8102
|
+
if (fenceStart === null) {
|
|
8103
|
+
const lastIsRow = TABLE_ROW.test(line);
|
|
8104
|
+
if (lastIsRow && tableStart === null) {
|
|
8105
|
+
tableStart = lineStart;
|
|
8106
|
+
tableRows = 0;
|
|
8107
|
+
} else if (!lastIsRow && line !== "") {
|
|
8108
|
+
tableStart = null;
|
|
8109
|
+
tableRows = 0;
|
|
8110
|
+
}
|
|
8111
|
+
if (tableStart !== null && tableRows < 2) return tableStart;
|
|
8112
|
+
}
|
|
8113
|
+
if (caughtUp) return text.length;
|
|
8114
|
+
if (fenceStart !== null) return fenceStart;
|
|
8115
|
+
if (TABLE_ROW.test(line)) return lineStart;
|
|
8116
|
+
if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;
|
|
8117
|
+
const cut = lineStart + safeInlineEnd(line);
|
|
8118
|
+
return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;
|
|
8119
|
+
}
|
|
8120
|
+
function prefersReducedMotion() {
|
|
8121
|
+
if (typeof window === "undefined") return true;
|
|
8122
|
+
try {
|
|
8123
|
+
return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
8124
|
+
} catch {
|
|
8125
|
+
return false;
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
function useProgressiveReveal(text, settled) {
|
|
8129
|
+
const [, setFrame] = React.useState(0);
|
|
8130
|
+
const [reduced] = React.useState(prefersReducedMotion);
|
|
8131
|
+
const immediate = settled || reduced;
|
|
8132
|
+
const textRef = React.useRef(text);
|
|
8133
|
+
const cursorRef = React.useRef(immediate ? text.length : 0);
|
|
8134
|
+
const cutRef = React.useRef(immediate ? text.length : 0);
|
|
8135
|
+
const committedRef = React.useRef(0);
|
|
8136
|
+
if (text !== textRef.current) {
|
|
8137
|
+
const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));
|
|
8138
|
+
if (revealed !== "" && !text.startsWith(revealed)) {
|
|
8139
|
+
cursorRef.current = text.length;
|
|
8140
|
+
}
|
|
8141
|
+
cursorRef.current = Math.min(cursorRef.current, text.length);
|
|
8142
|
+
cutRef.current = Math.min(cutRef.current, text.length);
|
|
8143
|
+
textRef.current = text;
|
|
8144
|
+
}
|
|
8145
|
+
if (immediate) cursorRef.current = text.length;
|
|
8146
|
+
const cut = immediate ? text.length : Math.max(
|
|
8147
|
+
cutRef.current,
|
|
8148
|
+
revealBoundary(text, Math.floor(cursorRef.current))
|
|
8149
|
+
);
|
|
8150
|
+
cutRef.current = cut;
|
|
8151
|
+
React.useEffect(() => {
|
|
8152
|
+
if (immediate) return;
|
|
8153
|
+
if (cursorRef.current >= text.length) return;
|
|
8154
|
+
let cancelled = false;
|
|
8155
|
+
let cancelStep = null;
|
|
8156
|
+
let previous = now();
|
|
8157
|
+
const commit = (at, force) => {
|
|
8158
|
+
if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;
|
|
8159
|
+
committedRef.current = at;
|
|
8160
|
+
setFrame((value) => value + 1);
|
|
8161
|
+
};
|
|
8162
|
+
const step = () => {
|
|
8163
|
+
cancelStep = null;
|
|
8164
|
+
if (cancelled) return;
|
|
8165
|
+
const at = now();
|
|
8166
|
+
const elapsed = clamp(at - previous, 0, MAX_STEP_MS);
|
|
8167
|
+
previous = at;
|
|
8168
|
+
const length = textRef.current.length;
|
|
8169
|
+
const pending = length - cursorRef.current;
|
|
8170
|
+
if (pending <= 0) {
|
|
8171
|
+
commit(at, true);
|
|
8172
|
+
return;
|
|
8173
|
+
}
|
|
8174
|
+
const perSecond = clamp(
|
|
8175
|
+
pending / (DRAIN_TARGET_MS / 1e3),
|
|
8176
|
+
MIN_CHARS_PER_SECOND,
|
|
8177
|
+
MAX_CHARS_PER_SECOND
|
|
8178
|
+
);
|
|
8179
|
+
cursorRef.current = Math.min(
|
|
8180
|
+
length,
|
|
8181
|
+
cursorRef.current + perSecond * elapsed / 1e3
|
|
8182
|
+
);
|
|
8183
|
+
const done = cursorRef.current >= length;
|
|
8184
|
+
commit(at, done);
|
|
8185
|
+
if (!done) schedule();
|
|
8186
|
+
};
|
|
8187
|
+
const schedule = () => {
|
|
8188
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
8189
|
+
const id2 = window.requestAnimationFrame(step);
|
|
8190
|
+
cancelStep = () => window.cancelAnimationFrame(id2);
|
|
8191
|
+
return;
|
|
8192
|
+
}
|
|
8193
|
+
const id = setTimeout(step, FALLBACK_STEP_MS);
|
|
8194
|
+
cancelStep = () => clearTimeout(id);
|
|
8195
|
+
};
|
|
8196
|
+
schedule();
|
|
8197
|
+
return () => {
|
|
8198
|
+
cancelled = true;
|
|
8199
|
+
cancelStep?.();
|
|
8200
|
+
};
|
|
8201
|
+
}, [text, immediate]);
|
|
8202
|
+
return text.slice(0, cut);
|
|
8203
|
+
}
|
|
7820
8204
|
function getRecognitionCtor() {
|
|
7821
8205
|
if (typeof window === "undefined") return null;
|
|
7822
8206
|
const w = window;
|
|
@@ -8035,6 +8419,141 @@ function useSpeechInput({
|
|
|
8035
8419
|
);
|
|
8036
8420
|
return { supported, listening, interim, error, start, stop, toggle };
|
|
8037
8421
|
}
|
|
8422
|
+
var SILENT_FAILURE_MS = 1500;
|
|
8423
|
+
function speechPlatform() {
|
|
8424
|
+
if (typeof window === "undefined") return null;
|
|
8425
|
+
const scope = window;
|
|
8426
|
+
const synth = scope.speechSynthesis;
|
|
8427
|
+
const Utterance = scope.SpeechSynthesisUtterance;
|
|
8428
|
+
if (!synth || typeof synth.speak !== "function") return null;
|
|
8429
|
+
if (typeof Utterance !== "function") return null;
|
|
8430
|
+
return { synth, Utterance };
|
|
8431
|
+
}
|
|
8432
|
+
function normaliseTag(tag) {
|
|
8433
|
+
return tag.trim().toLowerCase().replace(/_/g, "-");
|
|
8434
|
+
}
|
|
8435
|
+
function primaryOf(tag) {
|
|
8436
|
+
return normaliseTag(tag).split("-")[0] ?? "";
|
|
8437
|
+
}
|
|
8438
|
+
function pickVoice(voices, lang) {
|
|
8439
|
+
const wanted = normaliseTag(lang);
|
|
8440
|
+
if (wanted === "") return null;
|
|
8441
|
+
const primary = primaryOf(wanted);
|
|
8442
|
+
const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);
|
|
8443
|
+
const related = voices.filter((voice) => primaryOf(voice.lang) === primary);
|
|
8444
|
+
return exact.find((voice) => voice.default) ?? exact[0] ?? related.find((voice) => voice.default) ?? related[0] ?? null;
|
|
8445
|
+
}
|
|
8446
|
+
var activeOwner = null;
|
|
8447
|
+
var activeStop = null;
|
|
8448
|
+
function stopSpeaking() {
|
|
8449
|
+
activeStop?.();
|
|
8450
|
+
}
|
|
8451
|
+
function useSpeechOutput({
|
|
8452
|
+
lang
|
|
8453
|
+
} = {}) {
|
|
8454
|
+
const [available, setAvailable] = React.useState(() => speechPlatform() !== null);
|
|
8455
|
+
const [speaking, setSpeaking] = React.useState(false);
|
|
8456
|
+
const utteranceRef = React.useRef(null);
|
|
8457
|
+
const watchdogRef = React.useRef(null);
|
|
8458
|
+
const ownerRef = React.useRef({});
|
|
8459
|
+
const stopRef = React.useRef(() => void 0);
|
|
8460
|
+
const langRef = React.useRef(lang);
|
|
8461
|
+
langRef.current = lang;
|
|
8462
|
+
const clearWatchdog = React.useCallback(() => {
|
|
8463
|
+
if (watchdogRef.current === null) return;
|
|
8464
|
+
clearTimeout(watchdogRef.current);
|
|
8465
|
+
watchdogRef.current = null;
|
|
8466
|
+
}, []);
|
|
8467
|
+
const settle = React.useCallback(() => {
|
|
8468
|
+
clearWatchdog();
|
|
8469
|
+
utteranceRef.current = null;
|
|
8470
|
+
if (activeOwner === ownerRef.current) {
|
|
8471
|
+
activeOwner = null;
|
|
8472
|
+
activeStop = null;
|
|
8473
|
+
}
|
|
8474
|
+
setSpeaking(false);
|
|
8475
|
+
}, [clearWatchdog]);
|
|
8476
|
+
const stop = React.useCallback(() => {
|
|
8477
|
+
const owned = utteranceRef.current !== null;
|
|
8478
|
+
settle();
|
|
8479
|
+
if (owned) speechPlatform()?.synth.cancel();
|
|
8480
|
+
}, [settle]);
|
|
8481
|
+
stopRef.current = stop;
|
|
8482
|
+
const speak = React.useCallback(
|
|
8483
|
+
(text) => {
|
|
8484
|
+
const platform = speechPlatform();
|
|
8485
|
+
if (!platform) {
|
|
8486
|
+
setAvailable(false);
|
|
8487
|
+
return;
|
|
8488
|
+
}
|
|
8489
|
+
const spoken = text.trim();
|
|
8490
|
+
if (spoken === "") return;
|
|
8491
|
+
if (activeStop && activeOwner !== ownerRef.current) activeStop();
|
|
8492
|
+
platform.synth.cancel();
|
|
8493
|
+
const utterance = new platform.Utterance(spoken);
|
|
8494
|
+
const wanted = langRef.current;
|
|
8495
|
+
if (wanted !== void 0 && wanted !== "") {
|
|
8496
|
+
const voice = pickVoice(platform.synth.getVoices(), wanted);
|
|
8497
|
+
utterance.lang = voice?.lang ?? wanted;
|
|
8498
|
+
if (voice) utterance.voice = voice;
|
|
8499
|
+
}
|
|
8500
|
+
const mine = () => utteranceRef.current === utterance;
|
|
8501
|
+
utterance.onstart = () => {
|
|
8502
|
+
if (!mine()) return;
|
|
8503
|
+
clearWatchdog();
|
|
8504
|
+
setSpeaking(true);
|
|
8505
|
+
};
|
|
8506
|
+
utterance.onend = () => {
|
|
8507
|
+
if (!mine()) return;
|
|
8508
|
+
settle();
|
|
8509
|
+
};
|
|
8510
|
+
utterance.onerror = (event) => {
|
|
8511
|
+
if (!mine()) return;
|
|
8512
|
+
const code = typeof event?.error === "string" ? event.error : "";
|
|
8513
|
+
settle();
|
|
8514
|
+
if (code !== "interrupted" && code !== "canceled") setAvailable(false);
|
|
8515
|
+
};
|
|
8516
|
+
utteranceRef.current = utterance;
|
|
8517
|
+
activeOwner = ownerRef.current;
|
|
8518
|
+
activeStop = () => {
|
|
8519
|
+
stopRef.current();
|
|
8520
|
+
};
|
|
8521
|
+
setSpeaking(true);
|
|
8522
|
+
platform.synth.speak(utterance);
|
|
8523
|
+
clearWatchdog();
|
|
8524
|
+
watchdogRef.current = setTimeout(() => {
|
|
8525
|
+
watchdogRef.current = null;
|
|
8526
|
+
if (!mine()) return;
|
|
8527
|
+
if (platform.synth.speaking) return;
|
|
8528
|
+
settle();
|
|
8529
|
+
setAvailable(false);
|
|
8530
|
+
}, SILENT_FAILURE_MS);
|
|
8531
|
+
},
|
|
8532
|
+
[clearWatchdog, settle]
|
|
8533
|
+
);
|
|
8534
|
+
const toggle = React.useCallback(
|
|
8535
|
+
(text) => {
|
|
8536
|
+
if (speaking) stop();
|
|
8537
|
+
else speak(text);
|
|
8538
|
+
},
|
|
8539
|
+
[speak, speaking, stop]
|
|
8540
|
+
);
|
|
8541
|
+
React.useEffect(() => {
|
|
8542
|
+
if (!speaking || typeof window === "undefined") return;
|
|
8543
|
+
const onHide = () => {
|
|
8544
|
+
stopRef.current();
|
|
8545
|
+
};
|
|
8546
|
+
window.addEventListener("pagehide", onHide);
|
|
8547
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
8548
|
+
}, [speaking]);
|
|
8549
|
+
React.useEffect(
|
|
8550
|
+
() => () => {
|
|
8551
|
+
stopRef.current();
|
|
8552
|
+
},
|
|
8553
|
+
[]
|
|
8554
|
+
);
|
|
8555
|
+
return { supported: available, speaking, speak, stop, toggle };
|
|
8556
|
+
}
|
|
8038
8557
|
var SWIPE_THRESHOLD = 40;
|
|
8039
8558
|
function useSwipe({
|
|
8040
8559
|
onSwipeLeft,
|
|
@@ -8302,6 +8821,115 @@ var EXPLORE_CSS = `
|
|
|
8302
8821
|
height: auto;
|
|
8303
8822
|
}
|
|
8304
8823
|
|
|
8824
|
+
/* ---- Comparison and pricing tables ----------------------------------------------------
|
|
8825
|
+
The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
|
|
8826
|
+
the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
|
|
8827
|
+
the page must not scroll sideways because of it, and it has to be readable on a 320px
|
|
8828
|
+
phone, which a four-column grid of 1px borders is not.
|
|
8829
|
+
|
|
8830
|
+
Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
|
|
8831
|
+
\`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
|
|
8832
|
+
cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
|
|
8833
|
+
of table from chaining to the page \u2014 the same rule the transcript needed. */
|
|
8834
|
+
.boff-explore-body .boff-explore-table {
|
|
8835
|
+
max-width: 100%;
|
|
8836
|
+
overflow-x: auto;
|
|
8837
|
+
overscroll-behavior-x: contain;
|
|
8838
|
+
margin: 0.75rem 0;
|
|
8839
|
+
border: 1px solid hsl(var(--border));
|
|
8840
|
+
border-radius: 0.75rem;
|
|
8841
|
+
background-color: hsl(var(--card));
|
|
8842
|
+
}
|
|
8843
|
+
.boff-explore-body .boff-explore-table:focus-visible {
|
|
8844
|
+
outline: 2px solid hsl(var(--ring));
|
|
8845
|
+
outline-offset: 2px;
|
|
8846
|
+
}
|
|
8847
|
+
.boff-explore-body .boff-explore-table table {
|
|
8848
|
+
/* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
|
|
8849
|
+
display: table;
|
|
8850
|
+
width: 100%;
|
|
8851
|
+
max-width: none;
|
|
8852
|
+
margin: 0;
|
|
8853
|
+
border-collapse: collapse;
|
|
8854
|
+
font-size: 0.8125rem;
|
|
8855
|
+
}
|
|
8856
|
+
.boff-explore-body .boff-explore-table th,
|
|
8857
|
+
.boff-explore-body .boff-explore-table td {
|
|
8858
|
+
border: 0;
|
|
8859
|
+
border-top: 1px solid hsl(var(--border));
|
|
8860
|
+
padding: 0.5rem 0.75rem;
|
|
8861
|
+
/* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
|
|
8862
|
+
table there reads against the grain of every other line on the page. */
|
|
8863
|
+
text-align: start;
|
|
8864
|
+
vertical-align: top;
|
|
8865
|
+
overflow-wrap: anywhere;
|
|
8866
|
+
/* Prices in a column line up under each other instead of drifting by digit width. */
|
|
8867
|
+
font-variant-numeric: tabular-nums;
|
|
8868
|
+
min-width: 6rem;
|
|
8869
|
+
}
|
|
8870
|
+
.boff-explore-body .boff-explore-table thead th {
|
|
8871
|
+
border-top: 0;
|
|
8872
|
+
background-color: hsl(var(--muted));
|
|
8873
|
+
color: hsl(var(--foreground));
|
|
8874
|
+
font-weight: 600;
|
|
8875
|
+
white-space: nowrap;
|
|
8876
|
+
}
|
|
8877
|
+
/* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
|
|
8878
|
+
the row's heading is what makes a price list scannable rather than a grid of strings. */
|
|
8879
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
8880
|
+
font-weight: 600;
|
|
8881
|
+
color: hsl(var(--card-foreground));
|
|
8882
|
+
}
|
|
8883
|
+
|
|
8884
|
+
/* Narrow: one card per row. Each cell prints its own column heading, so a value is never
|
|
8885
|
+
stranded without the question it answers, and the subject cell becomes the card's title.
|
|
8886
|
+
A media query rather than a measured width \u2014 these pages are serialised post-hydration,
|
|
8887
|
+
so anything measured here would ship to every phone as static HTML. */
|
|
8888
|
+
@media (max-width: 30rem) {
|
|
8889
|
+
.boff-explore-body .boff-explore-table {
|
|
8890
|
+
overflow-x: visible;
|
|
8891
|
+
}
|
|
8892
|
+
.boff-explore-body .boff-explore-table table,
|
|
8893
|
+
.boff-explore-body .boff-explore-table tbody,
|
|
8894
|
+
.boff-explore-body .boff-explore-table tr,
|
|
8895
|
+
.boff-explore-body .boff-explore-table td {
|
|
8896
|
+
display: block;
|
|
8897
|
+
width: 100%;
|
|
8898
|
+
min-width: 0;
|
|
8899
|
+
}
|
|
8900
|
+
.boff-explore-body .boff-explore-table thead {
|
|
8901
|
+
display: none;
|
|
8902
|
+
}
|
|
8903
|
+
.boff-explore-body .boff-explore-table tr {
|
|
8904
|
+
border-top: 1px solid hsl(var(--border));
|
|
8905
|
+
padding: 0.5rem 0;
|
|
8906
|
+
}
|
|
8907
|
+
.boff-explore-body .boff-explore-table tr:first-child {
|
|
8908
|
+
border-top: 0;
|
|
8909
|
+
}
|
|
8910
|
+
.boff-explore-body .boff-explore-table td {
|
|
8911
|
+
border: 0;
|
|
8912
|
+
padding: 0.125rem 0.75rem;
|
|
8913
|
+
}
|
|
8914
|
+
.boff-explore-body .boff-explore-table td[data-label]::before {
|
|
8915
|
+
content: attr(data-label);
|
|
8916
|
+
display: block;
|
|
8917
|
+
font-size: 0.6875rem;
|
|
8918
|
+
font-weight: 600;
|
|
8919
|
+
text-transform: uppercase;
|
|
8920
|
+
letter-spacing: 0.04em;
|
|
8921
|
+
color: hsl(var(--muted-foreground));
|
|
8922
|
+
}
|
|
8923
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
8924
|
+
font-size: 0.9375rem;
|
|
8925
|
+
padding-bottom: 0.25rem;
|
|
8926
|
+
}
|
|
8927
|
+
.boff-explore-body .boff-explore-table tbody td:first-child::before {
|
|
8928
|
+
/* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
|
|
8929
|
+
display: none;
|
|
8930
|
+
}
|
|
8931
|
+
}
|
|
8932
|
+
|
|
8305
8933
|
/* ---- Ambient backdrop -----------------------------------------------------------------
|
|
8306
8934
|
Two very low-opacity radial washes behind the welcome state, drifting slowly. Painted on
|
|
8307
8935
|
a pseudo-element with pointer-events:none so it can never intercept a click \u2014 a
|
|
@@ -8473,7 +9101,7 @@ function isOwnElement(el, root) {
|
|
|
8473
9101
|
function visualViewportHeight() {
|
|
8474
9102
|
return window.visualViewport?.height ?? window.innerHeight;
|
|
8475
9103
|
}
|
|
8476
|
-
function
|
|
9104
|
+
function clamp2(value, min, max) {
|
|
8477
9105
|
return Math.min(Math.max(value, min), max);
|
|
8478
9106
|
}
|
|
8479
9107
|
function isRestoredNavigation() {
|
|
@@ -8538,14 +9166,196 @@ function isInlineImageAllowed(src, origins) {
|
|
|
8538
9166
|
if (url.protocol !== "https:" || url.username || url.password) return false;
|
|
8539
9167
|
return origins.has(url.origin);
|
|
8540
9168
|
}
|
|
8541
|
-
var
|
|
9169
|
+
var REFERENCE_KIND_ORDER = [
|
|
9170
|
+
"CONCEPT",
|
|
9171
|
+
"DOCS",
|
|
9172
|
+
"PRICING",
|
|
9173
|
+
"USE_CASE",
|
|
9174
|
+
"PAGE"
|
|
9175
|
+
];
|
|
9176
|
+
var PRODUCT_SUBDOMAINS = [
|
|
9177
|
+
"alphadocs.",
|
|
9178
|
+
"alphaapp.",
|
|
9179
|
+
"docs.",
|
|
9180
|
+
"alpha.",
|
|
9181
|
+
"app.",
|
|
9182
|
+
"www."
|
|
9183
|
+
];
|
|
9184
|
+
function readCatalogUrl(raw) {
|
|
9185
|
+
let parsed;
|
|
9186
|
+
try {
|
|
9187
|
+
parsed = new URL(raw);
|
|
9188
|
+
} catch {
|
|
9189
|
+
return null;
|
|
9190
|
+
}
|
|
9191
|
+
if (parsed.protocol !== "https:") return null;
|
|
9192
|
+
const host = parsed.hostname.toLowerCase();
|
|
9193
|
+
const prefix = PRODUCT_SUBDOMAINS.find(
|
|
9194
|
+
(candidate) => host.startsWith(candidate)
|
|
9195
|
+
);
|
|
9196
|
+
const apex = prefix === void 0 ? host : host.slice(prefix.length);
|
|
9197
|
+
const port = parsed.port ? `:${parsed.port}` : "";
|
|
9198
|
+
return {
|
|
9199
|
+
origin: `https://${apex}${port}`,
|
|
9200
|
+
prefix,
|
|
9201
|
+
pathname: parsed.pathname
|
|
9202
|
+
};
|
|
9203
|
+
}
|
|
9204
|
+
function referenceKindOf(url, origins) {
|
|
9205
|
+
const parsed = readCatalogUrl(url);
|
|
9206
|
+
if (parsed === null || !origins.has(parsed.origin)) return null;
|
|
9207
|
+
if (parsed.prefix === "docs." || parsed.prefix === "alphadocs.")
|
|
9208
|
+
return "DOCS";
|
|
9209
|
+
const segment = parsed.pathname.toLowerCase().split("/").filter(Boolean)[0] ?? "";
|
|
9210
|
+
if (segment === "concept" || segment === "concepts") return "CONCEPT";
|
|
9211
|
+
if (segment === "docs") return "DOCS";
|
|
9212
|
+
if (segment === "pricing" || segment === "plans") return "PRICING";
|
|
9213
|
+
if (segment === "use-case" || segment === "use-cases" || segment === "solutions") {
|
|
9214
|
+
return "USE_CASE";
|
|
9215
|
+
}
|
|
9216
|
+
return "PAGE";
|
|
9217
|
+
}
|
|
9218
|
+
function productNamesByOrigin(products) {
|
|
9219
|
+
const names = /* @__PURE__ */ new Map();
|
|
9220
|
+
for (const product of products) {
|
|
9221
|
+
let url;
|
|
9222
|
+
try {
|
|
9223
|
+
url = new URL(product.website);
|
|
9224
|
+
} catch {
|
|
9225
|
+
continue;
|
|
9226
|
+
}
|
|
9227
|
+
if (url.protocol !== "https:") continue;
|
|
9228
|
+
const apex = url.hostname.replace(/^www\./, "");
|
|
9229
|
+
const port = url.port ? `:${url.port}` : "";
|
|
9230
|
+
const key = `https://${apex}${port}`;
|
|
9231
|
+
if (!names.has(key)) names.set(key, product.name);
|
|
9232
|
+
}
|
|
9233
|
+
return names;
|
|
9234
|
+
}
|
|
9235
|
+
function summariseReferences(references, origins, productNames) {
|
|
9236
|
+
const counts = /* @__PURE__ */ new Map();
|
|
9237
|
+
const products = [];
|
|
9238
|
+
for (const reference of references) {
|
|
9239
|
+
const kind = referenceKindOf(reference.url, origins);
|
|
9240
|
+
if (kind !== null) counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
9241
|
+
const parsed = readCatalogUrl(reference.url);
|
|
9242
|
+
const name = (parsed === null ? void 0 : productNames.get(parsed.origin)) ?? (reference.product || null);
|
|
9243
|
+
if (name != null && !products.includes(name)) products.push(name);
|
|
9244
|
+
}
|
|
9245
|
+
return {
|
|
9246
|
+
total: references.length,
|
|
9247
|
+
kinds: REFERENCE_KIND_ORDER.filter((kind) => counts.has(kind)).map(
|
|
9248
|
+
(kind) => ({ kind, count: counts.get(kind) ?? 0 })
|
|
9249
|
+
),
|
|
9250
|
+
products
|
|
9251
|
+
};
|
|
9252
|
+
}
|
|
9253
|
+
var CODE_FENCE = /^\s*(?:```|~~~)/;
|
|
9254
|
+
var TABLE_ROW_LINE = /^\s{0,3}\|/;
|
|
9255
|
+
var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
|
|
9256
|
+
function readableLine(raw, mode) {
|
|
9257
|
+
let line = raw;
|
|
9258
|
+
line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
|
|
9259
|
+
line = mode === "speech" ? (
|
|
9260
|
+
// A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
|
|
9261
|
+
// prompt asks for two to five links in every answer. The label IS the words.
|
|
9262
|
+
line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
|
|
9263
|
+
) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
|
|
9264
|
+
line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
|
|
9265
|
+
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
9266
|
+
line = line.replace(/^\s{0,3}>\s?/, "");
|
|
9267
|
+
if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
|
|
9268
|
+
line = line.replace(/`([^`]+)`/g, "$1");
|
|
9269
|
+
line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
9270
|
+
line = line.replace(/__([^_]+)__/g, "$1");
|
|
9271
|
+
line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
|
|
9272
|
+
line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
|
|
9273
|
+
return line.replace(/[ \t]+$/, "");
|
|
9274
|
+
}
|
|
9275
|
+
function tableCells(row) {
|
|
9276
|
+
const trimmed = row.trim();
|
|
9277
|
+
const cells = [];
|
|
9278
|
+
let cell = "";
|
|
9279
|
+
for (let at = 0; at < trimmed.length; at += 1) {
|
|
9280
|
+
const char = trimmed[at] ?? "";
|
|
9281
|
+
if (char === "\\") {
|
|
9282
|
+
cell += char + (trimmed[at + 1] ?? "");
|
|
9283
|
+
at += 1;
|
|
9284
|
+
continue;
|
|
9285
|
+
}
|
|
9286
|
+
if (char === "|") {
|
|
9287
|
+
cells.push(cell.trim());
|
|
9288
|
+
cell = "";
|
|
9289
|
+
continue;
|
|
9290
|
+
}
|
|
9291
|
+
cell += char;
|
|
9292
|
+
}
|
|
9293
|
+
cells.push(cell.trim());
|
|
9294
|
+
if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
|
|
9295
|
+
if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
|
|
9296
|
+
return cells;
|
|
9297
|
+
}
|
|
9298
|
+
function asSentence(text) {
|
|
9299
|
+
return /[.!?]$/.test(text) ? text : `${text}.`;
|
|
9300
|
+
}
|
|
9301
|
+
function spokenTable(rows) {
|
|
9302
|
+
if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
|
|
9303
|
+
const labels = tableCells(rows[0] ?? "");
|
|
9304
|
+
const spoken = [];
|
|
9305
|
+
for (const row of rows.slice(2)) {
|
|
9306
|
+
const parts = [];
|
|
9307
|
+
tableCells(row).forEach((value, column) => {
|
|
9308
|
+
if (value === "") return;
|
|
9309
|
+
const label = column === 0 ? "" : labels[column] ?? "";
|
|
9310
|
+
parts.push(label === "" ? value : `${label}: ${value}`);
|
|
9311
|
+
});
|
|
9312
|
+
if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
|
|
9313
|
+
}
|
|
9314
|
+
return spoken;
|
|
9315
|
+
}
|
|
9316
|
+
function readableText(markdown, mode) {
|
|
9317
|
+
const lines = [];
|
|
9318
|
+
let inFence = false;
|
|
9319
|
+
let table = [];
|
|
9320
|
+
const flushTable = () => {
|
|
9321
|
+
if (table.length === 0) return;
|
|
9322
|
+
for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
|
|
9323
|
+
table = [];
|
|
9324
|
+
};
|
|
9325
|
+
for (const raw of markdown.split("\n")) {
|
|
9326
|
+
if (CODE_FENCE.test(raw)) {
|
|
9327
|
+
flushTable();
|
|
9328
|
+
inFence = !inFence;
|
|
9329
|
+
continue;
|
|
9330
|
+
}
|
|
9331
|
+
if (inFence) {
|
|
9332
|
+
lines.push(raw);
|
|
9333
|
+
continue;
|
|
9334
|
+
}
|
|
9335
|
+
if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
|
|
9336
|
+
table.push(raw);
|
|
9337
|
+
continue;
|
|
9338
|
+
}
|
|
9339
|
+
flushTable();
|
|
9340
|
+
lines.push(readableLine(raw, mode));
|
|
9341
|
+
}
|
|
9342
|
+
flushTable();
|
|
9343
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
9344
|
+
}
|
|
9345
|
+
function answerPlainText(markdown) {
|
|
9346
|
+
return readableText(markdown, "clipboard");
|
|
9347
|
+
}
|
|
9348
|
+
function answerSpeechText(markdown) {
|
|
9349
|
+
return readableText(markdown, "speech");
|
|
9350
|
+
}
|
|
9351
|
+
var AnswerScopeContext = React.createContext(null);
|
|
8542
9352
|
var InsideLinkContext = React.createContext(false);
|
|
8543
9353
|
function AnswerLink(props) {
|
|
8544
9354
|
return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
|
|
8545
9355
|
}
|
|
8546
9356
|
var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
|
|
8547
9357
|
function InlineConceptImage({ src, alt }) {
|
|
8548
|
-
const scope = React.useContext(
|
|
9358
|
+
const scope = React.useContext(AnswerScopeContext);
|
|
8549
9359
|
const insideLink = React.useContext(InsideLinkContext);
|
|
8550
9360
|
if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
|
|
8551
9361
|
return null;
|
|
@@ -8593,9 +9403,70 @@ function InlineConceptImage({ src, alt }) {
|
|
|
8593
9403
|
}
|
|
8594
9404
|
) });
|
|
8595
9405
|
}
|
|
9406
|
+
function markdownText(node) {
|
|
9407
|
+
if (!node) return "";
|
|
9408
|
+
if (typeof node.value === "string") return node.value;
|
|
9409
|
+
return (node.children ?? []).map(markdownText).join("");
|
|
9410
|
+
}
|
|
9411
|
+
function hasHeaderCells(node) {
|
|
9412
|
+
return (node?.children ?? []).some((cell) => cell.tagName === "th");
|
|
9413
|
+
}
|
|
9414
|
+
function findHeaderRow(node) {
|
|
9415
|
+
for (const child of node?.children ?? []) {
|
|
9416
|
+
if (child.tagName === "tr" && hasHeaderCells(child)) return child;
|
|
9417
|
+
const nested = findHeaderRow(child);
|
|
9418
|
+
if (nested !== null) return nested;
|
|
9419
|
+
}
|
|
9420
|
+
return null;
|
|
9421
|
+
}
|
|
9422
|
+
function tableColumnLabels(node) {
|
|
9423
|
+
const row = findHeaderRow(node);
|
|
9424
|
+
if (row === null) return [];
|
|
9425
|
+
return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
|
|
9426
|
+
}
|
|
9427
|
+
var TableColumnsContext = React.createContext([]);
|
|
9428
|
+
function AnswerTable({ node, children }) {
|
|
9429
|
+
const scope = React.useContext(AnswerScopeContext);
|
|
9430
|
+
const table = /* @__PURE__ */ jsxRuntime.jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsxRuntime.jsx("table", { children }) });
|
|
9431
|
+
if (!scope) return table;
|
|
9432
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9433
|
+
"div",
|
|
9434
|
+
{
|
|
9435
|
+
"data-boff-explore": "table",
|
|
9436
|
+
role: "region",
|
|
9437
|
+
"aria-label": scope.strings.answerTable,
|
|
9438
|
+
tabIndex: 0,
|
|
9439
|
+
className: "boff-explore-table",
|
|
9440
|
+
children: table
|
|
9441
|
+
}
|
|
9442
|
+
);
|
|
9443
|
+
}
|
|
9444
|
+
function AnswerTableRow({
|
|
9445
|
+
node,
|
|
9446
|
+
children,
|
|
9447
|
+
...props
|
|
9448
|
+
}) {
|
|
9449
|
+
const labels = React.useContext(TableColumnsContext);
|
|
9450
|
+
if (labels.length === 0 || hasHeaderCells(node)) {
|
|
9451
|
+
return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children });
|
|
9452
|
+
}
|
|
9453
|
+
let column = 0;
|
|
9454
|
+
const cells = React.Children.map(children, (cell) => {
|
|
9455
|
+
if (!React.isValidElement(cell)) return cell;
|
|
9456
|
+
const label = labels[column] ?? "";
|
|
9457
|
+
column += 1;
|
|
9458
|
+
if (label === "") return cell;
|
|
9459
|
+
return React.cloneElement(cell, {
|
|
9460
|
+
"data-label": label
|
|
9461
|
+
});
|
|
9462
|
+
});
|
|
9463
|
+
return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
|
|
9464
|
+
}
|
|
8596
9465
|
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
8597
9466
|
a: AnswerLink,
|
|
8598
|
-
img: InlineConceptImage
|
|
9467
|
+
img: InlineConceptImage,
|
|
9468
|
+
table: AnswerTable,
|
|
9469
|
+
tr: AnswerTableRow
|
|
8599
9470
|
};
|
|
8600
9471
|
function ReferenceCard({
|
|
8601
9472
|
reference,
|
|
@@ -8935,6 +9806,148 @@ function AnswerFeedback({
|
|
|
8935
9806
|
}
|
|
8936
9807
|
);
|
|
8937
9808
|
}
|
|
9809
|
+
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";
|
|
9810
|
+
function ProvenanceChips({
|
|
9811
|
+
strings: T,
|
|
9812
|
+
provenance
|
|
9813
|
+
}) {
|
|
9814
|
+
if (provenance.total === 0) return null;
|
|
9815
|
+
const kindLabels = {
|
|
9816
|
+
CONCEPT: T.sourceKindConcept,
|
|
9817
|
+
DOCS: T.sourceKindDocs,
|
|
9818
|
+
PRICING: T.sourceKindPricing,
|
|
9819
|
+
USE_CASE: T.sourceKindUseCase,
|
|
9820
|
+
PAGE: T.sourceKindPage
|
|
9821
|
+
};
|
|
9822
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9823
|
+
"ul",
|
|
9824
|
+
{
|
|
9825
|
+
"data-boff-explore": "provenance",
|
|
9826
|
+
"aria-label": T.groundedIn,
|
|
9827
|
+
className: "flex flex-wrap items-center gap-1.5",
|
|
9828
|
+
children: [
|
|
9829
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { "data-boff-explore": "provenance-count", className: PROVENANCE_CHIP, children: [
|
|
9830
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpenText, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
9831
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: provenance.total === 1 ? T.sourceCountOne : fill(T.sourceCountMany, { count: String(provenance.total) }) })
|
|
9832
|
+
] }),
|
|
9833
|
+
provenance.kinds.map(({ kind, count }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
9834
|
+
"li",
|
|
9835
|
+
{
|
|
9836
|
+
"data-boff-explore": "provenance-kind",
|
|
9837
|
+
"data-kind": kind,
|
|
9838
|
+
"data-count": count,
|
|
9839
|
+
className: PROVENANCE_CHIP,
|
|
9840
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: count > 1 ? `${kindLabels[kind]} \xB7 ${count}` : kindLabels[kind] })
|
|
9841
|
+
},
|
|
9842
|
+
kind
|
|
9843
|
+
)),
|
|
9844
|
+
provenance.products.map((name) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
9845
|
+
"li",
|
|
9846
|
+
{
|
|
9847
|
+
"data-boff-explore": "provenance-product",
|
|
9848
|
+
className: PROVENANCE_CHIP,
|
|
9849
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: name })
|
|
9850
|
+
},
|
|
9851
|
+
name
|
|
9852
|
+
))
|
|
9853
|
+
]
|
|
9854
|
+
}
|
|
9855
|
+
);
|
|
9856
|
+
}
|
|
9857
|
+
var COPY_FEEDBACK_MS = 2500;
|
|
9858
|
+
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";
|
|
9859
|
+
var ANSWER_ACTION_IDLE = "border-border bg-card text-muted-foreground hover:border-primary/40 hover:bg-accent hover:text-foreground";
|
|
9860
|
+
var ANSWER_ACTION_ACTIVE = "border-primary/40 bg-accent text-primary";
|
|
9861
|
+
function ReadAloudButton({
|
|
9862
|
+
strings: T,
|
|
9863
|
+
text,
|
|
9864
|
+
lang
|
|
9865
|
+
}) {
|
|
9866
|
+
const speech = useSpeechOutput({ lang });
|
|
9867
|
+
if (!speech.supported) return null;
|
|
9868
|
+
const label = speech.speaking ? T.stopReading : T.readAloud;
|
|
9869
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9870
|
+
"button",
|
|
9871
|
+
{
|
|
9872
|
+
"data-boff-explore": "read-aloud",
|
|
9873
|
+
"data-speaking": speech.speaking ? "true" : "false",
|
|
9874
|
+
type: "button",
|
|
9875
|
+
title: label,
|
|
9876
|
+
"aria-pressed": speech.speaking,
|
|
9877
|
+
onClick: () => speech.toggle(text),
|
|
9878
|
+
className: cn(
|
|
9879
|
+
ANSWER_ACTION_PILL,
|
|
9880
|
+
speech.speaking ? ANSWER_ACTION_ACTIVE : ANSWER_ACTION_IDLE
|
|
9881
|
+
),
|
|
9882
|
+
children: [
|
|
9883
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Volume2, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
|
|
9884
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label })
|
|
9885
|
+
]
|
|
9886
|
+
}
|
|
9887
|
+
);
|
|
9888
|
+
}
|
|
9889
|
+
function AnswerActions({
|
|
9890
|
+
strings: T,
|
|
9891
|
+
text,
|
|
9892
|
+
speechText,
|
|
9893
|
+
speechLang
|
|
9894
|
+
}) {
|
|
9895
|
+
const [outcome, setOutcome] = React.useState("idle");
|
|
9896
|
+
const resetRef = React.useRef(null);
|
|
9897
|
+
React.useEffect(
|
|
9898
|
+
() => () => {
|
|
9899
|
+
if (resetRef.current !== null) clearTimeout(resetRef.current);
|
|
9900
|
+
},
|
|
9901
|
+
[]
|
|
9902
|
+
);
|
|
9903
|
+
const copy = React.useCallback(async () => {
|
|
9904
|
+
let copied = false;
|
|
9905
|
+
try {
|
|
9906
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
9907
|
+
if (typeof clipboard?.writeText === "function") {
|
|
9908
|
+
await clipboard.writeText(text);
|
|
9909
|
+
copied = true;
|
|
9910
|
+
}
|
|
9911
|
+
} catch {
|
|
9912
|
+
copied = false;
|
|
9913
|
+
}
|
|
9914
|
+
setOutcome(copied ? "copied" : "failed");
|
|
9915
|
+
if (resetRef.current !== null) clearTimeout(resetRef.current);
|
|
9916
|
+
resetRef.current = setTimeout(() => setOutcome("idle"), COPY_FEEDBACK_MS);
|
|
9917
|
+
}, [text]);
|
|
9918
|
+
const label = outcome === "copied" ? T.copied : outcome === "failed" ? T.copyFailed : T.copyAnswer;
|
|
9919
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9920
|
+
"div",
|
|
9921
|
+
{
|
|
9922
|
+
"data-boff-explore": "answer-actions",
|
|
9923
|
+
className: "flex flex-wrap items-center gap-2 pt-0.5",
|
|
9924
|
+
children: [
|
|
9925
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9926
|
+
"button",
|
|
9927
|
+
{
|
|
9928
|
+
"data-boff-explore": "copy-answer",
|
|
9929
|
+
"data-outcome": outcome,
|
|
9930
|
+
type: "button",
|
|
9931
|
+
title: label,
|
|
9932
|
+
onClick: () => {
|
|
9933
|
+
void copy();
|
|
9934
|
+
},
|
|
9935
|
+
className: cn(
|
|
9936
|
+
ANSWER_ACTION_PILL,
|
|
9937
|
+
outcome === "failed" ? "border-destructive/40 bg-card text-destructive" : ANSWER_ACTION_IDLE
|
|
9938
|
+
),
|
|
9939
|
+
children: [
|
|
9940
|
+
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" }),
|
|
9941
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label })
|
|
9942
|
+
]
|
|
9943
|
+
}
|
|
9944
|
+
),
|
|
9945
|
+
/* @__PURE__ */ jsxRuntime.jsx(ReadAloudButton, { strings: T, text: speechText, lang: speechLang }),
|
|
9946
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-boff-explore": "copy-status", role: "status", className: "sr-only", children: outcome === "idle" ? "" : label })
|
|
9947
|
+
]
|
|
9948
|
+
}
|
|
9949
|
+
);
|
|
9950
|
+
}
|
|
8938
9951
|
function UserBubble({
|
|
8939
9952
|
strings: T,
|
|
8940
9953
|
message
|
|
@@ -8980,6 +9993,8 @@ function AssistantBubble({
|
|
|
8980
9993
|
message,
|
|
8981
9994
|
activity,
|
|
8982
9995
|
imageOrigins,
|
|
9996
|
+
productNames,
|
|
9997
|
+
speechLang,
|
|
8983
9998
|
onNavigate,
|
|
8984
9999
|
anchorRef,
|
|
8985
10000
|
onFollow,
|
|
@@ -8990,6 +10005,7 @@ function AssistantBubble({
|
|
|
8990
10005
|
const body = message.content || message.partialContent || "";
|
|
8991
10006
|
const failed = message.status === "FAILED";
|
|
8992
10007
|
const completed = message.status === "COMPLETED";
|
|
10008
|
+
const visible = useProgressiveReveal(body, !streaming);
|
|
8993
10009
|
const [failedImages, setFailedImages] = React.useState(
|
|
8994
10010
|
() => /* @__PURE__ */ new Set()
|
|
8995
10011
|
);
|
|
@@ -8998,7 +10014,7 @@ function AssistantBubble({
|
|
|
8998
10014
|
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
8999
10015
|
);
|
|
9000
10016
|
}, []);
|
|
9001
|
-
const
|
|
10017
|
+
const answerScope = React.useMemo(
|
|
9002
10018
|
() => ({
|
|
9003
10019
|
strings: T,
|
|
9004
10020
|
enabled: completed,
|
|
@@ -9008,6 +10024,19 @@ function AssistantBubble({
|
|
|
9008
10024
|
}),
|
|
9009
10025
|
[T, completed, imageOrigins, failedImages, markImageFailed]
|
|
9010
10026
|
);
|
|
10027
|
+
const provenance = React.useMemo(
|
|
10028
|
+
() => summariseReferences(message.references, imageOrigins, productNames),
|
|
10029
|
+
[message.references, imageOrigins, productNames]
|
|
10030
|
+
);
|
|
10031
|
+
const copyText = React.useMemo(
|
|
10032
|
+
() => answerPlainText(message.content),
|
|
10033
|
+
[message.content]
|
|
10034
|
+
);
|
|
10035
|
+
const speechText = React.useMemo(
|
|
10036
|
+
() => answerSpeechText(message.content),
|
|
10037
|
+
[message.content]
|
|
10038
|
+
);
|
|
10039
|
+
const grounded = completed && message.answered !== false;
|
|
9011
10040
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9012
10041
|
"div",
|
|
9013
10042
|
{
|
|
@@ -9053,10 +10082,11 @@ function AssistantBubble({
|
|
|
9053
10082
|
]
|
|
9054
10083
|
}
|
|
9055
10084
|
) : null,
|
|
9056
|
-
|
|
9057
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10085
|
+
visible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
|
|
10086
|
+
/* @__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 }) }) }),
|
|
9058
10087
|
streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
|
|
9059
10088
|
] }) : null,
|
|
10089
|
+
grounded && provenance.total > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ProvenanceChips, { strings: T, provenance }) : null,
|
|
9060
10090
|
streaming ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingIndicator, { activity }) : null,
|
|
9061
10091
|
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,
|
|
9062
10092
|
message.references.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
@@ -9096,6 +10126,15 @@ function AssistantBubble({
|
|
|
9096
10126
|
question
|
|
9097
10127
|
)) })
|
|
9098
10128
|
] }) : null,
|
|
10129
|
+
completed && copyText !== "" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10130
|
+
AnswerActions,
|
|
10131
|
+
{
|
|
10132
|
+
strings: T,
|
|
10133
|
+
text: copyText,
|
|
10134
|
+
speechText,
|
|
10135
|
+
speechLang
|
|
10136
|
+
}
|
|
10137
|
+
) : null,
|
|
9099
10138
|
completed && onFeedback ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9100
10139
|
AnswerFeedback,
|
|
9101
10140
|
{
|
|
@@ -9308,6 +10347,96 @@ function ProductStrip({
|
|
|
9308
10347
|
}
|
|
9309
10348
|
);
|
|
9310
10349
|
}
|
|
10350
|
+
var MAX_CONCEPT_CARDS = 6;
|
|
10351
|
+
function ConceptStrip({
|
|
10352
|
+
strings: T,
|
|
10353
|
+
highlights,
|
|
10354
|
+
onAsk,
|
|
10355
|
+
disabled
|
|
10356
|
+
}) {
|
|
10357
|
+
const [failed, setFailed] = React.useState(() => /* @__PURE__ */ new Set());
|
|
10358
|
+
const markFailed = React.useCallback((src) => {
|
|
10359
|
+
setFailed(
|
|
10360
|
+
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
10361
|
+
);
|
|
10362
|
+
}, []);
|
|
10363
|
+
const visible = highlights.filter(
|
|
10364
|
+
(highlight) => highlight.imageUrl !== "" && !failed.has(highlight.imageUrl)
|
|
10365
|
+
).slice(0, MAX_CONCEPT_CARDS);
|
|
10366
|
+
if (visible.length === 0) return null;
|
|
10367
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10368
|
+
"div",
|
|
10369
|
+
{
|
|
10370
|
+
"data-boff-explore": "concept-strip-block",
|
|
10371
|
+
className: "boff-explore-rise boff-explore-rise-1 mt-6",
|
|
10372
|
+
children: [
|
|
10373
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: T.conceptStripTitle }),
|
|
10374
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10375
|
+
"ul",
|
|
10376
|
+
{
|
|
10377
|
+
"data-boff-explore": "concept-strip",
|
|
10378
|
+
"aria-label": T.conceptStripLabel,
|
|
10379
|
+
className: "boff-explore-strip flex min-w-0 snap-x gap-3 overflow-x-auto pb-1",
|
|
10380
|
+
children: visible.map((highlight) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
10381
|
+
"li",
|
|
10382
|
+
{
|
|
10383
|
+
className: "shrink-0 snap-start",
|
|
10384
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10385
|
+
"button",
|
|
10386
|
+
{
|
|
10387
|
+
"data-boff-explore": "concept-card",
|
|
10388
|
+
"data-product": highlight.product,
|
|
10389
|
+
type: "button",
|
|
10390
|
+
disabled,
|
|
10391
|
+
"aria-label": fill(T.conceptAsk, {
|
|
10392
|
+
title: highlight.title,
|
|
10393
|
+
product: highlight.productName
|
|
10394
|
+
}),
|
|
10395
|
+
onClick: () => onAsk(
|
|
10396
|
+
fill(T.conceptAskQuestion, {
|
|
10397
|
+
title: highlight.title,
|
|
10398
|
+
product: highlight.productName
|
|
10399
|
+
})
|
|
10400
|
+
),
|
|
10401
|
+
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",
|
|
10402
|
+
children: [
|
|
10403
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative block aspect-[16/9] w-full overflow-hidden bg-muted", children: [
|
|
10404
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10405
|
+
"img",
|
|
10406
|
+
{
|
|
10407
|
+
src: highlight.imageUrl,
|
|
10408
|
+
alt: highlight.alt,
|
|
10409
|
+
loading: "lazy",
|
|
10410
|
+
decoding: "async",
|
|
10411
|
+
onError: () => markFailed(highlight.imageUrl),
|
|
10412
|
+
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
10413
|
+
}
|
|
10414
|
+
),
|
|
10415
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10416
|
+
"span",
|
|
10417
|
+
{
|
|
10418
|
+
"aria-hidden": "true",
|
|
10419
|
+
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",
|
|
10420
|
+
children: T.conceptIllustration
|
|
10421
|
+
}
|
|
10422
|
+
)
|
|
10423
|
+
] }),
|
|
10424
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col gap-0.5 p-2.5", children: [
|
|
10425
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-xs font-semibold leading-snug text-card-foreground", children: highlight.title }),
|
|
10426
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: highlight.productName })
|
|
10427
|
+
] })
|
|
10428
|
+
]
|
|
10429
|
+
}
|
|
10430
|
+
)
|
|
10431
|
+
},
|
|
10432
|
+
`${highlight.product}:${highlight.url}`
|
|
10433
|
+
))
|
|
10434
|
+
}
|
|
10435
|
+
)
|
|
10436
|
+
]
|
|
10437
|
+
}
|
|
10438
|
+
);
|
|
10439
|
+
}
|
|
9311
10440
|
function DisclaimerDialog({
|
|
9312
10441
|
open,
|
|
9313
10442
|
onClose,
|
|
@@ -9414,6 +10543,7 @@ function WelcomeState({
|
|
|
9414
10543
|
body,
|
|
9415
10544
|
prompts,
|
|
9416
10545
|
products,
|
|
10546
|
+
conceptHighlights,
|
|
9417
10547
|
focusProduct,
|
|
9418
10548
|
onPrompt,
|
|
9419
10549
|
onFocus,
|
|
@@ -9428,6 +10558,15 @@ function WelcomeState({
|
|
|
9428
10558
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-4 text-balance break-words text-2xl font-bold tracking-tight sm:text-4xl", children: title }),
|
|
9429
10559
|
/* @__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 })
|
|
9430
10560
|
] }),
|
|
10561
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10562
|
+
ConceptStrip,
|
|
10563
|
+
{
|
|
10564
|
+
strings: T,
|
|
10565
|
+
highlights: conceptHighlights,
|
|
10566
|
+
onAsk: onPrompt,
|
|
10567
|
+
disabled
|
|
10568
|
+
}
|
|
10569
|
+
),
|
|
9431
10570
|
/* @__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(
|
|
9432
10571
|
"li",
|
|
9433
10572
|
{
|
|
@@ -9489,6 +10628,7 @@ function ExplorePage({
|
|
|
9489
10628
|
);
|
|
9490
10629
|
const T = React.useMemo(() => exploreStrings(activeLocale), [activeLocale]);
|
|
9491
10630
|
const rtl = isRtlLocale(activeLocale);
|
|
10631
|
+
const speechLang = speechLocaleOf(activeLocale);
|
|
9492
10632
|
const {
|
|
9493
10633
|
phase,
|
|
9494
10634
|
catalog,
|
|
@@ -9573,6 +10713,10 @@ function ExplorePage({
|
|
|
9573
10713
|
() => inlineImageOrigins(catalog.products),
|
|
9574
10714
|
[catalog.products]
|
|
9575
10715
|
);
|
|
10716
|
+
const productNames = React.useMemo(
|
|
10717
|
+
() => productNamesByOrigin(catalog.products),
|
|
10718
|
+
[catalog.products]
|
|
10719
|
+
);
|
|
9576
10720
|
React.useEffect(() => {
|
|
9577
10721
|
const el = textareaRef.current;
|
|
9578
10722
|
if (!el) return;
|
|
@@ -9589,11 +10733,11 @@ function ExplorePage({
|
|
|
9589
10733
|
const layoutHeight = window.innerHeight;
|
|
9590
10734
|
if (layoutHeight === 0) return;
|
|
9591
10735
|
const top = Math.round(
|
|
9592
|
-
|
|
10736
|
+
clamp2(el.getBoundingClientRect().top, 0, layoutHeight)
|
|
9593
10737
|
);
|
|
9594
10738
|
const viewport = window.visualViewport;
|
|
9595
10739
|
const inset = viewport ? Math.round(
|
|
9596
|
-
|
|
10740
|
+
clamp2(
|
|
9597
10741
|
layoutHeight - viewport.height - viewport.offsetTop,
|
|
9598
10742
|
0,
|
|
9599
10743
|
layoutHeight
|
|
@@ -9874,6 +11018,7 @@ function ExplorePage({
|
|
|
9874
11018
|
const text = draft.trim();
|
|
9875
11019
|
if (!text || overLimit || busy || !canSend) return;
|
|
9876
11020
|
stopDictation();
|
|
11021
|
+
stopSpeaking();
|
|
9877
11022
|
setDraft("");
|
|
9878
11023
|
stickToBottomRef.current = true;
|
|
9879
11024
|
void send(text, toPayload(stagedRef.current));
|
|
@@ -9884,6 +11029,7 @@ function ExplorePage({
|
|
|
9884
11029
|
const sendPrompt = React.useCallback(
|
|
9885
11030
|
(prompt) => {
|
|
9886
11031
|
if (!canSend || busy) return;
|
|
11032
|
+
stopSpeaking();
|
|
9887
11033
|
setDraft("");
|
|
9888
11034
|
stickToBottomRef.current = true;
|
|
9889
11035
|
void send(prompt, toPayload(stagedRef.current));
|
|
@@ -10104,6 +11250,7 @@ function ExplorePage({
|
|
|
10104
11250
|
body: resolvedBody,
|
|
10105
11251
|
prompts,
|
|
10106
11252
|
products: catalog.products,
|
|
11253
|
+
conceptHighlights: catalog.conceptHighlights,
|
|
10107
11254
|
focusProduct,
|
|
10108
11255
|
onPrompt: sendPrompt,
|
|
10109
11256
|
onFocus: setFocusProduct,
|
|
@@ -10117,6 +11264,8 @@ function ExplorePage({
|
|
|
10117
11264
|
message,
|
|
10118
11265
|
activity,
|
|
10119
11266
|
imageOrigins,
|
|
11267
|
+
productNames,
|
|
11268
|
+
speechLang,
|
|
10120
11269
|
onNavigate,
|
|
10121
11270
|
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
|
|
10122
11271
|
onFollow: (kind, url) => recordClick(message.id, kind, url),
|
|
@@ -15098,6 +16247,7 @@ exports.DEFAULT_CONTACT_CATEGORIES = DEFAULT_CONTACT_CATEGORIES;
|
|
|
15098
16247
|
exports.DEFAULT_EXPLORE_EXAMPLE_PROMPTS = DEFAULT_EXPLORE_EXAMPLE_PROMPTS;
|
|
15099
16248
|
exports.ECOSYSTEM_PRODUCTS = ECOSYSTEM_PRODUCTS;
|
|
15100
16249
|
exports.EXPLORE_CATALOG_QUERY = EXPLORE_CATALOG_QUERY;
|
|
16250
|
+
exports.EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS = EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS;
|
|
15101
16251
|
exports.EXPLORE_LOCALES = EXPLORE_LOCALES;
|
|
15102
16252
|
exports.EXPLORE_MESSAGE_QUERY = EXPLORE_MESSAGE_QUERY;
|
|
15103
16253
|
exports.EXPLORE_RECAPTCHA_ACTION = EXPLORE_RECAPTCHA_ACTION;
|
|
@@ -15150,14 +16300,18 @@ exports.getConceptBySlug = getConceptBySlug;
|
|
|
15150
16300
|
exports.getRecaptchaV3Token = getRecaptchaV3Token;
|
|
15151
16301
|
exports.isValidUrl = isValidUrl;
|
|
15152
16302
|
exports.loadRecaptchaV3 = loadRecaptchaV3;
|
|
16303
|
+
exports.speechLocaleOf = speechLocaleOf;
|
|
16304
|
+
exports.stopSpeaking = stopSpeaking;
|
|
15153
16305
|
exports.useContentPage = useContentPage;
|
|
15154
16306
|
exports.useContentPages = useContentPages;
|
|
15155
16307
|
exports.useExploreChat = useExploreChat;
|
|
15156
16308
|
exports.useProduct = useProduct;
|
|
15157
16309
|
exports.useProductConfig = useProductConfig;
|
|
16310
|
+
exports.useProgressiveReveal = useProgressiveReveal;
|
|
15158
16311
|
exports.usePublicJobPosting = usePublicJobPosting;
|
|
15159
16312
|
exports.usePublicJobPostings = usePublicJobPostings;
|
|
15160
16313
|
exports.usePublicJobPostingsForSubmission = usePublicJobPostingsForSubmission;
|
|
16314
|
+
exports.useSpeechOutput = useSpeechOutput;
|
|
15161
16315
|
exports.useWebSDK = useWebSDK;
|
|
15162
16316
|
exports.useWebSDKConfig = useWebSDKConfig;
|
|
15163
16317
|
//# sourceMappingURL=index.js.map
|