@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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { E as ExploreProduct, a as ExploreReference } from './explore-types-B5sdOC5M.mjs';
|
|
3
|
+
import { Components } from 'react-markdown';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The answer stylesheet as an element. Safe to mount more than once — the rules are
|
|
7
|
+
* idempotent — so a page that shows answers and nothing else still gets them.
|
|
8
|
+
*/
|
|
9
|
+
declare function ExploreAnswerStyles(): react.JSX.Element;
|
|
10
|
+
/**
|
|
11
|
+
* Origins an answer may embed images from: every catalog product's website, apex and
|
|
12
|
+
* `www.` alike (Botlit ships on www.botlit.ai, everything else on the apex, and either can
|
|
13
|
+
* appear in the corpus). https only.
|
|
14
|
+
*/
|
|
15
|
+
declare function inlineImageOrigins(products: readonly Pick<ExploreProduct, "website">[]): Set<string>;
|
|
16
|
+
/**
|
|
17
|
+
* True when `src` is an https URL on a catalog product site. The backend already strips
|
|
18
|
+
* every image it did not put in the answer; this is the browser's own check, so a model
|
|
19
|
+
* that slips an arbitrary URL past it still cannot make the visitor's browser fetch it.
|
|
20
|
+
*/
|
|
21
|
+
declare function isInlineImageAllowed(src: unknown, origins: ReadonlySet<string>): src is string;
|
|
22
|
+
/**
|
|
23
|
+
* An answer as READABLE TEXT, for the clipboard.
|
|
24
|
+
*
|
|
25
|
+
* What counts as "our internal markup artefacts", and why each choice:
|
|
26
|
+
*
|
|
27
|
+
* - **Image embeds go.** `` is a picture this
|
|
28
|
+
* page injected into the answer; pasted into an email it is a URL and an alt string, and
|
|
29
|
+
* neither is the answer. The caption line the backend writes under it is ordinary prose and
|
|
30
|
+
* stays.
|
|
31
|
+
* - **Links become `label (url)`.** The destination is information the reader may need; the
|
|
32
|
+
* bracket-paren syntax is not. Dropping the URL would quietly strip the page it came from.
|
|
33
|
+
* - **Citation markers `[3]` stay.** They are the only attribution the copied text carries,
|
|
34
|
+
* and they are the answer's own — not something the UI added. The sources they point at are
|
|
35
|
+
* listed right next to the button that copied this.
|
|
36
|
+
* - **Emphasis, headings, blockquote and rule markers go**, because `**` and `###` are
|
|
37
|
+
* instructions to a renderer, not words.
|
|
38
|
+
* - **A table keeps its pipes.** Pasted into a README, an issue or a chat it is a table
|
|
39
|
+
* again; flattened into sentences it is not.
|
|
40
|
+
* - **Fenced code is verbatim**, fences included, minus nothing but the fence lines
|
|
41
|
+
* themselves: stripping `*` or `_` inside a code block would corrupt the code.
|
|
42
|
+
*
|
|
43
|
+
* Built from `message.content` rather than from the rendered DOM: `textContent` on the bubble
|
|
44
|
+
* would fold every paragraph, list item and table cell into one run-on line.
|
|
45
|
+
*/
|
|
46
|
+
declare function answerPlainText(markdown: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* The same answer as something to SAY, for read-aloud.
|
|
49
|
+
*
|
|
50
|
+
* It shares every rule above but the two that are right for a paste and wrong for a voice:
|
|
51
|
+
*
|
|
52
|
+
* - **A link is its label.** The clipboard keeps `label (https://…)` because a pasted answer
|
|
53
|
+
* loses the page otherwise. Spoken, that is a URL spelled out character by character in
|
|
54
|
+
* the middle of a sentence — and the prompt asks for two to five links in every answer, so
|
|
55
|
+
* it is not an edge case. The sources are listed under the answer either way.
|
|
56
|
+
* - **A table is read as rows, not as pipes.** `| --- | --- |` is a line of punctuation and
|
|
57
|
+
* the pipes are column rules, neither of which is a word. Each row is read with its cells
|
|
58
|
+
* under their own column headings — the phone layout, out loud — so a plan list still says
|
|
59
|
+
* which number is the price. Comparisons and plan lists are exactly the answers the
|
|
60
|
+
* feature was built for (CONTRACT.md §7), which is what makes this worth a second mode.
|
|
61
|
+
*
|
|
62
|
+
* Everything else stays deliberately identical, including citation markers: they are the
|
|
63
|
+
* answer's own attribution in both, and a listener hearing "two" after a claim is hearing
|
|
64
|
+
* the same thing a reader sees.
|
|
65
|
+
*/
|
|
66
|
+
declare function answerSpeechText(markdown: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* The strings the markdown overrides render.
|
|
69
|
+
*
|
|
70
|
+
* Deliberately three fields rather than the whole `ExploreStrings`: the chat and a published
|
|
71
|
+
* answer page carry different chrome vocabularies, and these three are the only words the
|
|
72
|
+
* ANSWER itself needs. `ExploreStrings` satisfies this structurally, so the chat passes its
|
|
73
|
+
* own `T` unchanged.
|
|
74
|
+
*/
|
|
75
|
+
interface AnswerRendererStrings {
|
|
76
|
+
/** Accessible name of the scrollable box a table sits in. */
|
|
77
|
+
answerTable: string;
|
|
78
|
+
/** The badge laid over a concept illustration embedded in an answer. */
|
|
79
|
+
conceptIllustration: string;
|
|
80
|
+
/** Read out after a link that opens a new tab. */
|
|
81
|
+
opensInNewTab: string;
|
|
82
|
+
}
|
|
83
|
+
interface ExploreAnswerBodyProps {
|
|
84
|
+
/** The answer, exactly as the harness wrote it: GitHub-flavoured markdown. */
|
|
85
|
+
children: string;
|
|
86
|
+
/** The three strings the overrides render. `ExploreStrings` satisfies this. */
|
|
87
|
+
strings: AnswerRendererStrings;
|
|
88
|
+
/** Origins an inline image may come from — see {@link inlineImageOrigins}. */
|
|
89
|
+
imageOrigins: ReadonlySet<string>;
|
|
90
|
+
/**
|
|
91
|
+
* False while the answer is still streaming, which suppresses its images: a partial is cut
|
|
92
|
+
* mid-token, so an image URL in it may be half-written, and the backend has not yet dropped
|
|
93
|
+
* the images it does not allow. A settled answer — every published one — is `true`.
|
|
94
|
+
*/
|
|
95
|
+
enabled?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Extra markdown overrides, merged OVER the shared ones.
|
|
98
|
+
*
|
|
99
|
+
* Pass a module-level constant, never an object built during render: react-markdown uses an
|
|
100
|
+
* override as the element TYPE, so a component re-created on every render remounts
|
|
101
|
+
* everything under it — an image would be fetched again, and one that had been hidden for
|
|
102
|
+
* failing to load would come back.
|
|
103
|
+
*/
|
|
104
|
+
components?: Components;
|
|
105
|
+
/** Extra classes for the prose wrapper. */
|
|
106
|
+
className?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* An answer, rendered.
|
|
110
|
+
*
|
|
111
|
+
* The wrapper carries `boff-prose` and `boff-explore-body`: the first styles ordinary
|
|
112
|
+
* markdown, the second is what every rule in {@link EXPLORE_ANSWER_CSS} is scoped to. A page
|
|
113
|
+
* that renders this must also mount `<ProseStyles />` and `<ExploreAnswerStyles />`, or a
|
|
114
|
+
* wide table has nothing holding it inside the page.
|
|
115
|
+
*
|
|
116
|
+
* The set of images that failed to load is held HERE rather than in each image, so a source
|
|
117
|
+
* that 404s stays hidden even when the markdown re-renders from scratch underneath it.
|
|
118
|
+
*/
|
|
119
|
+
declare function ExploreAnswerBody({ children, strings, imageOrigins, enabled, components, className, }: ExploreAnswerBodyProps): react.JSX.Element;
|
|
120
|
+
|
|
121
|
+
interface ExplorePageProps {
|
|
122
|
+
/**
|
|
123
|
+
* SEO metadata. Every field is optional and each one falls back to a default
|
|
124
|
+
* derived from {@link ExplorePageProps.productSlug}, so a site that passes
|
|
125
|
+
* nothing still gets a correct head — see `defaultExploreSeo`.
|
|
126
|
+
*
|
|
127
|
+
* Pass this only to say something the default cannot. All 38 sites used to
|
|
128
|
+
* hard-code the same two sentences here, which is how every one of them ended
|
|
129
|
+
* up announcing "Explore Burdenoff products with AI" — product-first on a page
|
|
130
|
+
* whose whole purpose is to ask the visitor about their problem, and simply
|
|
131
|
+
* the wrong brand on 37 of them.
|
|
132
|
+
*/
|
|
133
|
+
seo?: {
|
|
134
|
+
title?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
keywords?: string;
|
|
137
|
+
image?: string;
|
|
138
|
+
url?: string;
|
|
139
|
+
};
|
|
140
|
+
/** Slug of the product site this page lives on — a soft hint to the harness. */
|
|
141
|
+
productSlug?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Example prompts shown when `exploreCatalog` supplies none. Resolution
|
|
144
|
+
* order: catalog → this prop → the SDK's built-in list.
|
|
145
|
+
*/
|
|
146
|
+
examplePrompts?: string[];
|
|
147
|
+
/** Extra classes for the page root. */
|
|
148
|
+
className?: string;
|
|
149
|
+
/**
|
|
150
|
+
* `viewport` (default) pins the chat to the viewport with an internally
|
|
151
|
+
* scrolling transcript — the right feel for a dedicated `/explore` route.
|
|
152
|
+
* `auto` lets the page grow, for embedding inside a longer page.
|
|
153
|
+
*
|
|
154
|
+
* `viewport` also means "this component IS the page": only in that mode does it take over
|
|
155
|
+
* `history.scrollRestoration` and correct a restored scroll offset that has parked the
|
|
156
|
+
* page in the site footer. An `auto` embed never touches its host page's scrolling.
|
|
157
|
+
*/
|
|
158
|
+
heightMode?: "viewport" | "auto";
|
|
159
|
+
/**
|
|
160
|
+
* Chrome to subtract in `viewport` mode — the site header height. A number is treated as
|
|
161
|
+
* pixels. Default `0`.
|
|
162
|
+
*
|
|
163
|
+
* Pre-measurement fallback ONLY. Once mounted the pane measures its own top, so an
|
|
164
|
+
* approximate value is fine and the keyboard case is handled; see `measurePane` in
|
|
165
|
+
* `ExplorePage`.
|
|
166
|
+
*/
|
|
167
|
+
viewportOffset?: string | number;
|
|
168
|
+
/** SPA navigation for internal CTA paths. Falls back to a plain link. */
|
|
169
|
+
onNavigate?: (path: string) => void;
|
|
170
|
+
/** Fired with the message text every time a send is accepted. */
|
|
171
|
+
onSend?: (message: string) => void;
|
|
172
|
+
/** Overrides `exploreCatalog.welcomeTitle` — also the page `<h1>`. */
|
|
173
|
+
welcomeTitle?: string;
|
|
174
|
+
/** Overrides `exploreCatalog.welcomeBody`. */
|
|
175
|
+
welcomeBody?: string;
|
|
176
|
+
/**
|
|
177
|
+
* BCP-47 locale to render in. Defaults to the document's own `lang`, then the browser.
|
|
178
|
+
* Only the six the products ship are recognised (en, zh, hi, es, ar, ta); anything else
|
|
179
|
+
* falls back to English.
|
|
180
|
+
*/
|
|
181
|
+
locale?: string;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The product's own logo, derived from the reference's origin.
|
|
185
|
+
*
|
|
186
|
+
* Every Burdenoff product site serves `/favicon.svg`, so the logo needs no registry and no
|
|
187
|
+
* bundled assets — the reference URL already names the product. Deliberately NOT
|
|
188
|
+
* `/favicon-512.png`: that path returns 200 with `text/html` because the SPA fallback
|
|
189
|
+
* answers unknown paths, so it would render as a broken image rather than 404 into the
|
|
190
|
+
* fallback below.
|
|
191
|
+
*/
|
|
192
|
+
declare function logoUrlOf(url: string): string | null;
|
|
193
|
+
/**
|
|
194
|
+
* The kinds of page an answer can cite.
|
|
195
|
+
*
|
|
196
|
+
* `PAGE` is the catch-all for a catalog site's own marketing pages, so the list is total for
|
|
197
|
+
* anything on one of our origins; a reference that is NOT on a catalog origin gets no kind at
|
|
198
|
+
* all (see `referenceKindOf`).
|
|
199
|
+
*/
|
|
200
|
+
type ExploreReferenceKind = "CONCEPT" | "DOCS" | "PRICING" | "USE_CASE" | "PAGE";
|
|
201
|
+
/**
|
|
202
|
+
* What KIND of page a reference points at, decided from its URL alone.
|
|
203
|
+
*
|
|
204
|
+
* Deliberately not from the title: titles are model-written prose in six languages, so
|
|
205
|
+
* "Pricing" in one answer is "Precios" in the next and "Plans and pricing" in the one after.
|
|
206
|
+
* The URL is the only part of a reference nobody paraphrases.
|
|
207
|
+
*
|
|
208
|
+
* Reasoned against the catalog origins exactly as `isInlineImageAllowed` is: a URL that is
|
|
209
|
+
* not on a product site we list gets `null` rather than a guessed label, because the whole
|
|
210
|
+
* value of the row is that it says what the evidence IS.
|
|
211
|
+
*/
|
|
212
|
+
declare function referenceKindOf(url: string, origins: ReadonlySet<string>): ExploreReferenceKind | null;
|
|
213
|
+
/**
|
|
214
|
+
* Apex origin → the product's display name, built from the catalog the same way
|
|
215
|
+
* `inlineImageOrigins` builds its allow-list.
|
|
216
|
+
*
|
|
217
|
+
* The display name matters: `ExploreReference.product` carries the slug, so a row built from
|
|
218
|
+
* it says "vibecontrols" next to a page that calls itself VibeControls everywhere else.
|
|
219
|
+
*/
|
|
220
|
+
declare function productNamesByOrigin(products: readonly Pick<ExploreProduct, "name" | "website">[]): Map<string, string>;
|
|
221
|
+
/** The shape of one answer's evidence, as the chips render it. */
|
|
222
|
+
interface ExploreProvenance {
|
|
223
|
+
/** Every reference the answer cites, including any we could not classify. */
|
|
224
|
+
total: number;
|
|
225
|
+
/** Kinds present, in `REFERENCE_KIND_ORDER`, with how many sources each covers. */
|
|
226
|
+
kinds: Array<{
|
|
227
|
+
kind: ExploreReferenceKind;
|
|
228
|
+
count: number;
|
|
229
|
+
}>;
|
|
230
|
+
/** Product names, in the order the answer first cited them. */
|
|
231
|
+
products: string[];
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Summarise what an answer is grounded in. Pure, and derived entirely from `references` —
|
|
235
|
+
* there is no backend field behind this row.
|
|
236
|
+
*/
|
|
237
|
+
declare function summariseReferences(references: readonly ExploreReference[], origins: ReadonlySet<string>, productNames: ReadonlyMap<string, string>): ExploreProvenance;
|
|
238
|
+
declare function ExplorePage({ seo, productSlug, examplePrompts, className, heightMode, viewportOffset, onNavigate, onSend, welcomeTitle, welcomeBody, locale, }: ExplorePageProps): react.JSX.Element;
|
|
239
|
+
|
|
240
|
+
export { type AnswerRendererStrings as A, ExploreAnswerBody as E, type ExploreAnswerBodyProps as a, ExploreAnswerStyles as b, ExplorePage as c, type ExplorePageProps as d, answerPlainText as e, answerSpeechText as f, isInlineImageAllowed as g, type ExploreProvenance as h, inlineImageOrigins as i, type ExploreReferenceKind as j, logoUrlOf as l, productNamesByOrigin as p, referenceKindOf as r, summariseReferences as s };
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { E as ExploreProduct, a as ExploreReference } from './explore-types-B5sdOC5M.js';
|
|
3
|
+
import { Components } from 'react-markdown';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The answer stylesheet as an element. Safe to mount more than once — the rules are
|
|
7
|
+
* idempotent — so a page that shows answers and nothing else still gets them.
|
|
8
|
+
*/
|
|
9
|
+
declare function ExploreAnswerStyles(): react.JSX.Element;
|
|
10
|
+
/**
|
|
11
|
+
* Origins an answer may embed images from: every catalog product's website, apex and
|
|
12
|
+
* `www.` alike (Botlit ships on www.botlit.ai, everything else on the apex, and either can
|
|
13
|
+
* appear in the corpus). https only.
|
|
14
|
+
*/
|
|
15
|
+
declare function inlineImageOrigins(products: readonly Pick<ExploreProduct, "website">[]): Set<string>;
|
|
16
|
+
/**
|
|
17
|
+
* True when `src` is an https URL on a catalog product site. The backend already strips
|
|
18
|
+
* every image it did not put in the answer; this is the browser's own check, so a model
|
|
19
|
+
* that slips an arbitrary URL past it still cannot make the visitor's browser fetch it.
|
|
20
|
+
*/
|
|
21
|
+
declare function isInlineImageAllowed(src: unknown, origins: ReadonlySet<string>): src is string;
|
|
22
|
+
/**
|
|
23
|
+
* An answer as READABLE TEXT, for the clipboard.
|
|
24
|
+
*
|
|
25
|
+
* What counts as "our internal markup artefacts", and why each choice:
|
|
26
|
+
*
|
|
27
|
+
* - **Image embeds go.** `` is a picture this
|
|
28
|
+
* page injected into the answer; pasted into an email it is a URL and an alt string, and
|
|
29
|
+
* neither is the answer. The caption line the backend writes under it is ordinary prose and
|
|
30
|
+
* stays.
|
|
31
|
+
* - **Links become `label (url)`.** The destination is information the reader may need; the
|
|
32
|
+
* bracket-paren syntax is not. Dropping the URL would quietly strip the page it came from.
|
|
33
|
+
* - **Citation markers `[3]` stay.** They are the only attribution the copied text carries,
|
|
34
|
+
* and they are the answer's own — not something the UI added. The sources they point at are
|
|
35
|
+
* listed right next to the button that copied this.
|
|
36
|
+
* - **Emphasis, headings, blockquote and rule markers go**, because `**` and `###` are
|
|
37
|
+
* instructions to a renderer, not words.
|
|
38
|
+
* - **A table keeps its pipes.** Pasted into a README, an issue or a chat it is a table
|
|
39
|
+
* again; flattened into sentences it is not.
|
|
40
|
+
* - **Fenced code is verbatim**, fences included, minus nothing but the fence lines
|
|
41
|
+
* themselves: stripping `*` or `_` inside a code block would corrupt the code.
|
|
42
|
+
*
|
|
43
|
+
* Built from `message.content` rather than from the rendered DOM: `textContent` on the bubble
|
|
44
|
+
* would fold every paragraph, list item and table cell into one run-on line.
|
|
45
|
+
*/
|
|
46
|
+
declare function answerPlainText(markdown: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* The same answer as something to SAY, for read-aloud.
|
|
49
|
+
*
|
|
50
|
+
* It shares every rule above but the two that are right for a paste and wrong for a voice:
|
|
51
|
+
*
|
|
52
|
+
* - **A link is its label.** The clipboard keeps `label (https://…)` because a pasted answer
|
|
53
|
+
* loses the page otherwise. Spoken, that is a URL spelled out character by character in
|
|
54
|
+
* the middle of a sentence — and the prompt asks for two to five links in every answer, so
|
|
55
|
+
* it is not an edge case. The sources are listed under the answer either way.
|
|
56
|
+
* - **A table is read as rows, not as pipes.** `| --- | --- |` is a line of punctuation and
|
|
57
|
+
* the pipes are column rules, neither of which is a word. Each row is read with its cells
|
|
58
|
+
* under their own column headings — the phone layout, out loud — so a plan list still says
|
|
59
|
+
* which number is the price. Comparisons and plan lists are exactly the answers the
|
|
60
|
+
* feature was built for (CONTRACT.md §7), which is what makes this worth a second mode.
|
|
61
|
+
*
|
|
62
|
+
* Everything else stays deliberately identical, including citation markers: they are the
|
|
63
|
+
* answer's own attribution in both, and a listener hearing "two" after a claim is hearing
|
|
64
|
+
* the same thing a reader sees.
|
|
65
|
+
*/
|
|
66
|
+
declare function answerSpeechText(markdown: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* The strings the markdown overrides render.
|
|
69
|
+
*
|
|
70
|
+
* Deliberately three fields rather than the whole `ExploreStrings`: the chat and a published
|
|
71
|
+
* answer page carry different chrome vocabularies, and these three are the only words the
|
|
72
|
+
* ANSWER itself needs. `ExploreStrings` satisfies this structurally, so the chat passes its
|
|
73
|
+
* own `T` unchanged.
|
|
74
|
+
*/
|
|
75
|
+
interface AnswerRendererStrings {
|
|
76
|
+
/** Accessible name of the scrollable box a table sits in. */
|
|
77
|
+
answerTable: string;
|
|
78
|
+
/** The badge laid over a concept illustration embedded in an answer. */
|
|
79
|
+
conceptIllustration: string;
|
|
80
|
+
/** Read out after a link that opens a new tab. */
|
|
81
|
+
opensInNewTab: string;
|
|
82
|
+
}
|
|
83
|
+
interface ExploreAnswerBodyProps {
|
|
84
|
+
/** The answer, exactly as the harness wrote it: GitHub-flavoured markdown. */
|
|
85
|
+
children: string;
|
|
86
|
+
/** The three strings the overrides render. `ExploreStrings` satisfies this. */
|
|
87
|
+
strings: AnswerRendererStrings;
|
|
88
|
+
/** Origins an inline image may come from — see {@link inlineImageOrigins}. */
|
|
89
|
+
imageOrigins: ReadonlySet<string>;
|
|
90
|
+
/**
|
|
91
|
+
* False while the answer is still streaming, which suppresses its images: a partial is cut
|
|
92
|
+
* mid-token, so an image URL in it may be half-written, and the backend has not yet dropped
|
|
93
|
+
* the images it does not allow. A settled answer — every published one — is `true`.
|
|
94
|
+
*/
|
|
95
|
+
enabled?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Extra markdown overrides, merged OVER the shared ones.
|
|
98
|
+
*
|
|
99
|
+
* Pass a module-level constant, never an object built during render: react-markdown uses an
|
|
100
|
+
* override as the element TYPE, so a component re-created on every render remounts
|
|
101
|
+
* everything under it — an image would be fetched again, and one that had been hidden for
|
|
102
|
+
* failing to load would come back.
|
|
103
|
+
*/
|
|
104
|
+
components?: Components;
|
|
105
|
+
/** Extra classes for the prose wrapper. */
|
|
106
|
+
className?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* An answer, rendered.
|
|
110
|
+
*
|
|
111
|
+
* The wrapper carries `boff-prose` and `boff-explore-body`: the first styles ordinary
|
|
112
|
+
* markdown, the second is what every rule in {@link EXPLORE_ANSWER_CSS} is scoped to. A page
|
|
113
|
+
* that renders this must also mount `<ProseStyles />` and `<ExploreAnswerStyles />`, or a
|
|
114
|
+
* wide table has nothing holding it inside the page.
|
|
115
|
+
*
|
|
116
|
+
* The set of images that failed to load is held HERE rather than in each image, so a source
|
|
117
|
+
* that 404s stays hidden even when the markdown re-renders from scratch underneath it.
|
|
118
|
+
*/
|
|
119
|
+
declare function ExploreAnswerBody({ children, strings, imageOrigins, enabled, components, className, }: ExploreAnswerBodyProps): react.JSX.Element;
|
|
120
|
+
|
|
121
|
+
interface ExplorePageProps {
|
|
122
|
+
/**
|
|
123
|
+
* SEO metadata. Every field is optional and each one falls back to a default
|
|
124
|
+
* derived from {@link ExplorePageProps.productSlug}, so a site that passes
|
|
125
|
+
* nothing still gets a correct head — see `defaultExploreSeo`.
|
|
126
|
+
*
|
|
127
|
+
* Pass this only to say something the default cannot. All 38 sites used to
|
|
128
|
+
* hard-code the same two sentences here, which is how every one of them ended
|
|
129
|
+
* up announcing "Explore Burdenoff products with AI" — product-first on a page
|
|
130
|
+
* whose whole purpose is to ask the visitor about their problem, and simply
|
|
131
|
+
* the wrong brand on 37 of them.
|
|
132
|
+
*/
|
|
133
|
+
seo?: {
|
|
134
|
+
title?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
keywords?: string;
|
|
137
|
+
image?: string;
|
|
138
|
+
url?: string;
|
|
139
|
+
};
|
|
140
|
+
/** Slug of the product site this page lives on — a soft hint to the harness. */
|
|
141
|
+
productSlug?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Example prompts shown when `exploreCatalog` supplies none. Resolution
|
|
144
|
+
* order: catalog → this prop → the SDK's built-in list.
|
|
145
|
+
*/
|
|
146
|
+
examplePrompts?: string[];
|
|
147
|
+
/** Extra classes for the page root. */
|
|
148
|
+
className?: string;
|
|
149
|
+
/**
|
|
150
|
+
* `viewport` (default) pins the chat to the viewport with an internally
|
|
151
|
+
* scrolling transcript — the right feel for a dedicated `/explore` route.
|
|
152
|
+
* `auto` lets the page grow, for embedding inside a longer page.
|
|
153
|
+
*
|
|
154
|
+
* `viewport` also means "this component IS the page": only in that mode does it take over
|
|
155
|
+
* `history.scrollRestoration` and correct a restored scroll offset that has parked the
|
|
156
|
+
* page in the site footer. An `auto` embed never touches its host page's scrolling.
|
|
157
|
+
*/
|
|
158
|
+
heightMode?: "viewport" | "auto";
|
|
159
|
+
/**
|
|
160
|
+
* Chrome to subtract in `viewport` mode — the site header height. A number is treated as
|
|
161
|
+
* pixels. Default `0`.
|
|
162
|
+
*
|
|
163
|
+
* Pre-measurement fallback ONLY. Once mounted the pane measures its own top, so an
|
|
164
|
+
* approximate value is fine and the keyboard case is handled; see `measurePane` in
|
|
165
|
+
* `ExplorePage`.
|
|
166
|
+
*/
|
|
167
|
+
viewportOffset?: string | number;
|
|
168
|
+
/** SPA navigation for internal CTA paths. Falls back to a plain link. */
|
|
169
|
+
onNavigate?: (path: string) => void;
|
|
170
|
+
/** Fired with the message text every time a send is accepted. */
|
|
171
|
+
onSend?: (message: string) => void;
|
|
172
|
+
/** Overrides `exploreCatalog.welcomeTitle` — also the page `<h1>`. */
|
|
173
|
+
welcomeTitle?: string;
|
|
174
|
+
/** Overrides `exploreCatalog.welcomeBody`. */
|
|
175
|
+
welcomeBody?: string;
|
|
176
|
+
/**
|
|
177
|
+
* BCP-47 locale to render in. Defaults to the document's own `lang`, then the browser.
|
|
178
|
+
* Only the six the products ship are recognised (en, zh, hi, es, ar, ta); anything else
|
|
179
|
+
* falls back to English.
|
|
180
|
+
*/
|
|
181
|
+
locale?: string;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The product's own logo, derived from the reference's origin.
|
|
185
|
+
*
|
|
186
|
+
* Every Burdenoff product site serves `/favicon.svg`, so the logo needs no registry and no
|
|
187
|
+
* bundled assets — the reference URL already names the product. Deliberately NOT
|
|
188
|
+
* `/favicon-512.png`: that path returns 200 with `text/html` because the SPA fallback
|
|
189
|
+
* answers unknown paths, so it would render as a broken image rather than 404 into the
|
|
190
|
+
* fallback below.
|
|
191
|
+
*/
|
|
192
|
+
declare function logoUrlOf(url: string): string | null;
|
|
193
|
+
/**
|
|
194
|
+
* The kinds of page an answer can cite.
|
|
195
|
+
*
|
|
196
|
+
* `PAGE` is the catch-all for a catalog site's own marketing pages, so the list is total for
|
|
197
|
+
* anything on one of our origins; a reference that is NOT on a catalog origin gets no kind at
|
|
198
|
+
* all (see `referenceKindOf`).
|
|
199
|
+
*/
|
|
200
|
+
type ExploreReferenceKind = "CONCEPT" | "DOCS" | "PRICING" | "USE_CASE" | "PAGE";
|
|
201
|
+
/**
|
|
202
|
+
* What KIND of page a reference points at, decided from its URL alone.
|
|
203
|
+
*
|
|
204
|
+
* Deliberately not from the title: titles are model-written prose in six languages, so
|
|
205
|
+
* "Pricing" in one answer is "Precios" in the next and "Plans and pricing" in the one after.
|
|
206
|
+
* The URL is the only part of a reference nobody paraphrases.
|
|
207
|
+
*
|
|
208
|
+
* Reasoned against the catalog origins exactly as `isInlineImageAllowed` is: a URL that is
|
|
209
|
+
* not on a product site we list gets `null` rather than a guessed label, because the whole
|
|
210
|
+
* value of the row is that it says what the evidence IS.
|
|
211
|
+
*/
|
|
212
|
+
declare function referenceKindOf(url: string, origins: ReadonlySet<string>): ExploreReferenceKind | null;
|
|
213
|
+
/**
|
|
214
|
+
* Apex origin → the product's display name, built from the catalog the same way
|
|
215
|
+
* `inlineImageOrigins` builds its allow-list.
|
|
216
|
+
*
|
|
217
|
+
* The display name matters: `ExploreReference.product` carries the slug, so a row built from
|
|
218
|
+
* it says "vibecontrols" next to a page that calls itself VibeControls everywhere else.
|
|
219
|
+
*/
|
|
220
|
+
declare function productNamesByOrigin(products: readonly Pick<ExploreProduct, "name" | "website">[]): Map<string, string>;
|
|
221
|
+
/** The shape of one answer's evidence, as the chips render it. */
|
|
222
|
+
interface ExploreProvenance {
|
|
223
|
+
/** Every reference the answer cites, including any we could not classify. */
|
|
224
|
+
total: number;
|
|
225
|
+
/** Kinds present, in `REFERENCE_KIND_ORDER`, with how many sources each covers. */
|
|
226
|
+
kinds: Array<{
|
|
227
|
+
kind: ExploreReferenceKind;
|
|
228
|
+
count: number;
|
|
229
|
+
}>;
|
|
230
|
+
/** Product names, in the order the answer first cited them. */
|
|
231
|
+
products: string[];
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Summarise what an answer is grounded in. Pure, and derived entirely from `references` —
|
|
235
|
+
* there is no backend field behind this row.
|
|
236
|
+
*/
|
|
237
|
+
declare function summariseReferences(references: readonly ExploreReference[], origins: ReadonlySet<string>, productNames: ReadonlyMap<string, string>): ExploreProvenance;
|
|
238
|
+
declare function ExplorePage({ seo, productSlug, examplePrompts, className, heightMode, viewportOffset, onNavigate, onSend, welcomeTitle, welcomeBody, locale, }: ExplorePageProps): react.JSX.Element;
|
|
239
|
+
|
|
240
|
+
export { type AnswerRendererStrings as A, ExploreAnswerBody as E, type ExploreAnswerBodyProps as a, ExploreAnswerStyles as b, ExplorePage as c, type ExplorePageProps as d, answerPlainText as e, answerSpeechText as f, isInlineImageAllowed as g, type ExploreProvenance as h, inlineImageOrigins as i, type ExploreReferenceKind as j, logoUrlOf as l, productNamesByOrigin as p, referenceKindOf as r, summariseReferences as s };
|
|
@@ -43,7 +43,7 @@ type ExploreCtaKind = "CONTACT" | "PARTNERS" | "PRICING" | "SERVICES" | "WAITLIS
|
|
|
43
43
|
* gateway emits when it rate-limits at the edge; the SDK client also produces
|
|
44
44
|
* `TIMEOUT` / `NETWORK_ERROR` for transport failures.
|
|
45
45
|
*/
|
|
46
|
-
type ExploreErrorCode = "EXPLORE_DISABLED" | "RATE_LIMITED" | "EXPLORE_GLOBAL_LIMIT" | "CAPTCHA_REQUIRED" | "CAPTCHA_FAILED" | "MESSAGE_TOO_LONG" | "MESSAGE_EMPTY" | "CONVERSATION_LIMIT" | "CONVERSATION_BUSY" | "UPSTREAM_UNAVAILABLE" | "NOT_FOUND" | "HTTP_429" | "TIMEOUT" | "NETWORK_ERROR";
|
|
46
|
+
type ExploreErrorCode = "EXPLORE_DISABLED" | "RATE_LIMITED" | "EXPLORE_GLOBAL_LIMIT" | "CAPTCHA_REQUIRED" | "CAPTCHA_FAILED" | "MESSAGE_TOO_LONG" | "MESSAGE_EMPTY" | "CONVERSATION_LIMIT" | "CONVERSATION_BUSY" | "UPSTREAM_UNAVAILABLE" | "NOT_FOUND" | "INVALID_EMAIL" | "ESCALATION_FAILED" | "HTTP_429" | "TIMEOUT" | "NETWORK_ERROR";
|
|
47
47
|
/** `type ExploreReference` — a cited source page for an answer. */
|
|
48
48
|
interface ExploreReference {
|
|
49
49
|
url: string;
|
|
@@ -276,5 +276,78 @@ interface ExploreMessageQueryData {
|
|
|
276
276
|
interface SendExploreMessageMutationData {
|
|
277
277
|
sendExploreMessage: SendExploreMessageResult;
|
|
278
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* `input EscalateExploreConversationInput`.
|
|
281
|
+
*
|
|
282
|
+
* `email` is the only thing the visitor is asked for. It is **personal data**: it belongs in
|
|
283
|
+
* this input and nowhere else — never in `localStorage`, never in an `ExploreMessage`, never
|
|
284
|
+
* in an analytics call. See the note on {@link ESCALATE_EXPLORE_CONVERSATION_MUTATION}.
|
|
285
|
+
*/
|
|
286
|
+
interface EscalateExploreConversationInput {
|
|
287
|
+
/** The thread being handed over. A share (`?c=`) secret is NOT one. */
|
|
288
|
+
conversationToken: string;
|
|
289
|
+
/** Validated strictly server-side; a bad address comes back as `INVALID_EMAIL`. */
|
|
290
|
+
email: string;
|
|
291
|
+
/** Optional free text. Hard-capped server-side at 2000 and TRUNCATED, never rejected. */
|
|
292
|
+
note?: string | null;
|
|
293
|
+
/** Only when `exploreCatalog.captchaRequired`. Same v3 action as a chat message. */
|
|
294
|
+
recaptchaToken?: string;
|
|
295
|
+
/** `PlatformProduct` id of the site, so the ticket reaches that product's support inbox. */
|
|
296
|
+
productId?: string;
|
|
297
|
+
}
|
|
298
|
+
/** `type ExploreEscalation` — the handover on record, as the SERVER holds it. */
|
|
299
|
+
interface ExploreEscalation {
|
|
300
|
+
/**
|
|
301
|
+
* Where the reply is actually going.
|
|
302
|
+
*
|
|
303
|
+
* On a repeat submit this is the address the FIRST handover used, not the one just typed
|
|
304
|
+
* into the box — so this is the value to show back, never the local draft.
|
|
305
|
+
*/
|
|
306
|
+
email: string;
|
|
307
|
+
createdAt: string;
|
|
308
|
+
}
|
|
309
|
+
/** `type EscalateExploreConversationResult`. */
|
|
310
|
+
interface EscalateExploreConversationResult {
|
|
311
|
+
/**
|
|
312
|
+
* False when a handover already existed — the double-clicked button, the retried request,
|
|
313
|
+
* the reloaded page.
|
|
314
|
+
*
|
|
315
|
+
* **Not an error.** The conversation is with a human either way, and the UI must read as
|
|
316
|
+
* success on both values. The server enforces one handover per conversation with a UNIQUE
|
|
317
|
+
* index rather than a check-then-insert, so two concurrent submits produce one ticket and
|
|
318
|
+
* one of them gets this.
|
|
319
|
+
*/
|
|
320
|
+
created: boolean;
|
|
321
|
+
escalation: ExploreEscalation;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Hand this conversation to a person.
|
|
325
|
+
*
|
|
326
|
+
* Awaited and NOT fire-and-forget (unlike {@link RECORD_EXPLORE_CLICK_MUTATION}): it is the
|
|
327
|
+
* one explore write that throws, because it ends with a visitor being told a person has
|
|
328
|
+
* their question. "We passed it on" must never be shown for a handover that did not happen.
|
|
329
|
+
*
|
|
330
|
+
* **The address the visitor types is personal data.** It travels in these variables and
|
|
331
|
+
* stops there. Do not persist it, do not fold it into the transcript (`messages` is what the
|
|
332
|
+
* hook writes to localStorage), and do not pass it to `onSend` or any analytics call — the
|
|
333
|
+
* server already has everything a support agent needs.
|
|
334
|
+
*/
|
|
335
|
+
declare const ESCALATE_EXPLORE_CONVERSATION_MUTATION = "\n mutation EscalateExploreConversation(\n $input: EscalateExploreConversationInput!\n ) {\n escalateExploreConversation(input: $input) {\n created\n escalation {\n email\n createdAt\n }\n }\n }\n";
|
|
336
|
+
/**
|
|
337
|
+
* How many characters the note field accepts before it stops taking more.
|
|
338
|
+
*
|
|
339
|
+
* Mirrors the server's hard cap so the box does not invite text it will silently truncate.
|
|
340
|
+
* Same status as {@link EXPLORE_FEEDBACK_COMMENT_SOFT_MAX}: a courtesy, not the limit that
|
|
341
|
+
* holds.
|
|
342
|
+
*/
|
|
343
|
+
declare const EXPLORE_ESCALATION_NOTE_SOFT_MAX = 2000;
|
|
344
|
+
/**
|
|
345
|
+
* Whether `value` is worth sending to the mutation at all.
|
|
346
|
+
*
|
|
347
|
+
* A local pre-flight, never a promise of deliverability. Keep it a SUBSET of what the server
|
|
348
|
+
* accepts: a client that rejects something the server would have taken costs the visitor
|
|
349
|
+
* their answer with nobody able to see why.
|
|
350
|
+
*/
|
|
351
|
+
declare function looksLikeDeliverableEmail(value: string): boolean;
|
|
279
352
|
|
|
280
|
-
export { type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreReference as a, type ExploreLimits as b, type ExploreCatalog as c, type ExploreMessage as d, type ExploreFeedbackRating as e, type ExploreFeedbackReason as f,
|
|
353
|
+
export { looksLikeDeliverableEmail as A, type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreReference as a, type ExploreLimits as b, type ExploreCatalog as c, type ExploreMessage as d, type ExploreFeedbackRating as e, type ExploreFeedbackReason as f, ESCALATE_EXPLORE_CONVERSATION_MUTATION as g, EXPLORE_CATALOG_QUERY as h, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS as i, EXPLORE_ESCALATION_NOTE_SOFT_MAX as j, EXPLORE_MESSAGE_QUERY as k, type EscalateExploreConversationInput as l, type EscalateExploreConversationResult as m, type ExploreCatalogQueryData as n, type ExploreConceptHighlight as o, type ExploreConversation as p, type ExploreCtaKind as q, type ExploreCtaLink as r, type ExploreErrorCode as s, type ExploreEscalation as t, type ExploreMessageQueryData as u, type ExploreMessageRole as v, type ExploreMessageStatus as w, type SendExploreMessageInput as x, type SendExploreMessageMutationData as y, type SendExploreMessageResult as z };
|
|
@@ -43,7 +43,7 @@ type ExploreCtaKind = "CONTACT" | "PARTNERS" | "PRICING" | "SERVICES" | "WAITLIS
|
|
|
43
43
|
* gateway emits when it rate-limits at the edge; the SDK client also produces
|
|
44
44
|
* `TIMEOUT` / `NETWORK_ERROR` for transport failures.
|
|
45
45
|
*/
|
|
46
|
-
type ExploreErrorCode = "EXPLORE_DISABLED" | "RATE_LIMITED" | "EXPLORE_GLOBAL_LIMIT" | "CAPTCHA_REQUIRED" | "CAPTCHA_FAILED" | "MESSAGE_TOO_LONG" | "MESSAGE_EMPTY" | "CONVERSATION_LIMIT" | "CONVERSATION_BUSY" | "UPSTREAM_UNAVAILABLE" | "NOT_FOUND" | "HTTP_429" | "TIMEOUT" | "NETWORK_ERROR";
|
|
46
|
+
type ExploreErrorCode = "EXPLORE_DISABLED" | "RATE_LIMITED" | "EXPLORE_GLOBAL_LIMIT" | "CAPTCHA_REQUIRED" | "CAPTCHA_FAILED" | "MESSAGE_TOO_LONG" | "MESSAGE_EMPTY" | "CONVERSATION_LIMIT" | "CONVERSATION_BUSY" | "UPSTREAM_UNAVAILABLE" | "NOT_FOUND" | "INVALID_EMAIL" | "ESCALATION_FAILED" | "HTTP_429" | "TIMEOUT" | "NETWORK_ERROR";
|
|
47
47
|
/** `type ExploreReference` — a cited source page for an answer. */
|
|
48
48
|
interface ExploreReference {
|
|
49
49
|
url: string;
|
|
@@ -276,5 +276,78 @@ interface ExploreMessageQueryData {
|
|
|
276
276
|
interface SendExploreMessageMutationData {
|
|
277
277
|
sendExploreMessage: SendExploreMessageResult;
|
|
278
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* `input EscalateExploreConversationInput`.
|
|
281
|
+
*
|
|
282
|
+
* `email` is the only thing the visitor is asked for. It is **personal data**: it belongs in
|
|
283
|
+
* this input and nowhere else — never in `localStorage`, never in an `ExploreMessage`, never
|
|
284
|
+
* in an analytics call. See the note on {@link ESCALATE_EXPLORE_CONVERSATION_MUTATION}.
|
|
285
|
+
*/
|
|
286
|
+
interface EscalateExploreConversationInput {
|
|
287
|
+
/** The thread being handed over. A share (`?c=`) secret is NOT one. */
|
|
288
|
+
conversationToken: string;
|
|
289
|
+
/** Validated strictly server-side; a bad address comes back as `INVALID_EMAIL`. */
|
|
290
|
+
email: string;
|
|
291
|
+
/** Optional free text. Hard-capped server-side at 2000 and TRUNCATED, never rejected. */
|
|
292
|
+
note?: string | null;
|
|
293
|
+
/** Only when `exploreCatalog.captchaRequired`. Same v3 action as a chat message. */
|
|
294
|
+
recaptchaToken?: string;
|
|
295
|
+
/** `PlatformProduct` id of the site, so the ticket reaches that product's support inbox. */
|
|
296
|
+
productId?: string;
|
|
297
|
+
}
|
|
298
|
+
/** `type ExploreEscalation` — the handover on record, as the SERVER holds it. */
|
|
299
|
+
interface ExploreEscalation {
|
|
300
|
+
/**
|
|
301
|
+
* Where the reply is actually going.
|
|
302
|
+
*
|
|
303
|
+
* On a repeat submit this is the address the FIRST handover used, not the one just typed
|
|
304
|
+
* into the box — so this is the value to show back, never the local draft.
|
|
305
|
+
*/
|
|
306
|
+
email: string;
|
|
307
|
+
createdAt: string;
|
|
308
|
+
}
|
|
309
|
+
/** `type EscalateExploreConversationResult`. */
|
|
310
|
+
interface EscalateExploreConversationResult {
|
|
311
|
+
/**
|
|
312
|
+
* False when a handover already existed — the double-clicked button, the retried request,
|
|
313
|
+
* the reloaded page.
|
|
314
|
+
*
|
|
315
|
+
* **Not an error.** The conversation is with a human either way, and the UI must read as
|
|
316
|
+
* success on both values. The server enforces one handover per conversation with a UNIQUE
|
|
317
|
+
* index rather than a check-then-insert, so two concurrent submits produce one ticket and
|
|
318
|
+
* one of them gets this.
|
|
319
|
+
*/
|
|
320
|
+
created: boolean;
|
|
321
|
+
escalation: ExploreEscalation;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Hand this conversation to a person.
|
|
325
|
+
*
|
|
326
|
+
* Awaited and NOT fire-and-forget (unlike {@link RECORD_EXPLORE_CLICK_MUTATION}): it is the
|
|
327
|
+
* one explore write that throws, because it ends with a visitor being told a person has
|
|
328
|
+
* their question. "We passed it on" must never be shown for a handover that did not happen.
|
|
329
|
+
*
|
|
330
|
+
* **The address the visitor types is personal data.** It travels in these variables and
|
|
331
|
+
* stops there. Do not persist it, do not fold it into the transcript (`messages` is what the
|
|
332
|
+
* hook writes to localStorage), and do not pass it to `onSend` or any analytics call — the
|
|
333
|
+
* server already has everything a support agent needs.
|
|
334
|
+
*/
|
|
335
|
+
declare const ESCALATE_EXPLORE_CONVERSATION_MUTATION = "\n mutation EscalateExploreConversation(\n $input: EscalateExploreConversationInput!\n ) {\n escalateExploreConversation(input: $input) {\n created\n escalation {\n email\n createdAt\n }\n }\n }\n";
|
|
336
|
+
/**
|
|
337
|
+
* How many characters the note field accepts before it stops taking more.
|
|
338
|
+
*
|
|
339
|
+
* Mirrors the server's hard cap so the box does not invite text it will silently truncate.
|
|
340
|
+
* Same status as {@link EXPLORE_FEEDBACK_COMMENT_SOFT_MAX}: a courtesy, not the limit that
|
|
341
|
+
* holds.
|
|
342
|
+
*/
|
|
343
|
+
declare const EXPLORE_ESCALATION_NOTE_SOFT_MAX = 2000;
|
|
344
|
+
/**
|
|
345
|
+
* Whether `value` is worth sending to the mutation at all.
|
|
346
|
+
*
|
|
347
|
+
* A local pre-flight, never a promise of deliverability. Keep it a SUBSET of what the server
|
|
348
|
+
* accepts: a client that rejects something the server would have taken costs the visitor
|
|
349
|
+
* their answer with nobody able to see why.
|
|
350
|
+
*/
|
|
351
|
+
declare function looksLikeDeliverableEmail(value: string): boolean;
|
|
279
352
|
|
|
280
|
-
export { type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreReference as a, type ExploreLimits as b, type ExploreCatalog as c, type ExploreMessage as d, type ExploreFeedbackRating as e, type ExploreFeedbackReason as f,
|
|
353
|
+
export { looksLikeDeliverableEmail as A, type ExploreProduct as E, SEND_EXPLORE_MESSAGE_MUTATION as S, type ExploreReference as a, type ExploreLimits as b, type ExploreCatalog as c, type ExploreMessage as d, type ExploreFeedbackRating as e, type ExploreFeedbackReason as f, ESCALATE_EXPLORE_CONVERSATION_MUTATION as g, EXPLORE_CATALOG_QUERY as h, EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS as i, EXPLORE_ESCALATION_NOTE_SOFT_MAX as j, EXPLORE_MESSAGE_QUERY as k, type EscalateExploreConversationInput as l, type EscalateExploreConversationResult as m, type ExploreCatalogQueryData as n, type ExploreConceptHighlight as o, type ExploreConversation as p, type ExploreCtaKind as q, type ExploreCtaLink as r, type ExploreErrorCode as s, type ExploreEscalation as t, type ExploreMessageQueryData as u, type ExploreMessageRole as v, type ExploreMessageStatus as w, type SendExploreMessageInput as x, type SendExploreMessageMutationData as y, type SendExploreMessageResult as z };
|