@burdenoff/website-sdk 2026.922.5 → 2026.923.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/explore.d.mts +4 -180
- package/dist/components/explore.d.ts +4 -180
- package/dist/components/explore.js +1458 -927
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +1225 -694
- package/dist/components/explore.mjs.map +1 -1
- package/dist/explore-D25zoXy9.d.mts +240 -0
- package/dist/explore-I4QnY4J9.d.ts +240 -0
- package/dist/{explore-types-DvQPiL1t.d.mts → explore-types-B5sdOC5M.d.mts} +75 -2
- package/dist/{explore-types-DvQPiL1t.d.ts → explore-types-B5sdOC5M.d.ts} +75 -2
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +125 -8
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +125 -8
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +417 -25
- package/dist/index.d.ts +417 -25
- package/dist/index.js +2629 -872
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2610 -874
- package/dist/index.mjs.map +1 -1
- package/dist/{use-progressive-reveal-Ha3gYPfs.d.mts → use-progressive-reveal-Cmy8bA7L.d.mts} +54 -7
- package/dist/{use-progressive-reveal-BfU_IHip.d.ts → use-progressive-reveal-DHPGGxZu.d.ts} +54 -7
- package/package.json +3 -2
|
@@ -6,11 +6,11 @@ var ReactMarkdown = require('react-markdown');
|
|
|
6
6
|
var rehypeSanitize = require('rehype-sanitize');
|
|
7
7
|
var remarkGfm = require('remark-gfm');
|
|
8
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
var clsx = require('clsx');
|
|
10
|
+
var tailwindMerge = require('tailwind-merge');
|
|
9
11
|
var reactHelmetAsync = require('react-helmet-async');
|
|
10
12
|
var reactSlot = require('@radix-ui/react-slot');
|
|
11
13
|
var classVarianceAuthority = require('class-variance-authority');
|
|
12
|
-
var clsx = require('clsx');
|
|
13
|
-
var tailwindMerge = require('tailwind-merge');
|
|
14
14
|
|
|
15
15
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
16
|
|
|
@@ -40,244 +40,752 @@ var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
|
40
40
|
var __defProp = Object.defineProperty;
|
|
41
41
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
42
42
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
function MarkdownLink({
|
|
44
|
+
href,
|
|
45
|
+
children,
|
|
46
|
+
// react-markdown passes the hast node to every override; it is not a DOM attribute.
|
|
47
|
+
node: _node,
|
|
48
|
+
...props
|
|
49
|
+
}) {
|
|
50
|
+
const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
|
|
51
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
52
|
+
"a",
|
|
53
|
+
{
|
|
54
|
+
href,
|
|
55
|
+
...props,
|
|
56
|
+
...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
|
|
57
|
+
children
|
|
58
|
+
}
|
|
59
|
+
);
|
|
49
60
|
}
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
welcomeTitle: "What problem are you trying to solve?",
|
|
53
|
-
welcomeBody: "Describe the problem you're facing, in your own words. You can also ask about a specific product or compare a few. Every answer comes straight from our documentation.",
|
|
54
|
-
tryAsking: "Try asking",
|
|
55
|
-
focusProduct: "Focus on a product",
|
|
56
|
-
allProducts: "All products",
|
|
57
|
-
showAll: "All",
|
|
58
|
-
showLess: "Show less",
|
|
59
|
-
sources: "Sources",
|
|
60
|
-
nextYouCouldAsk: "Next you could ask",
|
|
61
|
-
placeholder: "Describe the problem you're facing\u2026",
|
|
62
|
-
placeholderProduct: "Describe a problem, or ask about {product}\u2026",
|
|
63
|
-
send: "Send",
|
|
64
|
-
stop: "Stop",
|
|
65
|
-
newChat: "New chat",
|
|
66
|
-
history: "History",
|
|
67
|
-
clearHistory: "Clear history",
|
|
68
|
-
share: "Share",
|
|
69
|
-
copied: "Copied",
|
|
70
|
-
copying: "Copying\u2026",
|
|
71
|
-
shareUnavailable: "Unavailable",
|
|
72
|
-
disclaimer: "Disclaimer",
|
|
73
|
-
disclaimerShort: "Grounded in our docs \u2014 check anything important.",
|
|
74
|
-
dictate: "Dictate your question",
|
|
75
|
-
stopDictating: "Stop dictating",
|
|
76
|
-
attach: "Attach files",
|
|
77
|
-
attachHint: "Up to {{max}} files, {{size}} MB each",
|
|
78
|
-
removeAttachment: "Remove {{name}}",
|
|
79
|
-
attachmentNameOnly: "name and type only",
|
|
80
|
-
attachmentTooLarge: "{{name}} is larger than {{size}} MB and was not attached.",
|
|
81
|
-
attachmentTooMany: "Only {{max}} files can be attached, so {{name}} was skipped.",
|
|
82
|
-
noAnswer: "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
|
|
83
|
-
answerDidNotArrive: "That answer didn't come through. Try asking again.",
|
|
84
|
-
askHuman: "ask a human",
|
|
85
|
-
// An offer, not a demand: this row sits under every answer on 38 marketing sites, so the
|
|
86
|
-
// wording has to be shruggable. "Was this helpful?" invites; "Please rate this answer"
|
|
87
|
-
// conscripts.
|
|
88
|
-
feedbackPrompt: "Was this helpful?",
|
|
89
|
-
feedbackHelpful: "Helpful",
|
|
90
|
-
feedbackNotHelpful: "Not helpful",
|
|
91
|
-
feedbackReasonTitle: "What was off?",
|
|
92
|
-
feedbackReasonInaccurate: "Inaccurate",
|
|
93
|
-
feedbackReasonIncomplete: "Incomplete",
|
|
94
|
-
feedbackReasonOffTopic: "Off topic",
|
|
95
|
-
feedbackReasonHardToFollow: "Hard to follow",
|
|
96
|
-
feedbackReasonOther: "Something else",
|
|
97
|
-
feedbackCommentPlaceholder: "Add a detail (optional)",
|
|
98
|
-
feedbackSend: "Send",
|
|
99
|
-
feedbackDismiss: "Dismiss",
|
|
100
|
-
feedbackThanks: "Thanks \u2014 that helps.",
|
|
101
|
-
feedbackNotSent: "That didn't send.",
|
|
102
|
-
prevSuggestion: "Previous suggestion",
|
|
103
|
-
nextSuggestion: "Next suggestion",
|
|
104
|
-
conceptIllustration: "Concept illustration",
|
|
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",
|
|
123
|
-
seoTitle: "Describe a problem, get an answer",
|
|
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.",
|
|
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.",
|
|
126
|
-
capabilities: [
|
|
127
|
-
"Grounded in our own docs",
|
|
128
|
-
"Compare across the ecosystem",
|
|
129
|
-
"Straight to the right page"
|
|
130
|
-
],
|
|
131
|
-
prompts: [
|
|
132
|
-
"Every course we run lives in a different spreadsheet.",
|
|
133
|
-
"We only hear a machine is down after the line has stopped.",
|
|
134
|
-
"Members quietly stop showing up and we never know why.",
|
|
135
|
-
"Which of your products fits a problem like mine?"
|
|
136
|
-
]
|
|
137
|
-
};
|
|
138
|
-
var ZH = {
|
|
139
|
-
badge: "\u57FA\u4E8E\u6211\u4EEC\u6587\u6863\u7684 AI \u56DE\u7B54",
|
|
140
|
-
welcomeTitle: "\u4F60\u60F3\u89E3\u51B3\u4EC0\u4E48\u95EE\u9898\uFF1F",
|
|
141
|
-
welcomeBody: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u4F60\u9047\u5230\u7684\u95EE\u9898\u3002\u4F60\u4E5F\u53EF\u4EE5\u8BE2\u95EE\u67D0\u4E00\u6B3E\u4EA7\u54C1\uFF0C\u6216\u6BD4\u8F83\u51E0\u6B3E\u4EA7\u54C1\u3002\u6240\u6709\u56DE\u7B54\u90FD\u76F4\u63A5\u6765\u81EA\u6211\u4EEC\u7684\u6587\u6863\u3002",
|
|
142
|
-
tryAsking: "\u8BD5\u7740\u95EE",
|
|
143
|
-
focusProduct: "\u805A\u7126\u67D0\u4E2A\u4EA7\u54C1",
|
|
144
|
-
allProducts: "\u5168\u90E8\u4EA7\u54C1",
|
|
145
|
-
showAll: "\u5168\u90E8",
|
|
146
|
-
showLess: "\u6536\u8D77",
|
|
147
|
-
sources: "\u6765\u6E90",
|
|
148
|
-
nextYouCouldAsk: "\u4F60\u8FD8\u53EF\u4EE5\u95EE",
|
|
149
|
-
placeholder: "\u63CF\u8FF0\u4F60\u9047\u5230\u7684\u95EE\u9898\u2026",
|
|
150
|
-
placeholderProduct: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u6216\u8BE2\u95EE {product}\u2026",
|
|
151
|
-
send: "\u53D1\u9001",
|
|
152
|
-
stop: "\u505C\u6B62",
|
|
153
|
-
newChat: "\u65B0\u5BF9\u8BDD",
|
|
154
|
-
history: "\u5386\u53F2",
|
|
155
|
-
clearHistory: "\u6E05\u9664\u5386\u53F2",
|
|
156
|
-
share: "\u5206\u4EAB",
|
|
157
|
-
copied: "\u5DF2\u590D\u5236",
|
|
158
|
-
copying: "\u590D\u5236\u4E2D\u2026",
|
|
159
|
-
shareUnavailable: "\u4E0D\u53EF\u7528",
|
|
160
|
-
disclaimer: "\u514D\u8D23\u58F0\u660E",
|
|
161
|
-
disclaimerShort: "\u5185\u5BB9\u57FA\u4E8E\u6211\u4EEC\u7684\u6587\u6863\uFF0C\u91CD\u8981\u4FE1\u606F\u8BF7\u81EA\u884C\u6838\u5BF9\u3002",
|
|
162
|
-
dictate: "\u8BED\u97F3\u8F93\u5165",
|
|
163
|
-
stopDictating: "\u505C\u6B62\u8BED\u97F3\u8F93\u5165",
|
|
164
|
-
attach: "\u6DFB\u52A0\u6587\u4EF6",
|
|
165
|
-
attachHint: "\u6700\u591A {{max}} \u4E2A\u6587\u4EF6\uFF0C\u6BCF\u4E2A\u4E0D\u8D85\u8FC7 {{size}} MB",
|
|
166
|
-
removeAttachment: "\u79FB\u9664 {{name}}",
|
|
167
|
-
attachmentNameOnly: "\u4EC5\u6587\u4EF6\u540D\u548C\u7C7B\u578B",
|
|
168
|
-
attachmentTooLarge: "{{name}} \u8D85\u8FC7 {{size}} MB\uFF0C\u672A\u80FD\u6DFB\u52A0\u3002",
|
|
169
|
-
attachmentTooMany: "\u6700\u591A\u53EA\u80FD\u6DFB\u52A0 {{max}} \u4E2A\u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7 {{name}}\u3002",
|
|
170
|
-
noAnswer: "\u6211\u5728\u6587\u6863\u4E2D\u6CA1\u6709\u627E\u5230\u76F8\u5173\u5185\u5BB9\uFF0C\u4E0B\u9762\u7684\u56DE\u7B54\u6CA1\u6709\u6765\u6E90\u652F\u6301\u3002\u8BF7\u6362\u4E2A\u8BF4\u6CD5\uFF0C\u6216",
|
|
171
|
-
answerDidNotArrive: "\u8FD9\u6761\u56DE\u7B54\u6CA1\u6709\u8FD4\u56DE\u3002\u8BF7\u518D\u95EE\u4E00\u6B21\u3002",
|
|
172
|
-
askHuman: "\u8054\u7CFB\u6211\u4EEC",
|
|
173
|
-
feedbackPrompt: "\u8FD9\u4E2A\u56DE\u7B54\u6709\u5E2E\u52A9\u5417\uFF1F",
|
|
174
|
-
feedbackHelpful: "\u6709\u5E2E\u52A9",
|
|
175
|
-
feedbackNotHelpful: "\u6CA1\u6709\u5E2E\u52A9",
|
|
176
|
-
feedbackReasonTitle: "\u54EA\u91CC\u4E0D\u5BF9\uFF1F",
|
|
177
|
-
feedbackReasonInaccurate: "\u4E0D\u51C6\u786E",
|
|
178
|
-
feedbackReasonIncomplete: "\u4E0D\u5B8C\u6574",
|
|
179
|
-
feedbackReasonOffTopic: "\u7B54\u975E\u6240\u95EE",
|
|
180
|
-
feedbackReasonHardToFollow: "\u770B\u4E0D\u592A\u61C2",
|
|
181
|
-
feedbackReasonOther: "\u5176\u4ED6\u539F\u56E0",
|
|
182
|
-
feedbackCommentPlaceholder: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09",
|
|
183
|
-
feedbackSend: "\u63D0\u4EA4",
|
|
184
|
-
feedbackDismiss: "\u5173\u95ED",
|
|
185
|
-
feedbackThanks: "\u8C22\u8C22\uFF0C\u8FD9\u5BF9\u6211\u4EEC\u5F88\u6709\u5E2E\u52A9\u3002",
|
|
186
|
-
feedbackNotSent: "\u6CA1\u6709\u63D0\u4EA4\u6210\u529F\u3002",
|
|
187
|
-
prevSuggestion: "\u4E0A\u4E00\u4E2A\u5EFA\u8BAE",
|
|
188
|
-
nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
|
|
189
|
-
conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
|
|
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",
|
|
208
|
-
seoTitle: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u83B7\u5F97\u7B54\u6848",
|
|
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",
|
|
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",
|
|
211
|
-
capabilities: ["\u57FA\u4E8E\u6211\u4EEC\u81EA\u5DF1\u7684\u6587\u6863", "\u8DE8\u4EA7\u54C1\u5BF9\u6BD4", "\u76F4\u8FBE\u6B63\u786E\u7684\u9875\u9762"],
|
|
212
|
-
prompts: [
|
|
213
|
-
"\u6BCF\u95E8\u8BFE\u7A0B\u90FD\u8BB0\u5728\u4E0D\u540C\u7684\u8868\u683C\u91CC\u3002",
|
|
214
|
-
"\u673A\u5668\u505C\u4E86\uFF0C\u8981\u7B49\u6574\u6761\u7EBF\u505C\u4E0B\u6765\u624D\u77E5\u9053\u3002",
|
|
215
|
-
"\u4F1A\u5458\u6162\u6162\u5C31\u4E0D\u6765\u4E86\uFF0C\u6211\u4EEC\u4E5F\u4E0D\u77E5\u9053\u4E3A\u4EC0\u4E48\u3002",
|
|
216
|
-
"\u54EA\u4E2A\u4EA7\u54C1\u9002\u5408\u6211\u8FD9\u6837\u7684\u95EE\u9898\uFF1F"
|
|
217
|
-
]
|
|
61
|
+
var defaultComponents = {
|
|
62
|
+
a: MarkdownLink
|
|
218
63
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
64
|
+
function Markdown({
|
|
65
|
+
children,
|
|
66
|
+
className,
|
|
67
|
+
inline,
|
|
68
|
+
components
|
|
69
|
+
}) {
|
|
70
|
+
const content = /* @__PURE__ */ jsxRuntime.jsx(
|
|
71
|
+
ReactMarkdown__default.default,
|
|
72
|
+
{
|
|
73
|
+
remarkPlugins: [remarkGfm__default.default],
|
|
74
|
+
rehypePlugins: [rehypeSanitize__default.default],
|
|
75
|
+
components: { ...defaultComponents, ...components },
|
|
76
|
+
...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
|
|
77
|
+
children
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
if (inline) {
|
|
81
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: content });
|
|
82
|
+
}
|
|
83
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: content });
|
|
84
|
+
}
|
|
85
|
+
function cn(...inputs) {
|
|
86
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
87
|
+
}
|
|
88
|
+
var EXPLORE_ANSWER_CSS = `
|
|
89
|
+
/* Nothing inside an answer may widen the page. Model output carries long URLs, code and
|
|
90
|
+
tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
|
|
91
|
+
WHOLE document horizontally scrollable, not just the bubble. */
|
|
92
|
+
.boff-explore-body,
|
|
93
|
+
.boff-explore-body p,
|
|
94
|
+
.boff-explore-body li,
|
|
95
|
+
.boff-explore-body a,
|
|
96
|
+
.boff-explore-body h1,
|
|
97
|
+
.boff-explore-body h2,
|
|
98
|
+
.boff-explore-body h3,
|
|
99
|
+
/* h4-h6 as well: a published answer page pushes the model's own headings down a level or
|
|
100
|
+
two so they nest under the page's, and a long unbroken word is no narrower for it. */
|
|
101
|
+
.boff-explore-body h4,
|
|
102
|
+
.boff-explore-body h5,
|
|
103
|
+
.boff-explore-body h6 {
|
|
104
|
+
overflow-wrap: anywhere;
|
|
105
|
+
word-break: break-word;
|
|
106
|
+
}
|
|
107
|
+
.boff-explore-body pre {
|
|
108
|
+
overflow-x: auto;
|
|
109
|
+
max-width: 100%;
|
|
110
|
+
}
|
|
111
|
+
.boff-explore-body table {
|
|
112
|
+
display: block;
|
|
113
|
+
overflow-x: auto;
|
|
114
|
+
max-width: 100%;
|
|
115
|
+
}
|
|
116
|
+
.boff-explore-body img {
|
|
117
|
+
max-width: 100%;
|
|
118
|
+
height: auto;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* ---- Comparison and pricing tables ----------------------------------------------------
|
|
122
|
+
The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
|
|
123
|
+
the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
|
|
124
|
+
the page must not scroll sideways because of it, and it has to be readable on a 320px
|
|
125
|
+
phone, which a four-column grid of 1px borders is not.
|
|
126
|
+
|
|
127
|
+
Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
|
|
128
|
+
\`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
|
|
129
|
+
cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
|
|
130
|
+
of table from chaining to the page \u2014 the same rule the transcript needed. */
|
|
131
|
+
.boff-explore-body .boff-explore-table {
|
|
132
|
+
max-width: 100%;
|
|
133
|
+
overflow-x: auto;
|
|
134
|
+
overscroll-behavior-x: contain;
|
|
135
|
+
margin: 0.75rem 0;
|
|
136
|
+
border: 1px solid hsl(var(--border));
|
|
137
|
+
border-radius: 0.75rem;
|
|
138
|
+
background-color: hsl(var(--card));
|
|
139
|
+
}
|
|
140
|
+
.boff-explore-body .boff-explore-table:focus-visible {
|
|
141
|
+
outline: 2px solid hsl(var(--ring));
|
|
142
|
+
outline-offset: 2px;
|
|
143
|
+
}
|
|
144
|
+
.boff-explore-body .boff-explore-table table {
|
|
145
|
+
/* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
|
|
146
|
+
display: table;
|
|
147
|
+
width: 100%;
|
|
148
|
+
max-width: none;
|
|
149
|
+
margin: 0;
|
|
150
|
+
border-collapse: collapse;
|
|
151
|
+
font-size: 0.8125rem;
|
|
152
|
+
}
|
|
153
|
+
.boff-explore-body .boff-explore-table th,
|
|
154
|
+
.boff-explore-body .boff-explore-table td {
|
|
155
|
+
border: 0;
|
|
156
|
+
border-top: 1px solid hsl(var(--border));
|
|
157
|
+
padding: 0.5rem 0.75rem;
|
|
158
|
+
/* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
|
|
159
|
+
table there reads against the grain of every other line on the page. */
|
|
160
|
+
text-align: start;
|
|
161
|
+
vertical-align: top;
|
|
162
|
+
overflow-wrap: anywhere;
|
|
163
|
+
/* Prices in a column line up under each other instead of drifting by digit width. */
|
|
164
|
+
font-variant-numeric: tabular-nums;
|
|
165
|
+
min-width: 6rem;
|
|
166
|
+
}
|
|
167
|
+
.boff-explore-body .boff-explore-table thead th {
|
|
168
|
+
border-top: 0;
|
|
169
|
+
background-color: hsl(var(--muted));
|
|
170
|
+
color: hsl(var(--foreground));
|
|
171
|
+
font-weight: 600;
|
|
172
|
+
white-space: nowrap;
|
|
173
|
+
}
|
|
174
|
+
/* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
|
|
175
|
+
the row's heading is what makes a price list scannable rather than a grid of strings. */
|
|
176
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
color: hsl(var(--card-foreground));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* Narrow: one card per row. Each cell prints its own column heading, so a value is never
|
|
182
|
+
stranded without the question it answers, and the subject cell becomes the card's title.
|
|
183
|
+
A media query rather than a measured width \u2014 these pages are serialised post-hydration,
|
|
184
|
+
so anything measured here would ship to every phone as static HTML. */
|
|
185
|
+
@media (max-width: 30rem) {
|
|
186
|
+
.boff-explore-body .boff-explore-table {
|
|
187
|
+
overflow-x: visible;
|
|
188
|
+
}
|
|
189
|
+
.boff-explore-body .boff-explore-table table,
|
|
190
|
+
.boff-explore-body .boff-explore-table tbody,
|
|
191
|
+
.boff-explore-body .boff-explore-table tr,
|
|
192
|
+
.boff-explore-body .boff-explore-table td {
|
|
193
|
+
display: block;
|
|
194
|
+
width: 100%;
|
|
195
|
+
min-width: 0;
|
|
196
|
+
}
|
|
197
|
+
.boff-explore-body .boff-explore-table thead {
|
|
198
|
+
display: none;
|
|
199
|
+
}
|
|
200
|
+
.boff-explore-body .boff-explore-table tr {
|
|
201
|
+
border-top: 1px solid hsl(var(--border));
|
|
202
|
+
padding: 0.5rem 0;
|
|
203
|
+
}
|
|
204
|
+
.boff-explore-body .boff-explore-table tr:first-child {
|
|
205
|
+
border-top: 0;
|
|
206
|
+
}
|
|
207
|
+
.boff-explore-body .boff-explore-table td {
|
|
208
|
+
border: 0;
|
|
209
|
+
padding: 0.125rem 0.75rem;
|
|
210
|
+
}
|
|
211
|
+
.boff-explore-body .boff-explore-table td[data-label]::before {
|
|
212
|
+
content: attr(data-label);
|
|
213
|
+
display: block;
|
|
214
|
+
font-size: 0.6875rem;
|
|
215
|
+
font-weight: 600;
|
|
216
|
+
text-transform: uppercase;
|
|
217
|
+
letter-spacing: 0.04em;
|
|
218
|
+
color: hsl(var(--muted-foreground));
|
|
219
|
+
}
|
|
220
|
+
.boff-explore-body .boff-explore-table tbody td:first-child {
|
|
221
|
+
font-size: 0.9375rem;
|
|
222
|
+
padding-bottom: 0.25rem;
|
|
223
|
+
}
|
|
224
|
+
.boff-explore-body .boff-explore-table tbody td:first-child::before {
|
|
225
|
+
/* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
|
|
226
|
+
display: none;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
`;
|
|
230
|
+
function ExploreAnswerStyles() {
|
|
231
|
+
return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_ANSWER_CSS } });
|
|
232
|
+
}
|
|
233
|
+
function inlineImageOrigins(products) {
|
|
234
|
+
const origins = /* @__PURE__ */ new Set();
|
|
235
|
+
for (const product of products) {
|
|
236
|
+
let url;
|
|
237
|
+
try {
|
|
238
|
+
url = new URL(product.website);
|
|
239
|
+
} catch {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (url.protocol !== "https:") continue;
|
|
243
|
+
const apex = url.hostname.replace(/^www\./, "");
|
|
244
|
+
const port = url.port ? `:${url.port}` : "";
|
|
245
|
+
origins.add(`https://${apex}${port}`);
|
|
246
|
+
origins.add(`https://www.${apex}${port}`);
|
|
247
|
+
}
|
|
248
|
+
return origins;
|
|
249
|
+
}
|
|
250
|
+
function isInlineImageAllowed(src, origins) {
|
|
251
|
+
if (typeof src !== "string" || src === "") return false;
|
|
252
|
+
let url;
|
|
253
|
+
try {
|
|
254
|
+
url = new URL(src);
|
|
255
|
+
} catch {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
if (url.protocol !== "https:" || url.username || url.password) return false;
|
|
259
|
+
return origins.has(url.origin);
|
|
260
|
+
}
|
|
261
|
+
var CODE_FENCE = /^\s*(?:```|~~~)/;
|
|
262
|
+
var TABLE_ROW_LINE = /^\s{0,3}\|/;
|
|
263
|
+
var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
|
|
264
|
+
function readableLine(raw, mode) {
|
|
265
|
+
let line = raw;
|
|
266
|
+
line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
|
|
267
|
+
line = mode === "speech" ? (
|
|
268
|
+
// A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
|
|
269
|
+
// prompt asks for two to five links in every answer. The label IS the words.
|
|
270
|
+
line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
|
|
271
|
+
) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
|
|
272
|
+
line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
|
|
273
|
+
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
274
|
+
line = line.replace(/^\s{0,3}>\s?/, "");
|
|
275
|
+
if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
|
|
276
|
+
line = line.replace(/`([^`]+)`/g, "$1");
|
|
277
|
+
line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
278
|
+
line = line.replace(/__([^_]+)__/g, "$1");
|
|
279
|
+
line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
|
|
280
|
+
line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
|
|
281
|
+
return line.replace(/[ \t]+$/, "");
|
|
282
|
+
}
|
|
283
|
+
function tableCells(row) {
|
|
284
|
+
const trimmed = row.trim();
|
|
285
|
+
const cells = [];
|
|
286
|
+
let cell = "";
|
|
287
|
+
for (let at = 0; at < trimmed.length; at += 1) {
|
|
288
|
+
const char = trimmed[at] ?? "";
|
|
289
|
+
if (char === "\\") {
|
|
290
|
+
cell += char + (trimmed[at + 1] ?? "");
|
|
291
|
+
at += 1;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (char === "|") {
|
|
295
|
+
cells.push(cell.trim());
|
|
296
|
+
cell = "";
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
cell += char;
|
|
300
|
+
}
|
|
301
|
+
cells.push(cell.trim());
|
|
302
|
+
if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
|
|
303
|
+
if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
|
|
304
|
+
return cells;
|
|
305
|
+
}
|
|
306
|
+
function asSentence(text) {
|
|
307
|
+
return /[.!?]$/.test(text) ? text : `${text}.`;
|
|
308
|
+
}
|
|
309
|
+
function spokenTable(rows) {
|
|
310
|
+
if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
|
|
311
|
+
const labels = tableCells(rows[0] ?? "");
|
|
312
|
+
const spoken = [];
|
|
313
|
+
for (const row of rows.slice(2)) {
|
|
314
|
+
const parts = [];
|
|
315
|
+
tableCells(row).forEach((value, column) => {
|
|
316
|
+
if (value === "") return;
|
|
317
|
+
const label = column === 0 ? "" : labels[column] ?? "";
|
|
318
|
+
parts.push(label === "" ? value : `${label}: ${value}`);
|
|
319
|
+
});
|
|
320
|
+
if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
|
|
321
|
+
}
|
|
322
|
+
return spoken;
|
|
323
|
+
}
|
|
324
|
+
function readableText(markdown, mode) {
|
|
325
|
+
const lines = [];
|
|
326
|
+
let inFence = false;
|
|
327
|
+
let table = [];
|
|
328
|
+
const flushTable = () => {
|
|
329
|
+
if (table.length === 0) return;
|
|
330
|
+
for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
|
|
331
|
+
table = [];
|
|
332
|
+
};
|
|
333
|
+
for (const raw of markdown.split("\n")) {
|
|
334
|
+
if (CODE_FENCE.test(raw)) {
|
|
335
|
+
flushTable();
|
|
336
|
+
inFence = !inFence;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (inFence) {
|
|
340
|
+
lines.push(raw);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
|
|
344
|
+
table.push(raw);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
flushTable();
|
|
348
|
+
lines.push(readableLine(raw, mode));
|
|
349
|
+
}
|
|
350
|
+
flushTable();
|
|
351
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
352
|
+
}
|
|
353
|
+
function answerPlainText(markdown) {
|
|
354
|
+
return readableText(markdown, "clipboard");
|
|
355
|
+
}
|
|
356
|
+
function answerSpeechText(markdown) {
|
|
357
|
+
return readableText(markdown, "speech");
|
|
358
|
+
}
|
|
359
|
+
var AnswerScopeContext = React.createContext(null);
|
|
360
|
+
var InsideLinkContext = React.createContext(false);
|
|
361
|
+
function AnswerLink(props) {
|
|
362
|
+
return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
|
|
363
|
+
}
|
|
364
|
+
var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
|
|
365
|
+
function InlineConceptImage({ src, alt }) {
|
|
366
|
+
const scope = React.useContext(AnswerScopeContext);
|
|
367
|
+
const insideLink = React.useContext(InsideLinkContext);
|
|
368
|
+
if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
const { strings: T, onFailed } = scope;
|
|
372
|
+
const picture = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
373
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
374
|
+
"img",
|
|
375
|
+
{
|
|
376
|
+
src,
|
|
377
|
+
alt: alt ?? "",
|
|
378
|
+
loading: "lazy",
|
|
379
|
+
decoding: "async",
|
|
380
|
+
onError: () => onFailed(src),
|
|
381
|
+
className: "block h-auto max-h-80 w-auto max-w-full object-contain"
|
|
382
|
+
}
|
|
383
|
+
),
|
|
384
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
385
|
+
"span",
|
|
386
|
+
{
|
|
387
|
+
"aria-hidden": "true",
|
|
388
|
+
className: "pointer-events-none absolute left-2 top-2 rounded-full border border-border bg-background/90 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-foreground shadow-sm",
|
|
389
|
+
children: T.conceptIllustration
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
] });
|
|
393
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-boff-explore": "inline-image", className: "my-3 block", children: insideLink ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: INLINE_IMAGE_FRAME, children: picture }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
394
|
+
"a",
|
|
395
|
+
{
|
|
396
|
+
href: src,
|
|
397
|
+
target: "_blank",
|
|
398
|
+
rel: "noopener noreferrer",
|
|
399
|
+
className: cn(
|
|
400
|
+
INLINE_IMAGE_FRAME,
|
|
401
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
402
|
+
),
|
|
403
|
+
children: [
|
|
404
|
+
picture,
|
|
405
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
|
|
406
|
+
"(",
|
|
407
|
+
T.opensInNewTab,
|
|
408
|
+
")"
|
|
409
|
+
] })
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
) });
|
|
413
|
+
}
|
|
414
|
+
function markdownText(node) {
|
|
415
|
+
if (!node) return "";
|
|
416
|
+
if (typeof node.value === "string") return node.value;
|
|
417
|
+
return (node.children ?? []).map(markdownText).join("");
|
|
418
|
+
}
|
|
419
|
+
function hasHeaderCells(node) {
|
|
420
|
+
return (node?.children ?? []).some((cell) => cell.tagName === "th");
|
|
421
|
+
}
|
|
422
|
+
function findHeaderRow(node) {
|
|
423
|
+
for (const child of node?.children ?? []) {
|
|
424
|
+
if (child.tagName === "tr" && hasHeaderCells(child)) return child;
|
|
425
|
+
const nested = findHeaderRow(child);
|
|
426
|
+
if (nested !== null) return nested;
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
function tableColumnLabels(node) {
|
|
431
|
+
const row = findHeaderRow(node);
|
|
432
|
+
if (row === null) return [];
|
|
433
|
+
return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
|
|
434
|
+
}
|
|
435
|
+
var TableColumnsContext = React.createContext([]);
|
|
436
|
+
function AnswerTable({ node, children }) {
|
|
437
|
+
const scope = React.useContext(AnswerScopeContext);
|
|
438
|
+
const table = /* @__PURE__ */ jsxRuntime.jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsxRuntime.jsx("table", { children }) });
|
|
439
|
+
if (!scope) return table;
|
|
440
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
441
|
+
"div",
|
|
442
|
+
{
|
|
443
|
+
"data-boff-explore": "table",
|
|
444
|
+
role: "region",
|
|
445
|
+
"aria-label": scope.strings.answerTable,
|
|
446
|
+
tabIndex: 0,
|
|
447
|
+
className: "boff-explore-table",
|
|
448
|
+
children: table
|
|
449
|
+
}
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
function AnswerTableRow({
|
|
453
|
+
node,
|
|
454
|
+
children,
|
|
455
|
+
...props
|
|
456
|
+
}) {
|
|
457
|
+
const labels = React.useContext(TableColumnsContext);
|
|
458
|
+
if (labels.length === 0 || hasHeaderCells(node)) {
|
|
459
|
+
return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children });
|
|
460
|
+
}
|
|
461
|
+
let column = 0;
|
|
462
|
+
const cells = React.Children.map(children, (cell) => {
|
|
463
|
+
if (!React.isValidElement(cell)) return cell;
|
|
464
|
+
const label = labels[column] ?? "";
|
|
465
|
+
column += 1;
|
|
466
|
+
if (label === "") return cell;
|
|
467
|
+
return React.cloneElement(cell, {
|
|
468
|
+
"data-label": label
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
|
|
472
|
+
}
|
|
473
|
+
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
474
|
+
a: AnswerLink,
|
|
475
|
+
img: InlineConceptImage,
|
|
476
|
+
table: AnswerTable,
|
|
477
|
+
tr: AnswerTableRow
|
|
478
|
+
};
|
|
479
|
+
function ExploreAnswerBody({
|
|
480
|
+
children,
|
|
481
|
+
strings,
|
|
482
|
+
imageOrigins,
|
|
483
|
+
enabled = true,
|
|
484
|
+
components,
|
|
485
|
+
className
|
|
486
|
+
}) {
|
|
487
|
+
const [failed, setFailed] = React.useState(() => /* @__PURE__ */ new Set());
|
|
488
|
+
const onFailed = React.useCallback((src) => {
|
|
489
|
+
setFailed(
|
|
490
|
+
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
491
|
+
);
|
|
492
|
+
}, []);
|
|
493
|
+
const scope = React.useMemo(
|
|
494
|
+
() => ({
|
|
495
|
+
strings,
|
|
496
|
+
enabled,
|
|
497
|
+
origins: imageOrigins,
|
|
498
|
+
failed,
|
|
499
|
+
onFailed
|
|
500
|
+
}),
|
|
501
|
+
[strings, enabled, imageOrigins, failed, onFailed]
|
|
502
|
+
);
|
|
503
|
+
const overrides = React.useMemo(
|
|
504
|
+
() => components ? { ...ANSWER_MARKDOWN_COMPONENTS, ...components } : ANSWER_MARKDOWN_COMPONENTS,
|
|
505
|
+
[components]
|
|
506
|
+
);
|
|
507
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsxRuntime.jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { components: overrides, children }) }) });
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/components/explore-i18n.ts
|
|
511
|
+
var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
|
|
512
|
+
var RTL_LOCALES = /* @__PURE__ */ new Set(["ar"]);
|
|
513
|
+
function isRtlLocale(locale) {
|
|
514
|
+
return RTL_LOCALES.has(locale);
|
|
515
|
+
}
|
|
516
|
+
var EN = {
|
|
517
|
+
badge: "AI answers, grounded in our documentation",
|
|
518
|
+
welcomeTitle: "What problem are you trying to solve?",
|
|
519
|
+
welcomeBody: "Describe the problem you're facing, in your own words. You can also ask about a specific product or compare a few. Every answer comes straight from our documentation.",
|
|
520
|
+
tryAsking: "Try asking",
|
|
521
|
+
focusProduct: "Focus on a product",
|
|
522
|
+
allProducts: "All products",
|
|
523
|
+
showAll: "All",
|
|
524
|
+
showLess: "Show less",
|
|
525
|
+
sources: "Sources",
|
|
526
|
+
nextYouCouldAsk: "Next you could ask",
|
|
527
|
+
placeholder: "Describe the problem\u2026",
|
|
528
|
+
placeholderProduct: "Describe a problem, or ask about {product}\u2026",
|
|
529
|
+
send: "Send",
|
|
530
|
+
stop: "Stop",
|
|
531
|
+
newChat: "New chat",
|
|
532
|
+
history: "History",
|
|
533
|
+
clearHistory: "Clear history",
|
|
534
|
+
share: "Share",
|
|
535
|
+
copied: "Copied",
|
|
536
|
+
copying: "Copying\u2026",
|
|
537
|
+
shareUnavailable: "Unavailable",
|
|
538
|
+
disclaimer: "Disclaimer",
|
|
539
|
+
disclaimerShort: "Grounded in our docs \u2014 check anything important.",
|
|
540
|
+
dictate: "Dictate your question",
|
|
541
|
+
stopDictating: "Stop dictating",
|
|
542
|
+
attach: "Attach files",
|
|
543
|
+
attachHint: "Up to {{max}} files, {{size}} MB each",
|
|
544
|
+
removeAttachment: "Remove {{name}}",
|
|
545
|
+
attachmentNameOnly: "name and type only",
|
|
546
|
+
attachmentTooLarge: "{{name}} is larger than {{size}} MB and was not attached.",
|
|
547
|
+
attachmentTooMany: "Only {{max}} files can be attached, so {{name}} was skipped.",
|
|
548
|
+
noAnswer: "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
|
|
549
|
+
answerDidNotArrive: "That answer didn't come through. Try asking again.",
|
|
550
|
+
askHuman: "ask a human",
|
|
551
|
+
// An offer, not a demand: this row sits under every answer on 38 marketing sites, so the
|
|
552
|
+
// wording has to be shruggable. "Was this helpful?" invites; "Please rate this answer"
|
|
553
|
+
// conscripts.
|
|
554
|
+
feedbackPrompt: "Was this helpful?",
|
|
555
|
+
feedbackHelpful: "Helpful",
|
|
556
|
+
feedbackNotHelpful: "Not helpful",
|
|
557
|
+
feedbackReasonTitle: "What was off?",
|
|
558
|
+
feedbackReasonInaccurate: "Inaccurate",
|
|
559
|
+
feedbackReasonIncomplete: "Incomplete",
|
|
560
|
+
feedbackReasonOffTopic: "Off topic",
|
|
561
|
+
feedbackReasonHardToFollow: "Hard to follow",
|
|
562
|
+
feedbackReasonOther: "Something else",
|
|
563
|
+
feedbackCommentPlaceholder: "Add a detail (optional)",
|
|
564
|
+
feedbackSend: "Send",
|
|
565
|
+
feedbackDismiss: "Dismiss",
|
|
566
|
+
feedbackThanks: "Thanks \u2014 that helps.",
|
|
567
|
+
feedbackNotSent: "That didn't send.",
|
|
568
|
+
escalateOpen: "Talk to a person",
|
|
569
|
+
escalateTitle: "Send this conversation to a person",
|
|
570
|
+
escalateBody: "We'll pass the whole conversation on, so you don't have to type any of it again.",
|
|
571
|
+
escalateEmailLabel: "Your email address",
|
|
572
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
573
|
+
escalateNoteLabel: "Anything else a person should know? (optional)",
|
|
574
|
+
escalateSubmit: "Send to a person",
|
|
575
|
+
escalateSending: "Sending\u2026",
|
|
576
|
+
escalateCancel: "Cancel",
|
|
577
|
+
escalateInvalidEmail: "That doesn't look like an address we can reply to. Please check it.",
|
|
578
|
+
escalateSent: "A person now has this conversation. We'll reply to {email}.",
|
|
579
|
+
escalateRateLimited: "That's a lot of requests in a short time. Please try again in a few minutes.",
|
|
580
|
+
escalateCaptcha: "We couldn't verify that you're human. Please try that again.",
|
|
581
|
+
escalateFailed: "We couldn't pass that on just now. Please try again.",
|
|
582
|
+
prevSuggestion: "Previous suggestion",
|
|
583
|
+
nextSuggestion: "Next suggestion",
|
|
584
|
+
conceptIllustration: "Concept illustration",
|
|
585
|
+
opensInNewTab: "opens in a new tab",
|
|
586
|
+
conceptStripTitle: "Or start from a concept",
|
|
587
|
+
conceptStripLabel: "Concepts you can ask about",
|
|
588
|
+
conceptAsk: "Ask about {title} in {product}",
|
|
589
|
+
conceptAskQuestion: "How does {title} work in {product}?",
|
|
590
|
+
copyAnswer: "Copy answer",
|
|
591
|
+
copyFailed: "Couldn't copy",
|
|
592
|
+
readAloud: "Read aloud",
|
|
593
|
+
stopReading: "Stop reading",
|
|
594
|
+
answerTable: "Table",
|
|
595
|
+
groundedIn: "What this answer is based on",
|
|
596
|
+
sourceCountOne: "1 source",
|
|
597
|
+
sourceCountMany: "{count} sources",
|
|
598
|
+
sourceKindConcept: "Concept",
|
|
599
|
+
sourceKindDocs: "Documentation",
|
|
600
|
+
sourceKindPricing: "Pricing",
|
|
601
|
+
sourceKindUseCase: "Use case",
|
|
602
|
+
sourceKindPage: "Product page",
|
|
603
|
+
seoTitle: "Describe a problem, get an answer",
|
|
604
|
+
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.",
|
|
605
|
+
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.",
|
|
606
|
+
capabilities: [
|
|
607
|
+
"Grounded in our own docs",
|
|
608
|
+
"Compare across the ecosystem",
|
|
609
|
+
"Straight to the right page"
|
|
610
|
+
],
|
|
611
|
+
prompts: [
|
|
612
|
+
"Every course we run lives in a different spreadsheet.",
|
|
613
|
+
"We only hear a machine is down after the line has stopped.",
|
|
614
|
+
"Members quietly stop showing up and we never know why.",
|
|
615
|
+
"Which of your products fits a problem like mine?"
|
|
616
|
+
]
|
|
617
|
+
};
|
|
618
|
+
var ZH = {
|
|
619
|
+
badge: "\u57FA\u4E8E\u6211\u4EEC\u6587\u6863\u7684 AI \u56DE\u7B54",
|
|
620
|
+
welcomeTitle: "\u4F60\u60F3\u89E3\u51B3\u4EC0\u4E48\u95EE\u9898\uFF1F",
|
|
621
|
+
welcomeBody: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u4F60\u9047\u5230\u7684\u95EE\u9898\u3002\u4F60\u4E5F\u53EF\u4EE5\u8BE2\u95EE\u67D0\u4E00\u6B3E\u4EA7\u54C1\uFF0C\u6216\u6BD4\u8F83\u51E0\u6B3E\u4EA7\u54C1\u3002\u6240\u6709\u56DE\u7B54\u90FD\u76F4\u63A5\u6765\u81EA\u6211\u4EEC\u7684\u6587\u6863\u3002",
|
|
622
|
+
tryAsking: "\u8BD5\u7740\u95EE",
|
|
623
|
+
focusProduct: "\u805A\u7126\u67D0\u4E2A\u4EA7\u54C1",
|
|
624
|
+
allProducts: "\u5168\u90E8\u4EA7\u54C1",
|
|
625
|
+
showAll: "\u5168\u90E8",
|
|
626
|
+
showLess: "\u6536\u8D77",
|
|
627
|
+
sources: "\u6765\u6E90",
|
|
628
|
+
nextYouCouldAsk: "\u4F60\u8FD8\u53EF\u4EE5\u95EE",
|
|
629
|
+
placeholder: "\u63CF\u8FF0\u4F60\u7684\u95EE\u9898\u2026",
|
|
630
|
+
placeholderProduct: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u6216\u8BE2\u95EE {product}\u2026",
|
|
631
|
+
send: "\u53D1\u9001",
|
|
632
|
+
stop: "\u505C\u6B62",
|
|
633
|
+
newChat: "\u65B0\u5BF9\u8BDD",
|
|
634
|
+
history: "\u5386\u53F2",
|
|
635
|
+
clearHistory: "\u6E05\u9664\u5386\u53F2",
|
|
636
|
+
share: "\u5206\u4EAB",
|
|
637
|
+
copied: "\u5DF2\u590D\u5236",
|
|
638
|
+
copying: "\u590D\u5236\u4E2D\u2026",
|
|
639
|
+
shareUnavailable: "\u4E0D\u53EF\u7528",
|
|
640
|
+
disclaimer: "\u514D\u8D23\u58F0\u660E",
|
|
641
|
+
disclaimerShort: "\u5185\u5BB9\u57FA\u4E8E\u6211\u4EEC\u7684\u6587\u6863\uFF0C\u91CD\u8981\u4FE1\u606F\u8BF7\u81EA\u884C\u6838\u5BF9\u3002",
|
|
642
|
+
dictate: "\u8BED\u97F3\u8F93\u5165",
|
|
643
|
+
stopDictating: "\u505C\u6B62\u8BED\u97F3\u8F93\u5165",
|
|
644
|
+
attach: "\u6DFB\u52A0\u6587\u4EF6",
|
|
645
|
+
attachHint: "\u6700\u591A {{max}} \u4E2A\u6587\u4EF6\uFF0C\u6BCF\u4E2A\u4E0D\u8D85\u8FC7 {{size}} MB",
|
|
646
|
+
removeAttachment: "\u79FB\u9664 {{name}}",
|
|
647
|
+
attachmentNameOnly: "\u4EC5\u6587\u4EF6\u540D\u548C\u7C7B\u578B",
|
|
648
|
+
attachmentTooLarge: "{{name}} \u8D85\u8FC7 {{size}} MB\uFF0C\u672A\u80FD\u6DFB\u52A0\u3002",
|
|
649
|
+
attachmentTooMany: "\u6700\u591A\u53EA\u80FD\u6DFB\u52A0 {{max}} \u4E2A\u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7 {{name}}\u3002",
|
|
650
|
+
noAnswer: "\u6211\u5728\u6587\u6863\u4E2D\u6CA1\u6709\u627E\u5230\u76F8\u5173\u5185\u5BB9\uFF0C\u4E0B\u9762\u7684\u56DE\u7B54\u6CA1\u6709\u6765\u6E90\u652F\u6301\u3002\u8BF7\u6362\u4E2A\u8BF4\u6CD5\uFF0C\u6216",
|
|
651
|
+
answerDidNotArrive: "\u8FD9\u6761\u56DE\u7B54\u6CA1\u6709\u8FD4\u56DE\u3002\u8BF7\u518D\u95EE\u4E00\u6B21\u3002",
|
|
652
|
+
askHuman: "\u8054\u7CFB\u6211\u4EEC",
|
|
653
|
+
feedbackPrompt: "\u8FD9\u4E2A\u56DE\u7B54\u6709\u5E2E\u52A9\u5417\uFF1F",
|
|
654
|
+
feedbackHelpful: "\u6709\u5E2E\u52A9",
|
|
655
|
+
feedbackNotHelpful: "\u6CA1\u6709\u5E2E\u52A9",
|
|
656
|
+
feedbackReasonTitle: "\u54EA\u91CC\u4E0D\u5BF9\uFF1F",
|
|
657
|
+
feedbackReasonInaccurate: "\u4E0D\u51C6\u786E",
|
|
658
|
+
feedbackReasonIncomplete: "\u4E0D\u5B8C\u6574",
|
|
659
|
+
feedbackReasonOffTopic: "\u7B54\u975E\u6240\u95EE",
|
|
660
|
+
feedbackReasonHardToFollow: "\u770B\u4E0D\u592A\u61C2",
|
|
661
|
+
feedbackReasonOther: "\u5176\u4ED6\u539F\u56E0",
|
|
662
|
+
feedbackCommentPlaceholder: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09",
|
|
663
|
+
feedbackSend: "\u63D0\u4EA4",
|
|
664
|
+
feedbackDismiss: "\u5173\u95ED",
|
|
665
|
+
feedbackThanks: "\u8C22\u8C22\uFF0C\u8FD9\u5BF9\u6211\u4EEC\u5F88\u6709\u5E2E\u52A9\u3002",
|
|
666
|
+
feedbackNotSent: "\u6CA1\u6709\u63D0\u4EA4\u6210\u529F\u3002",
|
|
667
|
+
escalateOpen: "\u8054\u7CFB\u771F\u4EBA",
|
|
668
|
+
escalateTitle: "\u628A\u8FD9\u6BB5\u5BF9\u8BDD\u8F6C\u7ED9\u771F\u4EBA",
|
|
669
|
+
escalateBody: "\u6211\u4EEC\u4F1A\u628A\u6574\u6BB5\u5BF9\u8BDD\u4E00\u8D77\u8F6C\u8FC7\u53BB\uFF0C\u60A8\u4E0D\u7528\u518D\u91CD\u65B0\u6253\u4E00\u904D\u3002",
|
|
670
|
+
escalateEmailLabel: "\u60A8\u7684\u90AE\u7BB1\u5730\u5740",
|
|
671
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
672
|
+
escalateNoteLabel: "\u8FD8\u6709\u4EC0\u4E48\u9700\u8981\u544A\u8BC9\u6211\u4EEC\u7684\u5417\uFF1F\uFF08\u9009\u586B\uFF09",
|
|
673
|
+
escalateSubmit: "\u8F6C\u7ED9\u771F\u4EBA",
|
|
674
|
+
escalateSending: "\u6B63\u5728\u53D1\u9001\u2026",
|
|
675
|
+
escalateCancel: "\u53D6\u6D88",
|
|
676
|
+
escalateInvalidEmail: "\u8FD9\u4F3C\u4E4E\u4E0D\u662F\u4E00\u4E2A\u6211\u4EEC\u53EF\u4EE5\u56DE\u590D\u7684\u90AE\u7BB1\u5730\u5740\uFF0C\u8BF7\u68C0\u67E5\u4E00\u4E0B\u3002",
|
|
677
|
+
escalateSent: "\u5DF2\u6709\u540C\u4E8B\u63A5\u624B\u8FD9\u6BB5\u5BF9\u8BDD\uFF0C\u6211\u4EEC\u4F1A\u56DE\u590D\u5230 {email}\u3002",
|
|
678
|
+
escalateRateLimited: "\u77ED\u65F6\u95F4\u5185\u8BF7\u6C42\u8FC7\u591A\uFF0C\u8BF7\u51E0\u5206\u949F\u540E\u518D\u8BD5\u3002",
|
|
679
|
+
escalateCaptcha: "\u6211\u4EEC\u65E0\u6CD5\u9A8C\u8BC1\u60A8\u662F\u771F\u4EBA\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21\u3002",
|
|
680
|
+
escalateFailed: "\u6682\u65F6\u65E0\u6CD5\u8F6C\u4EA4\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21\u3002",
|
|
681
|
+
prevSuggestion: "\u4E0A\u4E00\u4E2A\u5EFA\u8BAE",
|
|
682
|
+
nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
|
|
683
|
+
conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
|
|
684
|
+
opensInNewTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00",
|
|
685
|
+
conceptStripTitle: "\u6216\u8005\u4ECE\u4E00\u4E2A\u6982\u5FF5\u5F00\u59CB",
|
|
686
|
+
conceptStripLabel: "\u4F60\u53EF\u4EE5\u8BE2\u95EE\u7684\u6982\u5FF5",
|
|
687
|
+
conceptAsk: "\u8BE2\u95EE {product} \u4E2D\u7684 {title}",
|
|
688
|
+
conceptAskQuestion: "{product} \u7684 {title} \u662F\u600E\u4E48\u8FD0\u4F5C\u7684\uFF1F",
|
|
689
|
+
copyAnswer: "\u590D\u5236\u56DE\u7B54",
|
|
690
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
691
|
+
readAloud: "\u6717\u8BFB",
|
|
692
|
+
stopReading: "\u505C\u6B62\u6717\u8BFB",
|
|
693
|
+
answerTable: "\u8868\u683C",
|
|
694
|
+
groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
|
|
695
|
+
sourceCountOne: "1 \u4E2A\u6765\u6E90",
|
|
696
|
+
sourceCountMany: "{count} \u4E2A\u6765\u6E90",
|
|
697
|
+
sourceKindConcept: "\u6982\u5FF5",
|
|
698
|
+
sourceKindDocs: "\u6587\u6863",
|
|
699
|
+
sourceKindPricing: "\u4EF7\u683C",
|
|
700
|
+
sourceKindUseCase: "\u5E94\u7528\u573A\u666F",
|
|
701
|
+
sourceKindPage: "\u4EA7\u54C1\u9875\u9762",
|
|
702
|
+
seoTitle: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u83B7\u5F97\u7B54\u6848",
|
|
703
|
+
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",
|
|
704
|
+
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",
|
|
705
|
+
capabilities: ["\u57FA\u4E8E\u6211\u4EEC\u81EA\u5DF1\u7684\u6587\u6863", "\u8DE8\u4EA7\u54C1\u5BF9\u6BD4", "\u76F4\u8FBE\u6B63\u786E\u7684\u9875\u9762"],
|
|
706
|
+
prompts: [
|
|
707
|
+
"\u6BCF\u95E8\u8BFE\u7A0B\u90FD\u8BB0\u5728\u4E0D\u540C\u7684\u8868\u683C\u91CC\u3002",
|
|
708
|
+
"\u673A\u5668\u505C\u4E86\uFF0C\u8981\u7B49\u6574\u6761\u7EBF\u505C\u4E0B\u6765\u624D\u77E5\u9053\u3002",
|
|
709
|
+
"\u4F1A\u5458\u6162\u6162\u5C31\u4E0D\u6765\u4E86\uFF0C\u6211\u4EEC\u4E5F\u4E0D\u77E5\u9053\u4E3A\u4EC0\u4E48\u3002",
|
|
710
|
+
"\u54EA\u4E2A\u4EA7\u54C1\u9002\u5408\u6211\u8FD9\u6837\u7684\u95EE\u9898\uFF1F"
|
|
711
|
+
]
|
|
712
|
+
};
|
|
713
|
+
var HI = {
|
|
714
|
+
badge: "\u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 AI \u0909\u0924\u094D\u0924\u0930",
|
|
715
|
+
welcomeTitle: "\u0906\u092A \u0915\u094C\u0928 \u0938\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u0939\u0932 \u0915\u0930\u0928\u093E \u091A\u093E\u0939\u0924\u0947 \u0939\u0948\u0902?",
|
|
716
|
+
welcomeBody: "\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\u0964 \u0906\u092A \u0915\u093F\u0938\u0940 \u090F\u0915 \u0909\u0924\u094D\u092A\u093E\u0926 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092D\u0940 \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902 \u092F\u093E \u0915\u0941\u091B \u0915\u0940 \u0924\u0941\u0932\u0928\u093E \u0915\u0930 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964 \u0939\u0930 \u0909\u0924\u094D\u0924\u0930 \u0938\u0940\u0927\u0947 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C \u0938\u0947 \u0906\u0924\u093E \u0939\u0948\u0964",
|
|
717
|
+
tryAsking: "\u092F\u0939 \u092A\u0942\u091B\u0915\u0930 \u0926\u0947\u0916\u0947\u0902",
|
|
718
|
+
focusProduct: "\u0915\u093F\u0938\u0940 \u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0930 \u0927\u094D\u092F\u093E\u0928 \u0926\u0947\u0902",
|
|
719
|
+
allProducts: "\u0938\u092D\u0940 \u0909\u0924\u094D\u092A\u093E\u0926",
|
|
720
|
+
showAll: "\u0938\u092D\u0940",
|
|
721
|
+
showLess: "\u0915\u092E \u0926\u093F\u0916\u093E\u090F\u0901",
|
|
722
|
+
sources: "\u0938\u094D\u0930\u094B\u0924",
|
|
723
|
+
nextYouCouldAsk: "\u0906\u0917\u0947 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
|
|
724
|
+
placeholder: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902\u2026",
|
|
725
|
+
placeholderProduct: "\u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902, \u092F\u093E {product} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902\u2026",
|
|
726
|
+
send: "\u092D\u0947\u091C\u0947\u0902",
|
|
727
|
+
stop: "\u0930\u094B\u0915\u0947\u0902",
|
|
728
|
+
newChat: "\u0928\u0908 \u092C\u093E\u0924\u091A\u0940\u0924",
|
|
729
|
+
history: "\u0907\u0924\u093F\u0939\u093E\u0938",
|
|
730
|
+
clearHistory: "\u0907\u0924\u093F\u0939\u093E\u0938 \u092E\u093F\u091F\u093E\u090F\u0901",
|
|
731
|
+
share: "\u0938\u093E\u091D\u093E \u0915\u0930\u0947\u0902",
|
|
732
|
+
copied: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0917\u092F\u093E",
|
|
733
|
+
copying: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
|
|
734
|
+
shareUnavailable: "\u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902",
|
|
735
|
+
disclaimer: "\u0905\u0938\u094D\u0935\u0940\u0915\u0930\u0923",
|
|
736
|
+
disclaimerShort: "\u092F\u0939 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948 \u2014 \u092E\u0939\u0924\u094D\u0935\u092A\u0942\u0930\u094D\u0923 \u092C\u093E\u0924\u0947\u0902 \u091C\u093C\u0930\u0942\u0930 \u091C\u093E\u0901\u091A \u0932\u0947\u0902\u0964",
|
|
737
|
+
dictate: "\u092C\u094B\u0932\u0915\u0930 \u092A\u0942\u091B\u0947\u0902",
|
|
738
|
+
stopDictating: "\u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
739
|
+
attach: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0947\u0902",
|
|
740
|
+
attachHint: "\u0905\u0927\u093F\u0915\u0924\u092E {{max}} \u092B\u093C\u093E\u0907\u0932\u0947\u0902, \u092A\u094D\u0930\u0924\u094D\u092F\u0947\u0915 {{size}} MB \u0924\u0915",
|
|
741
|
+
removeAttachment: "{{name}} \u0939\u091F\u093E\u090F\u0901",
|
|
742
|
+
attachmentNameOnly: "\u0915\u0947\u0935\u0932 \u0928\u093E\u092E \u0914\u0930 \u092A\u094D\u0930\u0915\u093E\u0930",
|
|
743
|
+
attachmentTooLarge: "{{name}} {{size}} MB \u0938\u0947 \u092C\u0921\u093C\u0940 \u0939\u0948, \u0907\u0938\u0932\u093F\u090F \u091C\u094B\u0921\u093C\u0940 \u0928\u0939\u0940\u0902 \u0917\u0908\u0964",
|
|
744
|
+
attachmentTooMany: "\u0915\u0947\u0935\u0932 {{max}} \u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0940 \u091C\u093E \u0938\u0915\u0924\u0940 \u0939\u0948\u0902, \u0907\u0938\u0932\u093F\u090F {{name}} \u091B\u094B\u0921\u093C \u0926\u0940 \u0917\u0908\u0964",
|
|
745
|
+
noAnswer: "\u092F\u0939 \u092E\u0941\u091D\u0947 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092E\u0947\u0902 \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E, \u0907\u0938\u0932\u093F\u090F \u0928\u0940\u091A\u0947 \u0926\u093F\u092F\u093E \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938\u0940 \u0938\u094D\u0930\u094B\u0924 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 \u0926\u0942\u0938\u0930\u0947 \u0936\u092C\u094D\u0926\u094B\u0902 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902, \u092F\u093E",
|
|
746
|
+
answerDidNotArrive: "\u0935\u0939 \u0909\u0924\u094D\u0924\u0930 \u0928\u0939\u0940\u0902 \u0906 \u0938\u0915\u093E\u0964 \u0915\u0943\u092A\u092F\u093E \u0926\u094B\u092C\u093E\u0930\u093E \u092A\u0942\u091B\u0947\u0902\u0964",
|
|
747
|
+
askHuman: "\u0939\u092E\u0938\u0947 \u0938\u0902\u092A\u0930\u094D\u0915 \u0915\u0930\u0947\u0902",
|
|
748
|
+
feedbackPrompt: "\u0915\u094D\u092F\u093E \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u092E\u0926\u0926\u0917\u093E\u0930 \u0925\u093E?",
|
|
749
|
+
feedbackHelpful: "\u092E\u0926\u0926\u0917\u093E\u0930",
|
|
750
|
+
feedbackNotHelpful: "\u092E\u0926\u0926\u0917\u093E\u0930 \u0928\u0939\u0940\u0902",
|
|
751
|
+
feedbackReasonTitle: "\u0915\u094D\u092F\u093E \u0920\u0940\u0915 \u0928\u0939\u0940\u0902 \u0925\u093E?",
|
|
752
|
+
feedbackReasonInaccurate: "\u0917\u093C\u0932\u0924 \u091C\u093E\u0928\u0915\u093E\u0930\u0940",
|
|
753
|
+
feedbackReasonIncomplete: "\u0905\u0927\u0942\u0930\u093E",
|
|
754
|
+
feedbackReasonOffTopic: "\u0935\u093F\u0937\u092F \u0938\u0947 \u0939\u091F\u0915\u0930",
|
|
755
|
+
feedbackReasonHardToFollow: "\u0938\u092E\u091D\u0928\u0947 \u092E\u0947\u0902 \u092E\u0941\u0936\u094D\u0915\u093F\u0932",
|
|
756
|
+
feedbackReasonOther: "\u0915\u0941\u091B \u0914\u0930",
|
|
757
|
+
feedbackCommentPlaceholder: "\u0915\u0941\u091B \u0914\u0930 \u092C\u0924\u093E\u090F\u0902 (\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915)",
|
|
758
|
+
feedbackSend: "\u092D\u0947\u091C\u0947\u0902",
|
|
759
|
+
feedbackDismiss: "\u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
760
|
+
feedbackThanks: "\u0927\u0928\u094D\u092F\u0935\u093E\u0926, \u0907\u0938\u0938\u0947 \u092E\u0926\u0926 \u092E\u093F\u0932\u0924\u0940 \u0939\u0948\u0964",
|
|
761
|
+
feedbackNotSent: "\u092F\u0939 \u092D\u0947\u091C\u093E \u0928\u0939\u0940\u0902 \u091C\u093E \u0938\u0915\u093E\u0964",
|
|
762
|
+
escalateOpen: "\u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0938\u0947 \u092C\u093E\u0924 \u0915\u0930\u0947\u0902",
|
|
763
|
+
escalateTitle: "\u092F\u0939 \u092C\u093E\u0924\u091A\u0940\u0924 \u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u094B \u092D\u0947\u091C\u0947\u0902",
|
|
764
|
+
escalateBody: "\u0939\u092E \u092A\u0942\u0930\u0940 \u092C\u093E\u0924\u091A\u0940\u0924 \u0906\u0917\u0947 \u092D\u0947\u091C \u0926\u0947\u0902\u0917\u0947, \u0924\u093E\u0915\u093F \u0906\u092A\u0915\u094B \u0926\u094B\u092C\u093E\u0930\u093E \u0915\u0941\u091B \u0932\u093F\u0916\u0928\u093E \u0928 \u092A\u0921\u093C\u0947\u0964",
|
|
765
|
+
escalateEmailLabel: "\u0906\u092A\u0915\u093E \u0908\u092E\u0947\u0932 \u092A\u0924\u093E",
|
|
766
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
767
|
+
escalateNoteLabel: "\u0915\u094D\u092F\u093E \u0915\u0941\u091B \u0914\u0930 \u092C\u0924\u093E\u0928\u093E \u091A\u093E\u0939\u0947\u0902\u0917\u0947? (\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915)",
|
|
768
|
+
escalateSubmit: "\u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u094B \u092D\u0947\u091C\u0947\u0902",
|
|
769
|
+
escalateSending: "\u092D\u0947\u091C\u093E \u091C\u093E \u0930\u0939\u093E \u0939\u0948\u2026",
|
|
770
|
+
escalateCancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
|
|
771
|
+
escalateInvalidEmail: "\u092F\u0939 \u0910\u0938\u093E \u0908\u092E\u0947\u0932 \u092A\u0924\u093E \u0928\u0939\u0940\u0902 \u0932\u0917\u0924\u093E \u091C\u093F\u0938 \u092A\u0930 \u0939\u092E \u091C\u0935\u093E\u092C \u092D\u0947\u091C \u0938\u0915\u0947\u0902\u0964 \u0915\u0943\u092A\u092F\u093E \u091C\u093E\u0901\u091A \u0932\u0947\u0902\u0964",
|
|
772
|
+
escalateSent: "\u0905\u092C \u092F\u0939 \u092C\u093E\u0924\u091A\u0940\u0924 \u090F\u0915 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u0947 \u092A\u093E\u0938 \u0939\u0948\u0964 \u0939\u092E {email} \u092A\u0930 \u091C\u0935\u093E\u092C \u0926\u0947\u0902\u0917\u0947\u0964",
|
|
773
|
+
escalateRateLimited: "\u0925\u094B\u0921\u093C\u0947 \u0938\u092E\u092F \u092E\u0947\u0902 \u092C\u0939\u0941\u0924 \u0938\u093E\u0930\u0947 \u0905\u0928\u0941\u0930\u094B\u0927 \u0939\u094B \u0917\u090F\u0964 \u0915\u0943\u092A\u092F\u093E \u0915\u0941\u091B \u092E\u093F\u0928\u091F \u092C\u093E\u0926 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
774
|
+
escalateCaptcha: "\u0939\u092E \u092F\u0939 \u0938\u0924\u094D\u092F\u093E\u092A\u093F\u0924 \u0928\u0939\u0940\u0902 \u0915\u0930 \u0938\u0915\u0947 \u0915\u093F \u0906\u092A \u090F\u0915 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0939\u0948\u0902\u0964 \u0915\u0943\u092A\u092F\u093E \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
775
|
+
escalateFailed: "\u0905\u092D\u0940 \u0907\u0938\u0947 \u0906\u0917\u0947 \u0928\u0939\u0940\u0902 \u092D\u0947\u091C\u093E \u091C\u093E \u0938\u0915\u093E\u0964 \u0915\u0943\u092A\u092F\u093E \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
776
|
+
prevSuggestion: "\u092A\u093F\u091B\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
|
|
777
|
+
nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
|
|
778
|
+
conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
|
|
779
|
+
opensInNewTab: "\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948",
|
|
780
|
+
conceptStripTitle: "\u092F\u093E \u0915\u093F\u0938\u0940 \u0905\u0935\u0927\u093E\u0930\u0923\u093E \u0938\u0947 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
|
|
781
|
+
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",
|
|
782
|
+
conceptAsk: "{product} \u092E\u0947\u0902 {title} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902",
|
|
783
|
+
conceptAskQuestion: "{product} \u092E\u0947\u0902 {title} \u0915\u0948\u0938\u0947 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948?",
|
|
784
|
+
copyAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
|
|
785
|
+
copyFailed: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E",
|
|
786
|
+
readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
|
|
787
|
+
stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
788
|
+
answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
|
|
281
789
|
groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
|
|
282
790
|
sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
|
|
283
791
|
sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
|
|
@@ -312,7 +820,7 @@ var ES = {
|
|
|
312
820
|
showLess: "Ver menos",
|
|
313
821
|
sources: "Fuentes",
|
|
314
822
|
nextYouCouldAsk: "Tambi\xE9n puedes preguntar",
|
|
315
|
-
placeholder: "Describe el problema
|
|
823
|
+
placeholder: "Describe el problema\u2026",
|
|
316
824
|
placeholderProduct: "Describe un problema o pregunta por {product}\u2026",
|
|
317
825
|
send: "Enviar",
|
|
318
826
|
stop: "Detener",
|
|
@@ -350,6 +858,20 @@ var ES = {
|
|
|
350
858
|
feedbackDismiss: "Cerrar",
|
|
351
859
|
feedbackThanks: "Gracias, nos sirve.",
|
|
352
860
|
feedbackNotSent: "No se ha podido enviar.",
|
|
861
|
+
escalateOpen: "Hablar con una persona",
|
|
862
|
+
escalateTitle: "Env\xEDa esta conversaci\xF3n a una persona",
|
|
863
|
+
escalateBody: "Le pasaremos la conversaci\xF3n entera, as\xED no tienes que volver a escribirla.",
|
|
864
|
+
escalateEmailLabel: "Tu direcci\xF3n de correo",
|
|
865
|
+
escalateEmailPlaceholder: "tu@ejemplo.com",
|
|
866
|
+
escalateNoteLabel: "\xBFAlgo m\xE1s que deber\xEDamos saber? (opcional)",
|
|
867
|
+
escalateSubmit: "Enviar a una persona",
|
|
868
|
+
escalateSending: "Enviando\u2026",
|
|
869
|
+
escalateCancel: "Cancelar",
|
|
870
|
+
escalateInvalidEmail: "No parece una direcci\xF3n a la que podamos responder. Rev\xEDsala, por favor.",
|
|
871
|
+
escalateSent: "Una persona ya tiene esta conversaci\xF3n. Te responderemos a {email}.",
|
|
872
|
+
escalateRateLimited: "Han sido muchas peticiones en poco tiempo. Int\xE9ntalo de nuevo en unos minutos.",
|
|
873
|
+
escalateCaptcha: "No hemos podido verificar que eres una persona. Int\xE9ntalo de nuevo.",
|
|
874
|
+
escalateFailed: "No hemos podido pasarlo ahora mismo. Int\xE9ntalo de nuevo.",
|
|
353
875
|
prevSuggestion: "Sugerencia anterior",
|
|
354
876
|
nextSuggestion: "Sugerencia siguiente",
|
|
355
877
|
conceptIllustration: "Ilustraci\xF3n del concepto",
|
|
@@ -397,7 +919,7 @@ var AR = {
|
|
|
397
919
|
showLess: "\u0639\u0631\u0636 \u0623\u0642\u0644",
|
|
398
920
|
sources: "\u0627\u0644\u0645\u0635\u0627\u062F\u0631",
|
|
399
921
|
nextYouCouldAsk: "\u064A\u0645\u0643\u0646\u0643 \u0623\u0646 \u062A\u0633\u0623\u0644 \u0628\u0639\u062F \u0630\u0644\u0643",
|
|
400
|
-
placeholder: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629
|
|
922
|
+
placeholder: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629\u2026",
|
|
401
923
|
placeholderProduct: "\u0635\u0641 \u0645\u0634\u0643\u0644\u062A\u0643 \u0623\u0648 \u0627\u0633\u0623\u0644 \u0639\u0646 {product}\u2026",
|
|
402
924
|
send: "\u0625\u0631\u0633\u0627\u0644",
|
|
403
925
|
stop: "\u0625\u064A\u0642\u0627\u0641",
|
|
@@ -435,6 +957,20 @@ var AR = {
|
|
|
435
957
|
feedbackDismiss: "\u0625\u063A\u0644\u0627\u0642",
|
|
436
958
|
feedbackThanks: "\u0634\u0643\u0631\u064B\u0627\u060C \u0647\u0630\u0627 \u064A\u0633\u0627\u0639\u062F\u0646\u0627.",
|
|
437
959
|
feedbackNotSent: "\u0644\u0645 \u064A\u062A\u0645 \u0627\u0644\u0625\u0631\u0633\u0627\u0644.",
|
|
960
|
+
escalateOpen: "\u062A\u0648\u0627\u0635\u0644 \u0645\u0639 \u0634\u062E\u0635",
|
|
961
|
+
escalateTitle: "\u0623\u0631\u0633\u0644 \u0647\u0630\u0647 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0625\u0644\u0649 \u0634\u062E\u0635",
|
|
962
|
+
escalateBody: "\u0633\u0646\u0645\u0631\u0651\u0631 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0643\u0627\u0645\u0644\u0629\u060C \u062D\u062A\u0649 \u0644\u0627 \u062A\u0636\u0637\u0631 \u0625\u0644\u0649 \u0643\u062A\u0627\u0628\u062A\u0647\u0627 \u0645\u0646 \u062C\u062F\u064A\u062F.",
|
|
963
|
+
escalateEmailLabel: "\u0628\u0631\u064A\u062F\u0643 \u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A",
|
|
964
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
965
|
+
escalateNoteLabel: "\u0647\u0644 \u0645\u0646 \u0634\u064A\u0621 \u0622\u062E\u0631 \u064A\u0646\u0628\u063A\u064A \u0623\u0646 \u0646\u0639\u0631\u0641\u0647\u061F (\u0627\u062E\u062A\u064A\u0627\u0631\u064A)",
|
|
966
|
+
escalateSubmit: "\u0625\u0631\u0633\u0627\u0644 \u0625\u0644\u0649 \u0634\u062E\u0635",
|
|
967
|
+
escalateSending: "\u062C\u0627\u0631\u064D \u0627\u0644\u0625\u0631\u0633\u0627\u0644\u2026",
|
|
968
|
+
escalateCancel: "\u0625\u0644\u063A\u0627\u0621",
|
|
969
|
+
escalateInvalidEmail: "\u0644\u0627 \u064A\u0628\u062F\u0648 \u0647\u0630\u0627 \u0639\u0646\u0648\u0627\u0646\u064B\u0627 \u064A\u0645\u0643\u0646\u0646\u0627 \u0627\u0644\u0631\u062F \u0639\u0644\u064A\u0647. \u064A\u0631\u062C\u0649 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646\u0647.",
|
|
970
|
+
escalateSent: "\u0623\u0635\u0628\u062D\u062A \u0647\u0630\u0647 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0644\u062F\u0649 \u0623\u062D\u062F \u0627\u0644\u0632\u0645\u0644\u0627\u0621. \u0633\u0646\u0631\u062F \u0639\u0644\u0649 {email}.",
|
|
971
|
+
escalateRateLimited: "\u0637\u0644\u0628\u0627\u062A \u0643\u062B\u064A\u0631\u0629 \u062E\u0644\u0627\u0644 \u0648\u0642\u062A \u0642\u0635\u064A\u0631. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0628\u0639\u062F \u0628\u0636\u0639 \u062F\u0642\u0627\u0626\u0642.",
|
|
972
|
+
escalateCaptcha: "\u062A\u0639\u0630\u0651\u0631 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u0623\u0646\u0643 \u0644\u0633\u062A \u0631\u0648\u0628\u0648\u062A\u064B\u0627. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
|
|
973
|
+
escalateFailed: "\u062A\u0639\u0630\u0651\u0631 \u062A\u0645\u0631\u064A\u0631 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0627\u0644\u0622\u0646. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
|
|
438
974
|
prevSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u0633\u0627\u0628\u0642",
|
|
439
975
|
nextSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u062A\u0627\u0644\u064A",
|
|
440
976
|
conceptIllustration: "\u0631\u0633\u0645 \u062A\u0648\u0636\u064A\u062D\u064A \u0644\u0644\u0645\u0641\u0647\u0648\u0645",
|
|
@@ -482,7 +1018,7 @@ var TA = {
|
|
|
482
1018
|
showLess: "\u0B95\u0BC1\u0BB1\u0BC8\u0BB5\u0BBE\u0B95\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1",
|
|
483
1019
|
sources: "\u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
484
1020
|
nextYouCouldAsk: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BC7\u0B9F\u0BCD\u0B95\u0BB2\u0BBE\u0BAE\u0BCD",
|
|
485
|
-
placeholder: "\
|
|
1021
|
+
placeholder: "\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
|
|
486
1022
|
placeholderProduct: "\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD, \u0B85\u0BB2\u0BCD\u0BB2\u0BA4\u0BC1 {product} \u0BAA\u0BB1\u0BCD\u0BB1\u0BBF\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
|
|
487
1023
|
send: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1",
|
|
488
1024
|
stop: "\u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
@@ -520,6 +1056,20 @@ var TA = {
|
|
|
520
1056
|
feedbackDismiss: "\u0BAE\u0BC2\u0B9F\u0BC1",
|
|
521
1057
|
feedbackThanks: "\u0BA8\u0BA9\u0BCD\u0BB1\u0BBF, \u0B87\u0BA4\u0BC1 \u0B8E\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B89\u0BA4\u0BB5\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1.",
|
|
522
1058
|
feedbackNotSent: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
|
|
1059
|
+
escalateOpen: "\u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BBF\u0B9F\u0BAE\u0BCD \u0BAA\u0BC7\u0B9A\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
1060
|
+
escalateTitle: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BC8 \u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
1061
|
+
escalateBody: "\u0BAE\u0BC1\u0BB4\u0BC1 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD \u0BA8\u0BBE\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BBF\u0BB5\u0BBF\u0B9F\u0BC1\u0BB5\u0BCB\u0BAE\u0BCD; \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BA4\u0B9F\u0BCD\u0B9F\u0B9A\u0BCD\u0B9A\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD\u0BAF \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BBF\u0BAF\u0BA4\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
|
|
1062
|
+
escalateEmailLabel: "\u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BBF\u0BA9\u0BCD\u0BA9\u0B9E\u0BCD\u0B9A\u0BB2\u0BCD \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF",
|
|
1063
|
+
escalateEmailPlaceholder: "you@example.com",
|
|
1064
|
+
escalateNoteLabel: "\u0BB5\u0BC7\u0BB1\u0BC1 \u0B8F\u0BA4\u0BC7\u0BA9\u0BC1\u0BAE\u0BCD \u0B9A\u0BCA\u0BB2\u0BCD\u0BB2 \u0BB5\u0BBF\u0BB0\u0BC1\u0BAE\u0BCD\u0BAA\u0BC1\u0B95\u0BBF\u0BB1\u0BC0\u0BB0\u0BCD\u0B95\u0BB3\u0BBE? (\u0BB5\u0BBF\u0BB0\u0BC1\u0BAA\u0BCD\u0BAA\u0BAE\u0BCD)",
|
|
1065
|
+
escalateSubmit: "\u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1",
|
|
1066
|
+
escalateSending: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
|
|
1067
|
+
escalateCancel: "\u0BB0\u0BA4\u0BCD\u0BA4\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD",
|
|
1068
|
+
escalateInvalidEmail: "\u0B87\u0BA4\u0BC1 \u0BA8\u0BBE\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BB3\u0BBF\u0B95\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0BAE\u0BBF\u0BA9\u0BCD\u0BA9\u0B9E\u0BCD\u0B9A\u0BB2\u0BCD \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF \u0BAA\u0BCB\u0BB2\u0BCD \u0BA4\u0BC6\u0BB0\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
1069
|
+
escalateSent: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BCD \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BBF\u0B9F\u0BAE\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1. {email} \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BB3\u0BBF\u0BAA\u0BCD\u0BAA\u0BCB\u0BAE\u0BCD.",
|
|
1070
|
+
escalateRateLimited: "\u0B95\u0BC1\u0BB1\u0BC1\u0B95\u0BBF\u0BAF \u0BA8\u0BC7\u0BB0\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BA4\u0BBF\u0B95 \u0B95\u0BCB\u0BB0\u0BBF\u0B95\u0BCD\u0B95\u0BC8\u0B95\u0BB3\u0BCD. \u0B9A\u0BBF\u0BB2 \u0BA8\u0BBF\u0BAE\u0BBF\u0B9F\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BB4\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1 \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
1071
|
+
escalateCaptcha: "\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BA9\u0BBF\u0BA4\u0BB0\u0BCD \u0B8E\u0BA9\u0BCD\u0BAA\u0BA4\u0BC8 \u0B89\u0BB1\u0BC1\u0BA4\u0BBF\u0B9A\u0BC6\u0BAF\u0BCD\u0BAF \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
1072
|
+
escalateFailed: "\u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
523
1073
|
prevSuggestion: "\u0BAE\u0BC1\u0BA8\u0BCD\u0BA4\u0BC8\u0BAF \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
|
|
524
1074
|
nextSuggestion: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
|
|
525
1075
|
conceptIllustration: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BB5\u0BBF\u0BB3\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BAE\u0BCD",
|
|
@@ -762,6 +1312,29 @@ var SUBMIT_EXPLORE_FEEDBACK_MUTATION = (
|
|
|
762
1312
|
`
|
|
763
1313
|
);
|
|
764
1314
|
var EXPLORE_FEEDBACK_COMMENT_SOFT_MAX = 500;
|
|
1315
|
+
var ESCALATE_EXPLORE_CONVERSATION_MUTATION = (
|
|
1316
|
+
/* GraphQL */
|
|
1317
|
+
`
|
|
1318
|
+
mutation EscalateExploreConversation(
|
|
1319
|
+
$input: EscalateExploreConversationInput!
|
|
1320
|
+
) {
|
|
1321
|
+
escalateExploreConversation(input: $input) {
|
|
1322
|
+
created
|
|
1323
|
+
escalation {
|
|
1324
|
+
email
|
|
1325
|
+
createdAt
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
`
|
|
1330
|
+
);
|
|
1331
|
+
var EXPLORE_ESCALATION_NOTE_SOFT_MAX = 2e3;
|
|
1332
|
+
var DELIVERABLE_EMAIL = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}$/;
|
|
1333
|
+
function looksLikeDeliverableEmail(value) {
|
|
1334
|
+
const trimmed = value.trim();
|
|
1335
|
+
if (trimmed.length === 0 || trimmed.length > 254) return false;
|
|
1336
|
+
return DELIVERABLE_EMAIL.test(trimmed);
|
|
1337
|
+
}
|
|
765
1338
|
var CREATE_EXPLORE_SHARE_LINK_MUTATION = (
|
|
766
1339
|
/* GraphQL */
|
|
767
1340
|
`
|
|
@@ -782,48 +1355,6 @@ var EXPLORE_SHARED_CONVERSATION_QUERY = (
|
|
|
782
1355
|
}
|
|
783
1356
|
`
|
|
784
1357
|
);
|
|
785
|
-
function MarkdownLink({
|
|
786
|
-
href,
|
|
787
|
-
children,
|
|
788
|
-
// react-markdown passes the hast node to every override; it is not a DOM attribute.
|
|
789
|
-
node: _node,
|
|
790
|
-
...props
|
|
791
|
-
}) {
|
|
792
|
-
const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
|
|
793
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
794
|
-
"a",
|
|
795
|
-
{
|
|
796
|
-
href,
|
|
797
|
-
...props,
|
|
798
|
-
...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
|
|
799
|
-
children
|
|
800
|
-
}
|
|
801
|
-
);
|
|
802
|
-
}
|
|
803
|
-
var defaultComponents = {
|
|
804
|
-
a: MarkdownLink
|
|
805
|
-
};
|
|
806
|
-
function Markdown({
|
|
807
|
-
children,
|
|
808
|
-
className,
|
|
809
|
-
inline,
|
|
810
|
-
components
|
|
811
|
-
}) {
|
|
812
|
-
const content = /* @__PURE__ */ jsxRuntime.jsx(
|
|
813
|
-
ReactMarkdown__default.default,
|
|
814
|
-
{
|
|
815
|
-
remarkPlugins: [remarkGfm__default.default],
|
|
816
|
-
rehypePlugins: [rehypeSanitize__default.default],
|
|
817
|
-
components: { ...defaultComponents, ...components },
|
|
818
|
-
...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
|
|
819
|
-
children
|
|
820
|
-
}
|
|
821
|
-
);
|
|
822
|
-
if (inline) {
|
|
823
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: content });
|
|
824
|
-
}
|
|
825
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: content });
|
|
826
|
-
}
|
|
827
1358
|
var PROSE_CSS = `
|
|
828
1359
|
.boff-prose {
|
|
829
1360
|
max-width: none;
|
|
@@ -1220,6 +1751,10 @@ function useWebSDK() {
|
|
|
1220
1751
|
}
|
|
1221
1752
|
return client;
|
|
1222
1753
|
}
|
|
1754
|
+
function useWebSDKConfig() {
|
|
1755
|
+
const client = useWebSDK();
|
|
1756
|
+
return client.getConfig();
|
|
1757
|
+
}
|
|
1223
1758
|
|
|
1224
1759
|
// src/data/products.ts
|
|
1225
1760
|
var ECOSYSTEM_PRODUCTS = [
|
|
@@ -1566,6 +2101,25 @@ var VALIDATION_CODES = /* @__PURE__ */ new Set([
|
|
|
1566
2101
|
"CONVERSATION_LIMIT",
|
|
1567
2102
|
"INVALID_INPUT"
|
|
1568
2103
|
]);
|
|
2104
|
+
function escalateOutcomeForCode(code) {
|
|
2105
|
+
switch (code) {
|
|
2106
|
+
case "INVALID_EMAIL":
|
|
2107
|
+
return "invalid-email";
|
|
2108
|
+
case "RATE_LIMITED":
|
|
2109
|
+
case "HTTP_429":
|
|
2110
|
+
return "rate-limited";
|
|
2111
|
+
case "CAPTCHA_REQUIRED":
|
|
2112
|
+
case "CAPTCHA_FAILED":
|
|
2113
|
+
return "captcha";
|
|
2114
|
+
// The token owns nothing, or owns a different conversation. The server answers NOT_FOUND
|
|
2115
|
+
// rather than FORBIDDEN so the endpoint is not an oracle for whether a thread exists;
|
|
2116
|
+
// either way there is nothing this visitor can do about it, so it reads as a failure.
|
|
2117
|
+
case "NOT_FOUND":
|
|
2118
|
+
return "failed";
|
|
2119
|
+
default:
|
|
2120
|
+
return "failed";
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
1569
2123
|
function classifyExploreError(code, serverMessage) {
|
|
1570
2124
|
const c = code ?? "UNKNOWN";
|
|
1571
2125
|
if (RATE_LIMIT_CODES.has(c)) {
|
|
@@ -1732,6 +2286,7 @@ function useExploreChat(options = {}) {
|
|
|
1732
2286
|
onSend
|
|
1733
2287
|
} = options;
|
|
1734
2288
|
const client = useWebSDK();
|
|
2289
|
+
const { productId } = useWebSDKConfig();
|
|
1735
2290
|
const promptKey = JSON.stringify(examplePrompts ?? []);
|
|
1736
2291
|
const welcomeKey = JSON.stringify(fallbackWelcome ?? null);
|
|
1737
2292
|
const fallbackCatalog = React.useMemo(
|
|
@@ -1755,6 +2310,7 @@ function useExploreChat(options = {}) {
|
|
|
1755
2310
|
const pollGenerationRef = React.useRef(0);
|
|
1756
2311
|
const pollTimerRef = React.useRef(null);
|
|
1757
2312
|
const conversationTokenRef = React.useRef(null);
|
|
2313
|
+
const [hasConversation, setHasConversation] = React.useState(false);
|
|
1758
2314
|
const shareTokenRef = React.useRef(void 0);
|
|
1759
2315
|
if (shareTokenRef.current === void 0) {
|
|
1760
2316
|
shareTokenRef.current = readShareToken();
|
|
@@ -1769,6 +2325,10 @@ function useExploreChat(options = {}) {
|
|
|
1769
2325
|
);
|
|
1770
2326
|
const inFlightRef = React.useRef(false);
|
|
1771
2327
|
catalogRef.current = catalog;
|
|
2328
|
+
const rememberConversationToken = React.useCallback((token) => {
|
|
2329
|
+
conversationTokenRef.current = token;
|
|
2330
|
+
setHasConversation(token !== null);
|
|
2331
|
+
}, []);
|
|
1772
2332
|
const clearPollTimer = React.useCallback(() => {
|
|
1773
2333
|
if (pollTimerRef.current !== null) {
|
|
1774
2334
|
clearTimeout(pollTimerRef.current);
|
|
@@ -1835,7 +2395,7 @@ function useExploreChat(options = {}) {
|
|
|
1835
2395
|
setArchive(readArchive(storageKey));
|
|
1836
2396
|
const stored = readPersisted(storageKey);
|
|
1837
2397
|
if (stored) {
|
|
1838
|
-
|
|
2398
|
+
rememberConversationToken(stored.conversationToken);
|
|
1839
2399
|
focusProductRef.current = stored.focusProduct;
|
|
1840
2400
|
const settled = stored.messages.flatMap((m) => {
|
|
1841
2401
|
if (m.role !== "ASSISTANT") return [m];
|
|
@@ -1851,7 +2411,7 @@ function useExploreChat(options = {}) {
|
|
|
1851
2411
|
setTurnCount(turns);
|
|
1852
2412
|
}
|
|
1853
2413
|
setHydrated(true);
|
|
1854
|
-
}, [persist, storageKey]);
|
|
2414
|
+
}, [persist, rememberConversationToken, storageKey]);
|
|
1855
2415
|
React.useEffect(() => {
|
|
1856
2416
|
if (!persist || !hydrated || typeof window === "undefined") return;
|
|
1857
2417
|
try {
|
|
@@ -2044,7 +2604,7 @@ function useExploreChat(options = {}) {
|
|
|
2044
2604
|
setPhase(classified.kind === "rate-limit" ? "limited" : "error");
|
|
2045
2605
|
return;
|
|
2046
2606
|
}
|
|
2047
|
-
|
|
2607
|
+
rememberConversationToken(result.conversation.token);
|
|
2048
2608
|
turnCountRef.current = result.conversation.turnCount;
|
|
2049
2609
|
setTurnCount(result.conversation.turnCount);
|
|
2050
2610
|
setRemainingToday(result.remainingToday ?? null);
|
|
@@ -2070,7 +2630,14 @@ function useExploreChat(options = {}) {
|
|
|
2070
2630
|
setPhase("streaming");
|
|
2071
2631
|
startPolling(result.conversation.token, assistantMessage.id);
|
|
2072
2632
|
},
|
|
2073
|
-
[
|
|
2633
|
+
[
|
|
2634
|
+
client,
|
|
2635
|
+
locale,
|
|
2636
|
+
pageUrl,
|
|
2637
|
+
productSlug,
|
|
2638
|
+
rememberConversationToken,
|
|
2639
|
+
startPolling
|
|
2640
|
+
]
|
|
2074
2641
|
);
|
|
2075
2642
|
const send = React.useCallback(
|
|
2076
2643
|
async (message, attachments = []) => {
|
|
@@ -2187,7 +2754,7 @@ function useExploreChat(options = {}) {
|
|
|
2187
2754
|
);
|
|
2188
2755
|
cancelPolling();
|
|
2189
2756
|
inFlightRef.current = false;
|
|
2190
|
-
|
|
2757
|
+
rememberConversationToken(null);
|
|
2191
2758
|
lastSentTextRef.current = null;
|
|
2192
2759
|
lastSentAttachmentsRef.current = [];
|
|
2193
2760
|
turnCountRef.current = 0;
|
|
@@ -2202,7 +2769,7 @@ function useExploreChat(options = {}) {
|
|
|
2202
2769
|
} catch {
|
|
2203
2770
|
}
|
|
2204
2771
|
}
|
|
2205
|
-
}, [cancelPolling, storageKey]);
|
|
2772
|
+
}, [cancelPolling, rememberConversationToken, storageKey]);
|
|
2206
2773
|
const openConversation = React.useCallback(
|
|
2207
2774
|
(token) => {
|
|
2208
2775
|
const entry = readArchive(storageKey).find((e) => e.token === token);
|
|
@@ -2214,7 +2781,7 @@ function useExploreChat(options = {}) {
|
|
|
2214
2781
|
});
|
|
2215
2782
|
cancelPolling();
|
|
2216
2783
|
inFlightRef.current = false;
|
|
2217
|
-
|
|
2784
|
+
rememberConversationToken(entry.token);
|
|
2218
2785
|
turnCountRef.current = entry.messages.filter(
|
|
2219
2786
|
(m) => m.role === "USER"
|
|
2220
2787
|
).length;
|
|
@@ -2226,7 +2793,7 @@ function useExploreChat(options = {}) {
|
|
|
2226
2793
|
setPhase(catalogRef.current.enabled ? "ready" : "disabled");
|
|
2227
2794
|
setArchive(readArchive(storageKey));
|
|
2228
2795
|
},
|
|
2229
|
-
[cancelPolling, storageKey]
|
|
2796
|
+
[cancelPolling, rememberConversationToken, storageKey]
|
|
2230
2797
|
);
|
|
2231
2798
|
const deleteConversation = React.useCallback(
|
|
2232
2799
|
(token) => {
|
|
@@ -2315,6 +2882,69 @@ function useExploreChat(options = {}) {
|
|
|
2315
2882
|
},
|
|
2316
2883
|
[client]
|
|
2317
2884
|
);
|
|
2885
|
+
const escalate = React.useCallback(
|
|
2886
|
+
async (email, note) => {
|
|
2887
|
+
const token = conversationTokenRef.current;
|
|
2888
|
+
if (!token) {
|
|
2889
|
+
return { outcome: "no-conversation", email: null, createdAt: null };
|
|
2890
|
+
}
|
|
2891
|
+
const attempt = async (captchaAttempt) => {
|
|
2892
|
+
const activeCatalog = catalogRef.current;
|
|
2893
|
+
let recaptchaToken;
|
|
2894
|
+
if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {
|
|
2895
|
+
try {
|
|
2896
|
+
recaptchaToken = await getRecaptchaV3Token(
|
|
2897
|
+
activeCatalog.recaptchaSiteKey
|
|
2898
|
+
);
|
|
2899
|
+
} catch (err) {
|
|
2900
|
+
console.warn(
|
|
2901
|
+
"[WebSDK] reCAPTCHA v3 token could not be minted",
|
|
2902
|
+
err
|
|
2903
|
+
);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
const input = {
|
|
2907
|
+
conversationToken: token,
|
|
2908
|
+
email: email.trim(),
|
|
2909
|
+
note: note?.trim() ? note.trim() : null,
|
|
2910
|
+
...recaptchaToken ? { recaptchaToken } : {},
|
|
2911
|
+
...productId ? { productId } : {}
|
|
2912
|
+
};
|
|
2913
|
+
try {
|
|
2914
|
+
const res = await client.mutate(
|
|
2915
|
+
ESCALATE_EXPLORE_CONVERSATION_MUTATION,
|
|
2916
|
+
{ input }
|
|
2917
|
+
);
|
|
2918
|
+
const first = res?.errors?.[0];
|
|
2919
|
+
const result = res?.data?.escalateExploreConversation;
|
|
2920
|
+
if (first || !result) {
|
|
2921
|
+
const code = typeof first?.extensions?.code === "string" ? first.extensions.code : "ESCALATION_FAILED";
|
|
2922
|
+
if (code === "CAPTCHA_FAILED" && captchaAttempt === 0) {
|
|
2923
|
+
return attempt(1);
|
|
2924
|
+
}
|
|
2925
|
+
return {
|
|
2926
|
+
outcome: escalateOutcomeForCode(code),
|
|
2927
|
+
email: null,
|
|
2928
|
+
createdAt: null
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
return {
|
|
2932
|
+
// `created: false` is the double-click, the retry, the reload. The handover
|
|
2933
|
+
// exists, so this is a success — and the address comes from the SERVER, because
|
|
2934
|
+
// on a repeat submit the reply goes to whatever the FIRST handover used.
|
|
2935
|
+
outcome: result.created ? "sent" : "already-handed-over",
|
|
2936
|
+
email: result.escalation?.email ?? null,
|
|
2937
|
+
createdAt: result.escalation?.createdAt ?? null
|
|
2938
|
+
};
|
|
2939
|
+
} catch {
|
|
2940
|
+
return { outcome: "failed", email: null, createdAt: null };
|
|
2941
|
+
}
|
|
2942
|
+
};
|
|
2943
|
+
return attempt(0);
|
|
2944
|
+
},
|
|
2945
|
+
[client, productId]
|
|
2946
|
+
);
|
|
2947
|
+
const canEscalate = hasConversation && !isSharedView;
|
|
2318
2948
|
return {
|
|
2319
2949
|
phase,
|
|
2320
2950
|
catalog,
|
|
@@ -2335,6 +2965,8 @@ function useExploreChat(options = {}) {
|
|
|
2335
2965
|
clearHistory,
|
|
2336
2966
|
recordClick,
|
|
2337
2967
|
submitFeedback,
|
|
2968
|
+
escalate,
|
|
2969
|
+
canEscalate,
|
|
2338
2970
|
createShareLink,
|
|
2339
2971
|
isSharedView
|
|
2340
2972
|
};
|
|
@@ -3020,9 +3652,6 @@ function PageHead({
|
|
|
3020
3652
|
))
|
|
3021
3653
|
] });
|
|
3022
3654
|
}
|
|
3023
|
-
function cn(...inputs) {
|
|
3024
|
-
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
3025
|
-
}
|
|
3026
3655
|
var buttonVariants = classVarianceAuthority.cva(
|
|
3027
3656
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
3028
3657
|
{
|
|
@@ -3260,163 +3889,27 @@ var EXPLORE_CSS = `
|
|
|
3260
3889
|
.boff-explore-scroll::-webkit-scrollbar { width: 10px; }
|
|
3261
3890
|
.boff-explore-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
3262
3891
|
.boff-explore-scroll::-webkit-scrollbar-thumb {
|
|
3263
|
-
background-color: hsl(var(--border));
|
|
3264
|
-
border: 3px solid transparent;
|
|
3265
|
-
border-radius: 9999px;
|
|
3266
|
-
background-clip: content-box;
|
|
3267
|
-
}
|
|
3268
|
-
|
|
3269
|
-
/* The product strip scrolls inside itself; a visible scrollbar would read as a broken
|
|
3270
|
-
layout rather than an affordance, and reserving its gutter shifts the chips. */
|
|
3271
|
-
.boff-explore-strip {
|
|
3272
|
-
scrollbar-width: none;
|
|
3273
|
-
-ms-overflow-style: none;
|
|
3274
|
-
}
|
|
3275
|
-
.boff-explore-strip::-webkit-scrollbar { display: none; }
|
|
3276
|
-
|
|
3277
|
-
@keyframes boff-explore-fade-in {
|
|
3278
|
-
from { opacity: 0; transform: translateY(2px); }
|
|
3279
|
-
to { opacity: 1; transform: none; }
|
|
3280
|
-
}
|
|
3281
|
-
.boff-explore-fade { animation: boff-explore-fade-in 220ms ease-out; }
|
|
3282
|
-
@media (prefers-reduced-motion: reduce) {
|
|
3283
|
-
.boff-explore-fade { animation: none; }
|
|
3284
|
-
}
|
|
3285
|
-
|
|
3286
|
-
/* Nothing inside an answer may widen the page. Model output carries long URLs, code and
|
|
3287
|
-
tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
|
|
3288
|
-
WHOLE document horizontally scrollable, not just the bubble. */
|
|
3289
|
-
.boff-explore-body,
|
|
3290
|
-
.boff-explore-body p,
|
|
3291
|
-
.boff-explore-body li,
|
|
3292
|
-
.boff-explore-body a,
|
|
3293
|
-
.boff-explore-body h1,
|
|
3294
|
-
.boff-explore-body h2,
|
|
3295
|
-
.boff-explore-body h3 {
|
|
3296
|
-
overflow-wrap: anywhere;
|
|
3297
|
-
word-break: break-word;
|
|
3298
|
-
}
|
|
3299
|
-
.boff-explore-body pre {
|
|
3300
|
-
overflow-x: auto;
|
|
3301
|
-
max-width: 100%;
|
|
3302
|
-
}
|
|
3303
|
-
.boff-explore-body table {
|
|
3304
|
-
display: block;
|
|
3305
|
-
overflow-x: auto;
|
|
3306
|
-
max-width: 100%;
|
|
3307
|
-
}
|
|
3308
|
-
.boff-explore-body img {
|
|
3309
|
-
max-width: 100%;
|
|
3310
|
-
height: auto;
|
|
3311
|
-
}
|
|
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
|
-
}
|
|
3892
|
+
background-color: hsl(var(--border));
|
|
3893
|
+
border: 3px solid transparent;
|
|
3894
|
+
border-radius: 9999px;
|
|
3895
|
+
background-clip: content-box;
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3898
|
+
/* The product strip scrolls inside itself; a visible scrollbar would read as a broken
|
|
3899
|
+
layout rather than an affordance, and reserving its gutter shifts the chips. */
|
|
3900
|
+
.boff-explore-strip {
|
|
3901
|
+
scrollbar-width: none;
|
|
3902
|
+
-ms-overflow-style: none;
|
|
3903
|
+
}
|
|
3904
|
+
.boff-explore-strip::-webkit-scrollbar { display: none; }
|
|
3905
|
+
|
|
3906
|
+
@keyframes boff-explore-fade-in {
|
|
3907
|
+
from { opacity: 0; transform: translateY(2px); }
|
|
3908
|
+
to { opacity: 1; transform: none; }
|
|
3909
|
+
}
|
|
3910
|
+
.boff-explore-fade { animation: boff-explore-fade-in 220ms ease-out; }
|
|
3911
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3912
|
+
.boff-explore-fade { animation: none; }
|
|
3420
3913
|
}
|
|
3421
3914
|
|
|
3422
3915
|
/* ---- Ambient backdrop -----------------------------------------------------------------
|
|
@@ -3475,11 +3968,40 @@ var EXPLORE_CSS = `
|
|
|
3475
3968
|
animation: boff-explore-sheen 1.5s ease-in-out infinite;
|
|
3476
3969
|
}
|
|
3477
3970
|
|
|
3971
|
+
/* ---- The composer, before anyone has typed -----------------------------------------
|
|
3972
|
+
On the welcome screen the box IS the page: everything above it is a way of explaining
|
|
3973
|
+
what to put in it. Unhighlighted it reads as a search bar at the bottom of a landing
|
|
3974
|
+
page, which people scroll past \u2014 so while the conversation is empty it gets a soft halo
|
|
3975
|
+
in the site's own --primary and one slow breath of it, and the moment there is a
|
|
3976
|
+
transcript, or the visitor focuses or types, it drops back to the ordinary card it was.
|
|
3977
|
+
An input that keeps pulsing while you are using it is nagging, not inviting.
|
|
3978
|
+
|
|
3979
|
+
Colour comes from the product's --primary token, so it is whatever that site's palette
|
|
3980
|
+
already is, never a colour chosen here. */
|
|
3981
|
+
.boff-explore-invite {
|
|
3982
|
+
border-color: hsl(var(--primary) / 0.45);
|
|
3983
|
+
box-shadow:
|
|
3984
|
+
0 0 0 4px hsl(var(--primary) / 0.08),
|
|
3985
|
+
0 8px 24px -12px hsl(var(--primary) / 0.45);
|
|
3986
|
+
animation: boff-explore-breathe 3.2s ease-in-out infinite;
|
|
3987
|
+
}
|
|
3988
|
+
/* Focus and typing both mean "I have found it", and the hint has done its job. */
|
|
3989
|
+
.boff-explore-invite:focus-within,
|
|
3990
|
+
.boff-explore-invite.boff-explore-invite-quiet {
|
|
3991
|
+
animation: none;
|
|
3992
|
+
box-shadow: 0 0 0 4px hsl(var(--primary) / 0.10);
|
|
3993
|
+
}
|
|
3994
|
+
@keyframes boff-explore-breathe {
|
|
3995
|
+
0%, 100% { box-shadow: 0 0 0 4px hsl(var(--primary) / 0.07), 0 8px 24px -12px hsl(var(--primary) / 0.40); }
|
|
3996
|
+
50% { box-shadow: 0 0 0 7px hsl(var(--primary) / 0.13), 0 8px 28px -12px hsl(var(--primary) / 0.55); }
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3478
3999
|
/* Every one of the above is decoration. Reduced motion removes the movement and keeps the
|
|
3479
4000
|
final state, rather than leaving content mid-animation and invisible. */
|
|
3480
4001
|
@media (prefers-reduced-motion: reduce) {
|
|
3481
4002
|
.boff-explore-aura::before,
|
|
3482
|
-
.boff-explore-busy::after
|
|
4003
|
+
.boff-explore-busy::after,
|
|
4004
|
+
.boff-explore-invite { animation: none; }
|
|
3483
4005
|
.boff-explore-rise { animation: none; opacity: 1; transform: none; }
|
|
3484
4006
|
}
|
|
3485
4007
|
|
|
@@ -3506,457 +4028,211 @@ var EXPLORE_CSS = `
|
|
|
3506
4028
|
something is actually covering the bottom, so a snapshot taken
|
|
3507
4029
|
with no keyboard open never carries it. */
|
|
3508
4030
|
.boff-explore-viewport {
|
|
3509
|
-
height: calc(100vh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
|
|
3510
|
-
}
|
|
3511
|
-
@supports (height: 100dvh) {
|
|
3512
|
-
.boff-explore-viewport {
|
|
3513
|
-
height: calc(100dvh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
|
|
3514
|
-
}
|
|
3515
|
-
}
|
|
3516
|
-
|
|
3517
|
-
/* Landscape phones and other short viewports: a one-screen pane leaves a ~124px transcript
|
|
3518
|
-
sliver under ~610px of chrome and content. At or below the threshold the pane stops
|
|
3519
|
-
pretending to be a window and lets the page's own scrolling carry the transcript. The
|
|
3520
|
-
min-height is the available height rather than a fixed rem value, so the fallback can
|
|
3521
|
-
never be taller than the screen it is rescuing. */
|
|
3522
|
-
@media (max-height: ${SHORT_VIEWPORT_PX}px) {
|
|
3523
|
-
.boff-explore-viewport {
|
|
3524
|
-
height: auto;
|
|
3525
|
-
min-height: calc(100vh - var(--boff-explore-offset, 0px));
|
|
3526
|
-
}
|
|
3527
|
-
}
|
|
3528
|
-
@supports (height: 100dvh) {
|
|
3529
|
-
@media (max-height: ${SHORT_VIEWPORT_PX}px) {
|
|
3530
|
-
.boff-explore-viewport {
|
|
3531
|
-
min-height: calc(100dvh - var(--boff-explore-offset, 0px));
|
|
3532
|
-
}
|
|
3533
|
-
}
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
/* ---- Composer dock --------------------------------------------------------------------
|
|
3537
|
-
Three declarations, each the fallback for the one after it: a plain 1rem where \`max()\`
|
|
3538
|
-
is unknown, then the measured overlap of any FOREIGN fixed chrome sitting over the
|
|
3539
|
-
composer (the host sites' cookie banner is 229px tall and lands exactly there), then the
|
|
3540
|
-
same plus the home-indicator inset. \`--boff-explore-chrome\` is set on the root only once
|
|
3541
|
-
an overlap has actually been measured, so everywhere else this collapses to 1rem. */
|
|
3542
|
-
.boff-explore-composer {
|
|
3543
|
-
padding-bottom: 1rem;
|
|
3544
|
-
padding-bottom: max(1rem, var(--boff-explore-chrome, 0px));
|
|
3545
|
-
padding-bottom: max(1rem, env(safe-area-inset-bottom), var(--boff-explore-chrome, 0px));
|
|
3546
|
-
}
|
|
3547
|
-
`;
|
|
3548
|
-
function ExploreStyles() {
|
|
3549
|
-
return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
|
|
3550
|
-
}
|
|
3551
|
-
var CAPABILITIES = [
|
|
3552
|
-
{
|
|
3553
|
-
icon: lucideReact.BookOpenText,
|
|
3554
|
-
title: "Grounded in our own docs",
|
|
3555
|
-
body: "Every answer comes from published product pages and documentation \u2014 never invented."
|
|
3556
|
-
},
|
|
3557
|
-
{
|
|
3558
|
-
icon: lucideReact.Layers,
|
|
3559
|
-
title: "Compare across the ecosystem",
|
|
3560
|
-
body: "Ask which product fits your team, or how two of them work together."
|
|
3561
|
-
},
|
|
3562
|
-
{
|
|
3563
|
-
icon: lucideReact.Link2,
|
|
3564
|
-
title: "Straight to the right page",
|
|
3565
|
-
body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
|
|
3566
|
-
}
|
|
3567
|
-
];
|
|
3568
|
-
var MIN_VISIBLE_FRACTION = 0.25;
|
|
3569
|
-
var SDK_OVERLAY_SELECTOR = "[data-boff-explore]";
|
|
3570
|
-
var CTA_VARIANTS = {
|
|
3571
|
-
CONTACT: "default",
|
|
3572
|
-
WAITLIST: "default",
|
|
3573
|
-
APP: "default",
|
|
3574
|
-
PRICING: "secondary",
|
|
3575
|
-
STORE: "secondary",
|
|
3576
|
-
PARTNERS: "secondary",
|
|
3577
|
-
DOCS: "outline",
|
|
3578
|
-
SERVICES: "outline",
|
|
3579
|
-
OTHER: "outline"
|
|
3580
|
-
};
|
|
3581
|
-
function normalizeOffset(offset) {
|
|
3582
|
-
if (offset === void 0) return "0px";
|
|
3583
|
-
return typeof offset === "number" ? `${offset}px` : offset;
|
|
3584
|
-
}
|
|
3585
|
-
function isOwnElement(el, root) {
|
|
3586
|
-
if (el === document.body || el === document.documentElement) return true;
|
|
3587
|
-
if (root.contains(el)) return true;
|
|
3588
|
-
return el.closest(SDK_OVERLAY_SELECTOR) !== null;
|
|
3589
|
-
}
|
|
3590
|
-
function visualViewportHeight() {
|
|
3591
|
-
return window.visualViewport?.height ?? window.innerHeight;
|
|
3592
|
-
}
|
|
3593
|
-
function clamp2(value, min, max) {
|
|
3594
|
-
return Math.min(Math.max(value, min), max);
|
|
3595
|
-
}
|
|
3596
|
-
function isRestoredNavigation() {
|
|
3597
|
-
const entries = window.performance?.getEntriesByType?.("navigation");
|
|
3598
|
-
const entry = entries?.[0];
|
|
3599
|
-
if (!entry) return true;
|
|
3600
|
-
const navigation = entry;
|
|
3601
|
-
return navigation.type === "reload" || navigation.type === "back_forward";
|
|
3602
|
-
}
|
|
3603
|
-
function pageOwnsScrolling(el) {
|
|
3604
|
-
if (window.innerHeight > SHORT_VIEWPORT_PX) return false;
|
|
3605
|
-
return el.scrollHeight - el.clientHeight <= 1;
|
|
3606
|
-
}
|
|
3607
|
-
function isInternalPath(url) {
|
|
3608
|
-
return url.startsWith("/") && !url.startsWith("//");
|
|
3609
|
-
}
|
|
3610
|
-
function hostnameOf(url) {
|
|
3611
|
-
try {
|
|
3612
|
-
return new URL(url).hostname.replace(/^www\./, "");
|
|
3613
|
-
} catch {
|
|
3614
|
-
return "";
|
|
3615
|
-
}
|
|
3616
|
-
}
|
|
3617
|
-
function initialOf(value, fallback) {
|
|
3618
|
-
const source = (value || fallback).trim();
|
|
3619
|
-
return source ? source.slice(0, 1).toUpperCase() : "?";
|
|
3620
|
-
}
|
|
3621
|
-
function logoUrlOf(url) {
|
|
3622
|
-
try {
|
|
3623
|
-
const { origin, protocol } = new URL(url);
|
|
3624
|
-
if (protocol !== "https:" && protocol !== "http:") return null;
|
|
3625
|
-
return `${origin}/favicon.svg`;
|
|
3626
|
-
} catch {
|
|
3627
|
-
return null;
|
|
3628
|
-
}
|
|
3629
|
-
}
|
|
3630
|
-
function inlineImageOrigins(products) {
|
|
3631
|
-
const origins = /* @__PURE__ */ new Set();
|
|
3632
|
-
for (const product of products) {
|
|
3633
|
-
let url;
|
|
3634
|
-
try {
|
|
3635
|
-
url = new URL(product.website);
|
|
3636
|
-
} catch {
|
|
3637
|
-
continue;
|
|
3638
|
-
}
|
|
3639
|
-
if (url.protocol !== "https:") continue;
|
|
3640
|
-
const apex = url.hostname.replace(/^www\./, "");
|
|
3641
|
-
const port = url.port ? `:${url.port}` : "";
|
|
3642
|
-
origins.add(`https://${apex}${port}`);
|
|
3643
|
-
origins.add(`https://www.${apex}${port}`);
|
|
3644
|
-
}
|
|
3645
|
-
return origins;
|
|
3646
|
-
}
|
|
3647
|
-
function isInlineImageAllowed(src, origins) {
|
|
3648
|
-
if (typeof src !== "string" || src === "") return false;
|
|
3649
|
-
let url;
|
|
3650
|
-
try {
|
|
3651
|
-
url = new URL(src);
|
|
3652
|
-
} catch {
|
|
3653
|
-
return false;
|
|
3654
|
-
}
|
|
3655
|
-
if (url.protocol !== "https:" || url.username || url.password) return false;
|
|
3656
|
-
return origins.has(url.origin);
|
|
3657
|
-
}
|
|
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
|
-
};
|
|
4031
|
+
height: calc(100vh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
|
|
3741
4032
|
}
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
var
|
|
3745
|
-
|
|
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]+$/, "");
|
|
4033
|
+
@supports (height: 100dvh) {
|
|
4034
|
+
.boff-explore-viewport {
|
|
4035
|
+
height: calc(100dvh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
|
|
4036
|
+
}
|
|
3763
4037
|
}
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
4038
|
+
|
|
4039
|
+
/* Landscape phones and other short viewports: a one-screen pane leaves a ~124px transcript
|
|
4040
|
+
sliver under ~610px of chrome and content. At or below the threshold the pane stops
|
|
4041
|
+
pretending to be a window and lets the page's own scrolling carry the transcript. The
|
|
4042
|
+
min-height is the available height rather than a fixed rem value, so the fallback can
|
|
4043
|
+
never be taller than the screen it is rescuing. */
|
|
4044
|
+
@media (max-height: ${SHORT_VIEWPORT_PX}px) {
|
|
4045
|
+
.boff-explore-viewport {
|
|
4046
|
+
height: auto;
|
|
4047
|
+
min-height: calc(100vh - var(--boff-explore-offset, 0px));
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
@supports (height: 100dvh) {
|
|
4051
|
+
@media (max-height: ${SHORT_VIEWPORT_PX}px) {
|
|
4052
|
+
.boff-explore-viewport {
|
|
4053
|
+
min-height: calc(100dvh - var(--boff-explore-offset, 0px));
|
|
3779
4054
|
}
|
|
3780
|
-
cell += char;
|
|
3781
4055
|
}
|
|
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
4056
|
}
|
|
3787
|
-
|
|
3788
|
-
|
|
4057
|
+
|
|
4058
|
+
/* ---- Composer dock --------------------------------------------------------------------
|
|
4059
|
+
Three declarations, each the fallback for the one after it: a plain 1rem where \`max()\`
|
|
4060
|
+
is unknown, then the measured overlap of any FOREIGN fixed chrome sitting over the
|
|
4061
|
+
composer (the host sites' cookie banner is 229px tall and lands exactly there), then the
|
|
4062
|
+
same plus the home-indicator inset. \`--boff-explore-chrome\` is set on the root only once
|
|
4063
|
+
an overlap has actually been measured, so everywhere else this collapses to 1rem. */
|
|
4064
|
+
.boff-explore-composer {
|
|
4065
|
+
padding-bottom: 1rem;
|
|
4066
|
+
padding-bottom: max(1rem, var(--boff-explore-chrome, 0px));
|
|
4067
|
+
padding-bottom: max(1rem, env(safe-area-inset-bottom), var(--boff-explore-chrome, 0px));
|
|
3789
4068
|
}
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
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;
|
|
4069
|
+
`;
|
|
4070
|
+
function ExploreStyles() {
|
|
4071
|
+
return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
|
|
3804
4072
|
}
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
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));
|
|
4073
|
+
var CAPABILITIES = [
|
|
4074
|
+
{
|
|
4075
|
+
icon: lucideReact.BookOpenText,
|
|
4076
|
+
title: "Grounded in our own docs",
|
|
4077
|
+
body: "Every answer comes from published product pages and documentation \u2014 never invented."
|
|
4078
|
+
},
|
|
4079
|
+
{
|
|
4080
|
+
icon: lucideReact.Layers,
|
|
4081
|
+
title: "Compare across the ecosystem",
|
|
4082
|
+
body: "Ask which product fits your team, or how two of them work together."
|
|
4083
|
+
},
|
|
4084
|
+
{
|
|
4085
|
+
icon: lucideReact.Link2,
|
|
4086
|
+
title: "Straight to the right page",
|
|
4087
|
+
body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
|
|
3830
4088
|
}
|
|
3831
|
-
|
|
3832
|
-
|
|
4089
|
+
];
|
|
4090
|
+
var MIN_VISIBLE_FRACTION = 0.25;
|
|
4091
|
+
var SDK_OVERLAY_SELECTOR = "[data-boff-explore]";
|
|
4092
|
+
var CTA_VARIANTS = {
|
|
4093
|
+
CONTACT: "default",
|
|
4094
|
+
WAITLIST: "default",
|
|
4095
|
+
APP: "default",
|
|
4096
|
+
PRICING: "secondary",
|
|
4097
|
+
STORE: "secondary",
|
|
4098
|
+
PARTNERS: "secondary",
|
|
4099
|
+
DOCS: "outline",
|
|
4100
|
+
SERVICES: "outline",
|
|
4101
|
+
OTHER: "outline"
|
|
4102
|
+
};
|
|
4103
|
+
function normalizeOffset(offset) {
|
|
4104
|
+
if (offset === void 0) return "0px";
|
|
4105
|
+
return typeof offset === "number" ? `${offset}px` : offset;
|
|
3833
4106
|
}
|
|
3834
|
-
function
|
|
3835
|
-
|
|
4107
|
+
function isOwnElement(el, root) {
|
|
4108
|
+
if (el === document.body || el === document.documentElement) return true;
|
|
4109
|
+
if (root.contains(el)) return true;
|
|
4110
|
+
return el.closest(SDK_OVERLAY_SELECTOR) !== null;
|
|
3836
4111
|
}
|
|
3837
|
-
function
|
|
3838
|
-
return
|
|
4112
|
+
function visualViewportHeight() {
|
|
4113
|
+
return window.visualViewport?.height ?? window.innerHeight;
|
|
3839
4114
|
}
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
function AnswerLink(props) {
|
|
3843
|
-
return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
|
|
4115
|
+
function clamp2(value, min, max) {
|
|
4116
|
+
return Math.min(Math.max(value, min), max);
|
|
3844
4117
|
}
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
const
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
}
|
|
3852
|
-
const { strings: T, onFailed } = scope;
|
|
3853
|
-
const picture = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3854
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3855
|
-
"img",
|
|
3856
|
-
{
|
|
3857
|
-
src,
|
|
3858
|
-
alt: alt ?? "",
|
|
3859
|
-
loading: "lazy",
|
|
3860
|
-
decoding: "async",
|
|
3861
|
-
onError: () => onFailed(src),
|
|
3862
|
-
className: "block h-auto max-h-80 w-auto max-w-full object-contain"
|
|
3863
|
-
}
|
|
3864
|
-
),
|
|
3865
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3866
|
-
"span",
|
|
3867
|
-
{
|
|
3868
|
-
"aria-hidden": "true",
|
|
3869
|
-
className: "pointer-events-none absolute left-2 top-2 rounded-full border border-border bg-background/90 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-foreground shadow-sm",
|
|
3870
|
-
children: T.conceptIllustration
|
|
3871
|
-
}
|
|
3872
|
-
)
|
|
3873
|
-
] });
|
|
3874
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-boff-explore": "inline-image", className: "my-3 block", children: insideLink ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: INLINE_IMAGE_FRAME, children: picture }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3875
|
-
"a",
|
|
3876
|
-
{
|
|
3877
|
-
href: src,
|
|
3878
|
-
target: "_blank",
|
|
3879
|
-
rel: "noopener noreferrer",
|
|
3880
|
-
className: cn(
|
|
3881
|
-
INLINE_IMAGE_FRAME,
|
|
3882
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
3883
|
-
),
|
|
3884
|
-
children: [
|
|
3885
|
-
picture,
|
|
3886
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
|
|
3887
|
-
"(",
|
|
3888
|
-
T.opensInNewTab,
|
|
3889
|
-
")"
|
|
3890
|
-
] })
|
|
3891
|
-
]
|
|
3892
|
-
}
|
|
3893
|
-
) });
|
|
4118
|
+
function isRestoredNavigation() {
|
|
4119
|
+
const entries = window.performance?.getEntriesByType?.("navigation");
|
|
4120
|
+
const entry = entries?.[0];
|
|
4121
|
+
if (!entry) return true;
|
|
4122
|
+
const navigation = entry;
|
|
4123
|
+
return navigation.type === "reload" || navigation.type === "back_forward";
|
|
3894
4124
|
}
|
|
3895
|
-
function
|
|
3896
|
-
if (
|
|
3897
|
-
|
|
3898
|
-
return (node.children ?? []).map(markdownText).join("");
|
|
4125
|
+
function pageOwnsScrolling(el) {
|
|
4126
|
+
if (window.innerHeight > SHORT_VIEWPORT_PX) return false;
|
|
4127
|
+
return el.scrollHeight - el.clientHeight <= 1;
|
|
3899
4128
|
}
|
|
3900
|
-
function
|
|
3901
|
-
return
|
|
4129
|
+
function isInternalPath(url) {
|
|
4130
|
+
return url.startsWith("/") && !url.startsWith("//");
|
|
3902
4131
|
}
|
|
3903
|
-
function
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
4132
|
+
function hostnameOf(url) {
|
|
4133
|
+
try {
|
|
4134
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
4135
|
+
} catch {
|
|
4136
|
+
return "";
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
function initialOf(value, fallback) {
|
|
4140
|
+
const source = (value || fallback).trim();
|
|
4141
|
+
return source ? source.slice(0, 1).toUpperCase() : "?";
|
|
4142
|
+
}
|
|
4143
|
+
function logoUrlOf(url) {
|
|
4144
|
+
try {
|
|
4145
|
+
const { origin, protocol } = new URL(url);
|
|
4146
|
+
if (protocol !== "https:" && protocol !== "http:") return null;
|
|
4147
|
+
return `${origin}/favicon.svg`;
|
|
4148
|
+
} catch {
|
|
4149
|
+
return null;
|
|
3908
4150
|
}
|
|
3909
|
-
return null;
|
|
3910
4151
|
}
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
4152
|
+
var REFERENCE_KIND_ORDER = [
|
|
4153
|
+
"CONCEPT",
|
|
4154
|
+
"DOCS",
|
|
4155
|
+
"PRICING",
|
|
4156
|
+
"USE_CASE",
|
|
4157
|
+
"PAGE"
|
|
4158
|
+
];
|
|
4159
|
+
var PRODUCT_SUBDOMAINS = [
|
|
4160
|
+
"alphadocs.",
|
|
4161
|
+
"alphaapp.",
|
|
4162
|
+
"docs.",
|
|
4163
|
+
"alpha.",
|
|
4164
|
+
"app.",
|
|
4165
|
+
"www."
|
|
4166
|
+
];
|
|
4167
|
+
function readCatalogUrl(raw) {
|
|
4168
|
+
let parsed;
|
|
4169
|
+
try {
|
|
4170
|
+
parsed = new URL(raw);
|
|
4171
|
+
} catch {
|
|
4172
|
+
return null;
|
|
4173
|
+
}
|
|
4174
|
+
if (parsed.protocol !== "https:") return null;
|
|
4175
|
+
const host = parsed.hostname.toLowerCase();
|
|
4176
|
+
const prefix = PRODUCT_SUBDOMAINS.find(
|
|
4177
|
+
(candidate) => host.startsWith(candidate)
|
|
4178
|
+
);
|
|
4179
|
+
const apex = prefix === void 0 ? host : host.slice(prefix.length);
|
|
4180
|
+
const port = parsed.port ? `:${parsed.port}` : "";
|
|
4181
|
+
return {
|
|
4182
|
+
origin: `https://${apex}${port}`,
|
|
4183
|
+
prefix,
|
|
4184
|
+
pathname: parsed.pathname
|
|
4185
|
+
};
|
|
3915
4186
|
}
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
4187
|
+
function referenceKindOf(url, origins) {
|
|
4188
|
+
const parsed = readCatalogUrl(url);
|
|
4189
|
+
if (parsed === null || !origins.has(parsed.origin)) return null;
|
|
4190
|
+
if (parsed.prefix === "docs." || parsed.prefix === "alphadocs.")
|
|
4191
|
+
return "DOCS";
|
|
4192
|
+
const segment = parsed.pathname.toLowerCase().split("/").filter(Boolean)[0] ?? "";
|
|
4193
|
+
if (segment === "concept" || segment === "concepts") return "CONCEPT";
|
|
4194
|
+
if (segment === "docs") return "DOCS";
|
|
4195
|
+
if (segment === "pricing" || segment === "plans") return "PRICING";
|
|
4196
|
+
if (segment === "use-case" || segment === "use-cases" || segment === "solutions") {
|
|
4197
|
+
return "USE_CASE";
|
|
4198
|
+
}
|
|
4199
|
+
return "PAGE";
|
|
4200
|
+
}
|
|
4201
|
+
function productNamesByOrigin(products) {
|
|
4202
|
+
const names = /* @__PURE__ */ new Map();
|
|
4203
|
+
for (const product of products) {
|
|
4204
|
+
let url;
|
|
4205
|
+
try {
|
|
4206
|
+
url = new URL(product.website);
|
|
4207
|
+
} catch {
|
|
4208
|
+
continue;
|
|
3930
4209
|
}
|
|
3931
|
-
|
|
4210
|
+
if (url.protocol !== "https:") continue;
|
|
4211
|
+
const apex = url.hostname.replace(/^www\./, "");
|
|
4212
|
+
const port = url.port ? `:${url.port}` : "";
|
|
4213
|
+
const key = `https://${apex}${port}`;
|
|
4214
|
+
if (!names.has(key)) names.set(key, product.name);
|
|
4215
|
+
}
|
|
4216
|
+
return names;
|
|
3932
4217
|
}
|
|
3933
|
-
function
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4218
|
+
function summariseReferences(references, origins, productNames) {
|
|
4219
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4220
|
+
const products = [];
|
|
4221
|
+
for (const reference of references) {
|
|
4222
|
+
const kind = referenceKindOf(reference.url, origins);
|
|
4223
|
+
if (kind !== null) counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
4224
|
+
const parsed = readCatalogUrl(reference.url);
|
|
4225
|
+
const name = (parsed === null ? void 0 : productNames.get(parsed.origin)) ?? (reference.product || null);
|
|
4226
|
+
if (name != null && !products.includes(name)) products.push(name);
|
|
3941
4227
|
}
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
"data-label": label
|
|
3950
|
-
});
|
|
3951
|
-
});
|
|
3952
|
-
return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
|
|
4228
|
+
return {
|
|
4229
|
+
total: references.length,
|
|
4230
|
+
kinds: REFERENCE_KIND_ORDER.filter((kind) => counts.has(kind)).map(
|
|
4231
|
+
(kind) => ({ kind, count: counts.get(kind) ?? 0 })
|
|
4232
|
+
),
|
|
4233
|
+
products
|
|
4234
|
+
};
|
|
3953
4235
|
}
|
|
3954
|
-
var ANSWER_MARKDOWN_COMPONENTS = {
|
|
3955
|
-
a: AnswerLink,
|
|
3956
|
-
img: InlineConceptImage,
|
|
3957
|
-
table: AnswerTable,
|
|
3958
|
-
tr: AnswerTableRow
|
|
3959
|
-
};
|
|
3960
4236
|
function ReferenceCard({
|
|
3961
4237
|
reference,
|
|
3962
4238
|
onFollow
|
|
@@ -4295,6 +4571,195 @@ function AnswerFeedback({
|
|
|
4295
4571
|
}
|
|
4296
4572
|
);
|
|
4297
4573
|
}
|
|
4574
|
+
function EscalateForm({
|
|
4575
|
+
strings: T,
|
|
4576
|
+
formId,
|
|
4577
|
+
onEscalate,
|
|
4578
|
+
onClose,
|
|
4579
|
+
onBusyChange
|
|
4580
|
+
}) {
|
|
4581
|
+
const [email, setEmail] = React.useState("");
|
|
4582
|
+
const [note, setNote] = React.useState("");
|
|
4583
|
+
const [error, setError] = React.useState(null);
|
|
4584
|
+
const [sending, setSending] = React.useState(false);
|
|
4585
|
+
const [sentTo, setSentTo] = React.useState(null);
|
|
4586
|
+
const emailRef = React.useRef(null);
|
|
4587
|
+
const inFlightRef = React.useRef(false);
|
|
4588
|
+
const titleId = `${formId}-title`;
|
|
4589
|
+
const emailId = `${formId}-email`;
|
|
4590
|
+
const noteId = `${formId}-note`;
|
|
4591
|
+
const errorId = `${formId}-error`;
|
|
4592
|
+
React.useEffect(() => {
|
|
4593
|
+
emailRef.current?.focus();
|
|
4594
|
+
}, []);
|
|
4595
|
+
React.useEffect(() => {
|
|
4596
|
+
onBusyChange?.(sending);
|
|
4597
|
+
return () => onBusyChange?.(false);
|
|
4598
|
+
}, [sending, onBusyChange]);
|
|
4599
|
+
const handleSubmit = React.useCallback(
|
|
4600
|
+
async (event) => {
|
|
4601
|
+
event.preventDefault();
|
|
4602
|
+
if (inFlightRef.current) return;
|
|
4603
|
+
const address = email.trim();
|
|
4604
|
+
if (!looksLikeDeliverableEmail(address)) {
|
|
4605
|
+
setError(T.escalateInvalidEmail);
|
|
4606
|
+
emailRef.current?.focus();
|
|
4607
|
+
return;
|
|
4608
|
+
}
|
|
4609
|
+
inFlightRef.current = true;
|
|
4610
|
+
setSending(true);
|
|
4611
|
+
setError(null);
|
|
4612
|
+
const result = await onEscalate(address, note);
|
|
4613
|
+
inFlightRef.current = false;
|
|
4614
|
+
setSending(false);
|
|
4615
|
+
switch (result.outcome) {
|
|
4616
|
+
case "sent":
|
|
4617
|
+
case "already-handed-over":
|
|
4618
|
+
setSentTo(result.email ?? address);
|
|
4619
|
+
return;
|
|
4620
|
+
case "invalid-email":
|
|
4621
|
+
setError(T.escalateInvalidEmail);
|
|
4622
|
+
emailRef.current?.focus();
|
|
4623
|
+
return;
|
|
4624
|
+
case "rate-limited":
|
|
4625
|
+
setError(T.escalateRateLimited);
|
|
4626
|
+
return;
|
|
4627
|
+
case "captcha":
|
|
4628
|
+
setError(T.escalateCaptcha);
|
|
4629
|
+
return;
|
|
4630
|
+
// `no-conversation` included: the form should not have been offered at all, and the
|
|
4631
|
+
// honest thing to show is that nothing was passed on.
|
|
4632
|
+
default:
|
|
4633
|
+
setError(T.escalateFailed);
|
|
4634
|
+
return;
|
|
4635
|
+
}
|
|
4636
|
+
},
|
|
4637
|
+
[T, email, note, onEscalate]
|
|
4638
|
+
);
|
|
4639
|
+
if (sentTo !== null) {
|
|
4640
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4641
|
+
"p",
|
|
4642
|
+
{
|
|
4643
|
+
id: formId,
|
|
4644
|
+
"data-boff-explore": "escalate-sent",
|
|
4645
|
+
role: "status",
|
|
4646
|
+
className: "boff-explore-fade mt-2 rounded-xl border border-border bg-card px-3 py-2 text-xs leading-relaxed text-card-foreground",
|
|
4647
|
+
children: fill(T.escalateSent, { email: sentTo })
|
|
4648
|
+
}
|
|
4649
|
+
);
|
|
4650
|
+
}
|
|
4651
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4652
|
+
"form",
|
|
4653
|
+
{
|
|
4654
|
+
id: formId,
|
|
4655
|
+
"data-boff-explore": "escalate-form",
|
|
4656
|
+
"aria-labelledby": titleId,
|
|
4657
|
+
noValidate: true,
|
|
4658
|
+
onSubmit: handleSubmit,
|
|
4659
|
+
onKeyDown: (event) => {
|
|
4660
|
+
if (event.key !== "Escape") return;
|
|
4661
|
+
event.stopPropagation();
|
|
4662
|
+
if (sending) return;
|
|
4663
|
+
onClose();
|
|
4664
|
+
},
|
|
4665
|
+
className: "boff-explore-fade mt-2 space-y-2 rounded-xl border border-border bg-card p-3",
|
|
4666
|
+
children: [
|
|
4667
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { id: titleId, className: "text-xs font-medium text-card-foreground", children: T.escalateTitle }),
|
|
4668
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: T.escalateBody }),
|
|
4669
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
4670
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4671
|
+
"label",
|
|
4672
|
+
{
|
|
4673
|
+
htmlFor: emailId,
|
|
4674
|
+
className: "block text-xs font-medium text-card-foreground",
|
|
4675
|
+
children: T.escalateEmailLabel
|
|
4676
|
+
}
|
|
4677
|
+
),
|
|
4678
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4679
|
+
"input",
|
|
4680
|
+
{
|
|
4681
|
+
ref: emailRef,
|
|
4682
|
+
id: emailId,
|
|
4683
|
+
name: "email",
|
|
4684
|
+
"data-boff-explore": "escalate-email",
|
|
4685
|
+
type: "email",
|
|
4686
|
+
inputMode: "email",
|
|
4687
|
+
autoComplete: "email",
|
|
4688
|
+
required: true,
|
|
4689
|
+
value: email,
|
|
4690
|
+
onChange: (event) => {
|
|
4691
|
+
setEmail(event.target.value);
|
|
4692
|
+
if (error) setError(null);
|
|
4693
|
+
},
|
|
4694
|
+
"aria-invalid": error ? "true" : void 0,
|
|
4695
|
+
"aria-describedby": error ? errorId : void 0,
|
|
4696
|
+
placeholder: T.escalateEmailPlaceholder,
|
|
4697
|
+
className: "w-full rounded-md border border-border bg-background px-2 py-1 text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:text-sm"
|
|
4698
|
+
}
|
|
4699
|
+
)
|
|
4700
|
+
] }),
|
|
4701
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
4702
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4703
|
+
"label",
|
|
4704
|
+
{
|
|
4705
|
+
htmlFor: noteId,
|
|
4706
|
+
className: "block text-xs font-medium text-card-foreground",
|
|
4707
|
+
children: T.escalateNoteLabel
|
|
4708
|
+
}
|
|
4709
|
+
),
|
|
4710
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4711
|
+
Textarea,
|
|
4712
|
+
{
|
|
4713
|
+
id: noteId,
|
|
4714
|
+
name: "note",
|
|
4715
|
+
"data-boff-explore": "escalate-note",
|
|
4716
|
+
rows: 2,
|
|
4717
|
+
value: note,
|
|
4718
|
+
onChange: (event) => setNote(event.target.value),
|
|
4719
|
+
maxLength: EXPLORE_ESCALATION_NOTE_SOFT_MAX,
|
|
4720
|
+
className: "min-h-0 border-border bg-background"
|
|
4721
|
+
}
|
|
4722
|
+
)
|
|
4723
|
+
] }),
|
|
4724
|
+
error ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4725
|
+
"p",
|
|
4726
|
+
{
|
|
4727
|
+
id: errorId,
|
|
4728
|
+
"data-boff-explore": "escalate-error",
|
|
4729
|
+
role: "alert",
|
|
4730
|
+
className: "text-xs leading-relaxed text-destructive",
|
|
4731
|
+
children: error
|
|
4732
|
+
}
|
|
4733
|
+
) : null,
|
|
4734
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
4735
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4736
|
+
Button,
|
|
4737
|
+
{
|
|
4738
|
+
"data-boff-explore": "escalate-send",
|
|
4739
|
+
type: "submit",
|
|
4740
|
+
size: "sm",
|
|
4741
|
+
disabled: sending,
|
|
4742
|
+
children: sending ? T.escalateSending : T.escalateSubmit
|
|
4743
|
+
}
|
|
4744
|
+
),
|
|
4745
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4746
|
+
Button,
|
|
4747
|
+
{
|
|
4748
|
+
"data-boff-explore": "escalate-cancel",
|
|
4749
|
+
type: "button",
|
|
4750
|
+
size: "sm",
|
|
4751
|
+
variant: "ghost",
|
|
4752
|
+
className: "text-muted-foreground",
|
|
4753
|
+
disabled: sending,
|
|
4754
|
+
onClick: onClose,
|
|
4755
|
+
children: T.escalateCancel
|
|
4756
|
+
}
|
|
4757
|
+
)
|
|
4758
|
+
] })
|
|
4759
|
+
]
|
|
4760
|
+
}
|
|
4761
|
+
);
|
|
4762
|
+
}
|
|
4298
4763
|
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
4764
|
function ProvenanceChips({
|
|
4300
4765
|
strings: T,
|
|
@@ -4488,31 +4953,15 @@ function AssistantBubble({
|
|
|
4488
4953
|
anchorRef,
|
|
4489
4954
|
onFollow,
|
|
4490
4955
|
onFollowUp,
|
|
4491
|
-
onFeedback
|
|
4956
|
+
onFeedback,
|
|
4957
|
+
onEscalate,
|
|
4958
|
+
isLatestAnswer = false
|
|
4492
4959
|
}) {
|
|
4493
4960
|
const streaming = message.status === "PENDING" || message.status === "RUNNING";
|
|
4494
4961
|
const body = message.content || message.partialContent || "";
|
|
4495
4962
|
const failed = message.status === "FAILED";
|
|
4496
4963
|
const completed = message.status === "COMPLETED";
|
|
4497
4964
|
const visible = useProgressiveReveal(body, !streaming);
|
|
4498
|
-
const [failedImages, setFailedImages] = React.useState(
|
|
4499
|
-
() => /* @__PURE__ */ new Set()
|
|
4500
|
-
);
|
|
4501
|
-
const markImageFailed = React.useCallback((src) => {
|
|
4502
|
-
setFailedImages(
|
|
4503
|
-
(current) => current.has(src) ? current : new Set(current).add(src)
|
|
4504
|
-
);
|
|
4505
|
-
}, []);
|
|
4506
|
-
const answerScope = React.useMemo(
|
|
4507
|
-
() => ({
|
|
4508
|
-
strings: T,
|
|
4509
|
-
enabled: completed,
|
|
4510
|
-
origins: imageOrigins,
|
|
4511
|
-
failed: failedImages,
|
|
4512
|
-
onFailed: markImageFailed
|
|
4513
|
-
}),
|
|
4514
|
-
[T, completed, imageOrigins, failedImages, markImageFailed]
|
|
4515
|
-
);
|
|
4516
4965
|
const provenance = React.useMemo(
|
|
4517
4966
|
() => summariseReferences(message.references, imageOrigins, productNames),
|
|
4518
4967
|
[message.references, imageOrigins, productNames]
|
|
@@ -4526,6 +4975,20 @@ function AssistantBubble({
|
|
|
4526
4975
|
[message.content]
|
|
4527
4976
|
);
|
|
4528
4977
|
const grounded = completed && message.answered !== false;
|
|
4978
|
+
const [escalateOpen, setEscalateOpen] = React.useState(false);
|
|
4979
|
+
const [escalateBusy, setEscalateBusy] = React.useState(false);
|
|
4980
|
+
const escalateTriggerRef = React.useRef(null);
|
|
4981
|
+
const escalateFormId = `boff-explore-escalate-${message.id}`;
|
|
4982
|
+
const unanswered = completed && message.answered === false;
|
|
4983
|
+
const offerEscalate = Boolean(onEscalate) && completed;
|
|
4984
|
+
const showQuietEscalate = offerEscalate && !unanswered && isLatestAnswer;
|
|
4985
|
+
const closeEscalate = React.useCallback(() => {
|
|
4986
|
+
setEscalateOpen(false);
|
|
4987
|
+
escalateTriggerRef.current?.focus();
|
|
4988
|
+
}, []);
|
|
4989
|
+
const handleEscalateBusy = React.useCallback((busy) => {
|
|
4990
|
+
setEscalateBusy(busy);
|
|
4991
|
+
}, []);
|
|
4529
4992
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4530
4993
|
"div",
|
|
4531
4994
|
{
|
|
@@ -4543,7 +5006,7 @@ function AssistantBubble({
|
|
|
4543
5006
|
}
|
|
4544
5007
|
),
|
|
4545
5008
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
|
|
4546
|
-
|
|
5009
|
+
unanswered ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4547
5010
|
"div",
|
|
4548
5011
|
{
|
|
4549
5012
|
"data-boff-explore": "no-answer",
|
|
@@ -4553,18 +5016,39 @@ function AssistantBubble({
|
|
|
4553
5016
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4554
5017
|
T.noAnswer,
|
|
4555
5018
|
" ",
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
5019
|
+
offerEscalate ? (
|
|
5020
|
+
/* The point of the wave: the same words, but they now open a form that
|
|
5021
|
+
sends the conversation we are both looking at, instead of a link to a
|
|
5022
|
+
blank contact page that asks the visitor to type it all again. */
|
|
5023
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5024
|
+
"button",
|
|
5025
|
+
{
|
|
5026
|
+
ref: escalateTriggerRef,
|
|
5027
|
+
"data-boff-explore": "escalate-open",
|
|
5028
|
+
type: "button",
|
|
5029
|
+
"aria-expanded": escalateOpen,
|
|
5030
|
+
"aria-controls": escalateOpen ? escalateFormId : void 0,
|
|
5031
|
+
onClick: () => setEscalateOpen(true),
|
|
5032
|
+
className: "rounded-sm underline underline-offset-2 hover:text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
5033
|
+
children: T.askHuman
|
|
5034
|
+
}
|
|
5035
|
+
)
|
|
5036
|
+
) : (
|
|
5037
|
+
/* No conversation to hand over (a shared, read-only view). The old dead end
|
|
5038
|
+
is still better than a control whose submit could only fail. */
|
|
5039
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5040
|
+
"a",
|
|
5041
|
+
{
|
|
5042
|
+
href: "/contact",
|
|
5043
|
+
className: "underline underline-offset-2 hover:text-primary",
|
|
5044
|
+
onClick: (event) => {
|
|
5045
|
+
if (!onNavigate) return;
|
|
5046
|
+
event.preventDefault();
|
|
5047
|
+
onNavigate("/contact");
|
|
5048
|
+
},
|
|
5049
|
+
children: T.askHuman
|
|
5050
|
+
}
|
|
5051
|
+
)
|
|
4568
5052
|
),
|
|
4569
5053
|
"."
|
|
4570
5054
|
] })
|
|
@@ -4572,7 +5056,16 @@ function AssistantBubble({
|
|
|
4572
5056
|
}
|
|
4573
5057
|
) : null,
|
|
4574
5058
|
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(
|
|
5059
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5060
|
+
ExploreAnswerBody,
|
|
5061
|
+
{
|
|
5062
|
+
strings: T,
|
|
5063
|
+
imageOrigins,
|
|
5064
|
+
enabled: completed,
|
|
5065
|
+
className: "boff-prose-sm text-card-foreground",
|
|
5066
|
+
children: visible
|
|
5067
|
+
}
|
|
5068
|
+
),
|
|
4576
5069
|
streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
|
|
4577
5070
|
] }) : null,
|
|
4578
5071
|
grounded && provenance.total > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ProvenanceChips, { strings: T, provenance }) : null,
|
|
@@ -4632,6 +5125,30 @@ function AssistantBubble({
|
|
|
4632
5125
|
feedback: message.feedback,
|
|
4633
5126
|
onSubmit: onFeedback
|
|
4634
5127
|
}
|
|
5128
|
+
) : null,
|
|
5129
|
+
showQuietEscalate ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5130
|
+
"button",
|
|
5131
|
+
{
|
|
5132
|
+
ref: escalateTriggerRef,
|
|
5133
|
+
"data-boff-explore": "escalate-open",
|
|
5134
|
+
type: "button",
|
|
5135
|
+
"aria-expanded": escalateOpen,
|
|
5136
|
+
"aria-controls": escalateOpen ? escalateFormId : void 0,
|
|
5137
|
+
disabled: escalateBusy,
|
|
5138
|
+
onClick: () => setEscalateOpen((open) => !open),
|
|
5139
|
+
className: "rounded-sm text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
5140
|
+
children: T.escalateOpen
|
|
5141
|
+
}
|
|
5142
|
+
) }) : null,
|
|
5143
|
+
escalateOpen && onEscalate ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5144
|
+
EscalateForm,
|
|
5145
|
+
{
|
|
5146
|
+
strings: T,
|
|
5147
|
+
formId: escalateFormId,
|
|
5148
|
+
onEscalate,
|
|
5149
|
+
onClose: closeEscalate,
|
|
5150
|
+
onBusyChange: handleEscalateBusy
|
|
5151
|
+
}
|
|
4635
5152
|
) : null
|
|
4636
5153
|
] })
|
|
4637
5154
|
]
|
|
@@ -4857,7 +5374,7 @@ function ConceptStrip({
|
|
|
4857
5374
|
"div",
|
|
4858
5375
|
{
|
|
4859
5376
|
"data-boff-explore": "concept-strip-block",
|
|
4860
|
-
className: "boff-explore-rise boff-explore-rise-
|
|
5377
|
+
className: "boff-explore-rise boff-explore-rise-3 mt-6",
|
|
4861
5378
|
children: [
|
|
4862
5379
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: T.conceptStripTitle }),
|
|
4863
5380
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5047,15 +5564,6 @@ function WelcomeState({
|
|
|
5047
5564
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-4 text-balance break-words text-2xl font-bold tracking-tight sm:text-4xl", children: title }),
|
|
5048
5565
|
/* @__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 })
|
|
5049
5566
|
] }),
|
|
5050
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5051
|
-
ConceptStrip,
|
|
5052
|
-
{
|
|
5053
|
-
strings: T,
|
|
5054
|
-
highlights: conceptHighlights,
|
|
5055
|
-
onAsk: onPrompt,
|
|
5056
|
-
disabled
|
|
5057
|
-
}
|
|
5058
|
-
),
|
|
5059
5567
|
/* @__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(
|
|
5060
5568
|
"li",
|
|
5061
5569
|
{
|
|
@@ -5091,7 +5599,16 @@ function WelcomeState({
|
|
|
5091
5599
|
onFocus
|
|
5092
5600
|
}
|
|
5093
5601
|
)
|
|
5094
|
-
] }) : null
|
|
5602
|
+
] }) : null,
|
|
5603
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5604
|
+
ConceptStrip,
|
|
5605
|
+
{
|
|
5606
|
+
strings: T,
|
|
5607
|
+
highlights: conceptHighlights,
|
|
5608
|
+
onAsk: onPrompt,
|
|
5609
|
+
disabled
|
|
5610
|
+
}
|
|
5611
|
+
)
|
|
5095
5612
|
] });
|
|
5096
5613
|
}
|
|
5097
5614
|
function ExplorePage({
|
|
@@ -5138,6 +5655,8 @@ function ExplorePage({
|
|
|
5138
5655
|
clearHistory,
|
|
5139
5656
|
recordClick,
|
|
5140
5657
|
submitFeedback,
|
|
5658
|
+
escalate,
|
|
5659
|
+
canEscalate,
|
|
5141
5660
|
createShareLink,
|
|
5142
5661
|
isSharedView
|
|
5143
5662
|
} = useExploreChat({
|
|
@@ -5581,6 +6100,7 @@ function ExplorePage({
|
|
|
5581
6100
|
),
|
|
5582
6101
|
/* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
|
|
5583
6102
|
/* @__PURE__ */ jsxRuntime.jsx(ExploreStyles, {}),
|
|
6103
|
+
/* @__PURE__ */ jsxRuntime.jsx(ExploreAnswerStyles, {}),
|
|
5584
6104
|
!showWelcome ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 border-b border-border px-4 py-2.5", children: [
|
|
5585
6105
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5586
6106
|
"span",
|
|
@@ -5759,7 +6279,9 @@ function ExplorePage({
|
|
|
5759
6279
|
anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
|
|
5760
6280
|
onFollow: (kind, url) => recordClick(message.id, kind, url),
|
|
5761
6281
|
onFollowUp: sendPrompt,
|
|
5762
|
-
onFeedback: isSharedView ? void 0 : (rating, detail) => submitFeedback(message.id, rating, detail)
|
|
6282
|
+
onFeedback: isSharedView ? void 0 : (rating, detail) => submitFeedback(message.id, rating, detail),
|
|
6283
|
+
onEscalate: canEscalate ? escalate : void 0,
|
|
6284
|
+
isLatestAnswer: message.id === latestAssistantId
|
|
5763
6285
|
},
|
|
5764
6286
|
message.id
|
|
5765
6287
|
)
|
|
@@ -5866,10 +6388,19 @@ function ExplorePage({
|
|
|
5866
6388
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5867
6389
|
"div",
|
|
5868
6390
|
{
|
|
6391
|
+
"data-boff-explore": "composer-field",
|
|
5869
6392
|
className: cn(
|
|
5870
6393
|
"flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
|
|
5871
6394
|
busy && "boff-explore-busy",
|
|
5872
|
-
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
|
|
6395
|
+
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring",
|
|
6396
|
+
// The halo is an invitation, and an invitation is only useful before it has
|
|
6397
|
+
// been accepted: while the conversation is empty, the composer is usable, and
|
|
6398
|
+
// nothing has been typed. Once any of those stops being true the class goes
|
|
6399
|
+
// and the box is an ordinary card again. `-quiet` keeps the ring but stops the
|
|
6400
|
+
// movement the moment there are characters in the field, so it does not pulse
|
|
6401
|
+
// at someone who is mid-sentence.
|
|
6402
|
+
showWelcome && !composerDisabled && !overLimit && "boff-explore-invite",
|
|
6403
|
+
showWelcome && draft.length > 0 && "boff-explore-invite-quiet"
|
|
5873
6404
|
),
|
|
5874
6405
|
children: [
|
|
5875
6406
|
/* @__PURE__ */ jsxRuntime.jsx(
|