@burdenoff/website-sdk 2026.925.1 → 2026.925.3

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.
Files changed (39) hide show
  1. package/dist/client/index.js +2 -0
  2. package/dist/client/index.js.map +1 -1
  3. package/dist/client/index.mjs +2 -0
  4. package/dist/client/index.mjs.map +1 -1
  5. package/dist/components/explore.d.mts +1 -1
  6. package/dist/components/explore.d.ts +1 -1
  7. package/dist/components/explore.js +1049 -502
  8. package/dist/components/explore.js.map +1 -1
  9. package/dist/components/explore.mjs +1051 -505
  10. package/dist/components/explore.mjs.map +1 -1
  11. package/dist/components/partners.js +3 -2
  12. package/dist/components/partners.js.map +1 -1
  13. package/dist/components/partners.mjs +3 -2
  14. package/dist/components/partners.mjs.map +1 -1
  15. package/dist/content/index.d.mts +1 -1
  16. package/dist/content/index.d.ts +1 -1
  17. package/dist/content/index.js +3 -2
  18. package/dist/content/index.js.map +1 -1
  19. package/dist/content/index.mjs +3 -2
  20. package/dist/content/index.mjs.map +1 -1
  21. package/dist/{explore-C3SEP5K6.d.ts → explore-Cam_ElSJ.d.ts} +71 -13
  22. package/dist/{explore-Crc41y0j.d.mts → explore-hTQm-_SF.d.mts} +71 -13
  23. package/dist/hooks/index.js +24 -14
  24. package/dist/hooks/index.js.map +1 -1
  25. package/dist/hooks/index.mjs +24 -14
  26. package/dist/hooks/index.mjs.map +1 -1
  27. package/dist/{index-BMWQNZDX.d.mts → index-gm3pBCJT.d.mts} +8 -2
  28. package/dist/{index-BqHF-G-S.d.ts → index-ySbOLSD5.d.ts} +8 -2
  29. package/dist/index.d.mts +30 -9
  30. package/dist/index.d.ts +30 -9
  31. package/dist/index.js +829 -260
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +829 -262
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/store/index.js +3 -2
  36. package/dist/store/index.js.map +1 -1
  37. package/dist/store/index.mjs +3 -2
  38. package/dist/store/index.mjs.map +1 -1
  39. package/package.json +1 -1
@@ -2,6 +2,17 @@ import * as react from 'react';
2
2
  import { E as ExploreProduct, a as ExploreReference } from './explore-types-B5sdOC5M.js';
3
3
  import { Components } from 'react-markdown';
4
4
 
5
+ /** The words the player needs. `AnswerRendererStrings`, and so `ExploreStrings`, carry them. */
6
+ interface ExploreVideoStrings {
7
+ /** Accessible name of the Close button. */
8
+ closeVideo: string;
9
+ /** The full-screen toggle's label in each state — it says what pressing it does NOW. */
10
+ enterFullScreen: string;
11
+ exitFullScreen: string;
12
+ /** Shown in place of the player when the file cannot be played. */
13
+ videoUnavailable: string;
14
+ }
15
+
5
16
  /**
6
17
  * The answer stylesheet as an element. Safe to mount more than once — the rules are
7
18
  * idempotent — so a page that shows answers and nothing else still gets them.
@@ -9,7 +20,7 @@ import { Components } from 'react-markdown';
9
20
  declare function ExploreAnswerStyles(): react.JSX.Element;
10
21
  /**
11
22
  * 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
23
+ * `www.` alike (BotManor ships on www.botmanor.com, everything else on the apex, and either can
13
24
  * appear in the corpus). https only.
14
25
  */
15
26
  declare function inlineImageOrigins(products: readonly Pick<ExploreProduct, "website">[]): Set<string>;
@@ -17,8 +28,25 @@ declare function inlineImageOrigins(products: readonly Pick<ExploreProduct, "web
17
28
  * True when `src` is an https URL on a catalog product site. The backend already strips
18
29
  * every image it did not put in the answer; this is the browser's own check, so a model
19
30
  * that slips an arbitrary URL past it still cannot make the visitor's browser fetch it.
31
+ *
32
+ * A video or caption file is refused even on a catalog site: as an `<img src>` it downloads
33
+ * video bytes into a transcript that promises never to preload one (§7), and draws nothing.
34
+ * That covers a product-video card's poster as well as a plain `![..](..mp4)`.
20
35
  */
21
36
  declare function isInlineImageAllowed(src: unknown, origins: ReadonlySet<string>): src is string;
37
+ /**
38
+ * True when `href` may become a product-video card: the SAME origin rule as an inline image
39
+ * (https, no credentials, a catalog product site), and a path that is a playable video file.
40
+ */
41
+ declare function isInlineVideoAllowed(href: unknown, origins: ReadonlySet<string>): href is string;
42
+ /**
43
+ * Apex origin → catalog slug, so a video card can say WHICH product it is (`data-product`).
44
+ *
45
+ * The file's host is not the answer on its own: HealthyBowl ships on gethealthybowl.com and
46
+ * Workspaces on burdenoff.com, and an e2e selector written against the slug would miss both.
47
+ * First entry wins, as in `productNamesByOrigin`.
48
+ */
49
+ declare function productSlugsByOrigin(products: readonly Pick<ExploreProduct, "slug" | "website">[]): Map<string, string>;
22
50
  /**
23
51
  * An answer as READABLE TEXT, for the clipboard.
24
52
  *
@@ -30,6 +58,10 @@ declare function isInlineImageAllowed(src: unknown, origins: ReadonlySet<string>
30
58
  * stays.
31
59
  * - **Links become `label (url)`.** The destination is information the reader may need; the
32
60
  * bracket-paren syntax is not. Dropping the URL would quietly strip the page it came from.
61
+ * - **A linked image is a link like any other**, labelled by the image's alt text — so the
62
+ * product video is `Planmagnet product video (https://…mp4)`. The image itself goes as
63
+ * every image does; the link is kept because, unlike a bare illustration, it is somewhere
64
+ * the reader can GO — and a pasted answer has no card to click.
33
65
  * - **Citation markers `[3]` stay.** They are the only attribution the copied text carries,
34
66
  * and they are the answer's own — not something the UI added. The sources they point at are
35
67
  * listed right next to the button that copied this.
@@ -47,12 +79,15 @@ declare function answerPlainText(markdown: string): string;
47
79
  /**
48
80
  * The same answer as something to SAY, for read-aloud.
49
81
  *
50
- * It shares every rule above but the two that are right for a paste and wrong for a voice:
82
+ * It shares every rule above but the three that are right for a paste and wrong for a voice:
51
83
  *
52
84
  * - **A link is its label.** The clipboard keeps `label (https://…)` because a pasted answer
53
85
  * loses the page otherwise. Spoken, that is a URL spelled out character by character in
54
86
  * the middle of a sentence — and the prompt asks for two to five links in every answer, so
55
87
  * it is not an edge case. The sources are listed under the answer either way.
88
+ * - **A linked image is not read at all** — the product video included. Its label is a
89
+ * picture, which is never read, and its link is an address; the card or illustration is
90
+ * on screen for anyone who can click it.
56
91
  * - **A table is read as rows, not as pipes.** `| --- | --- |` is a line of punctuation and
57
92
  * the pipes are column rules, neither of which is a word. Each row is read with its cells
58
93
  * under their own column headings — the phone layout, out loud — so a plan list still says
@@ -67,30 +102,53 @@ declare function answerSpeechText(markdown: string): string;
67
102
  /**
68
103
  * The strings the markdown overrides render.
69
104
  *
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.
105
+ * Deliberately a handful of fields rather than the whole `ExploreStrings`: the chat and a
106
+ * published answer page carry different chrome vocabularies, and these are the only words
107
+ * the ANSWER itself needs — including the product-video card and the player it opens.
108
+ * `ExploreStrings` satisfies this structurally, so the chat passes its own `T` unchanged.
74
109
  */
75
- interface AnswerRendererStrings {
110
+ interface AnswerRendererStrings extends ExploreVideoStrings {
76
111
  /** Accessible name of the scrollable box a table sits in. */
77
112
  answerTable: string;
78
113
  /** The badge laid over a concept illustration embedded in an answer. */
79
114
  conceptIllustration: string;
80
115
  /** Read out after a link that opens a new tab. */
81
116
  opensInNewTab: string;
117
+ /** The badge on a product-video card, and its name when the answer gave the video none. */
118
+ productVideo: string;
119
+ /**
120
+ * A product video's name, in the page's language. Takes `{product}`, read out of the
121
+ * English `<Name> product video` alt text the canonical line always carries.
122
+ */
123
+ productVideoTitle: string;
124
+ /** Accessible name of the card's play button. Takes `{title}`. */
125
+ playVideo: string;
82
126
  }
83
127
  interface ExploreAnswerBodyProps {
84
128
  /** The answer, exactly as the harness wrote it: GitHub-flavoured markdown. */
85
129
  children: string;
86
- /** The three strings the overrides render. `ExploreStrings` satisfies this. */
130
+ /** The strings the overrides render. `ExploreStrings` satisfies this. */
87
131
  strings: AnswerRendererStrings;
88
132
  /** Origins an inline image may come from — see {@link inlineImageOrigins}. */
89
133
  imageOrigins: ReadonlySet<string>;
90
134
  /**
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`.
135
+ * Origins a product video and its poster may come from. Defaults to `imageOrigins`, which
136
+ * in the chat is already every catalog site. A published answer page allows fewer images
137
+ * than that and passes the whole catalog here: the video is the product's own file, and is
138
+ * never one of the sources the page's image list is built from. Pass a stable set.
139
+ */
140
+ videoOrigins?: ReadonlySet<string>;
141
+ /**
142
+ * Apex origin → catalog slug (see {@link productSlugsByOrigin}), so a video card can name
143
+ * its product in `data-product`. Without it the card names the file's host instead. Pass a
144
+ * stable map.
145
+ */
146
+ productSlugs?: ReadonlyMap<string, string>;
147
+ /**
148
+ * False while the answer is still streaming, which suppresses its images and its video
149
+ * card: a partial is cut mid-token, so a URL in it may be half-written, and the backend has
150
+ * not yet dropped what it does not allow. A settled answer — every published one — is
151
+ * `true`.
94
152
  */
95
153
  enabled?: boolean;
96
154
  /**
@@ -116,7 +174,7 @@ interface ExploreAnswerBodyProps {
116
174
  * The set of images that failed to load is held HERE rather than in each image, so a source
117
175
  * that 404s stays hidden even when the markdown re-renders from scratch underneath it.
118
176
  */
119
- declare function ExploreAnswerBody({ children, strings, imageOrigins, enabled, components, className, }: ExploreAnswerBodyProps): react.JSX.Element;
177
+ declare function ExploreAnswerBody({ children, strings, imageOrigins, videoOrigins, productSlugs, enabled, components, className, }: ExploreAnswerBodyProps): react.JSX.Element;
120
178
 
121
179
  interface ExplorePageProps {
122
180
  /**
@@ -248,4 +306,4 @@ declare const EXPLORE_PREFILL_MAX_LENGTH = 500;
248
306
  declare function exploreQuestionHref(question: string, base?: string): string;
249
307
  declare function ExplorePage({ seo, productSlug, examplePrompts, className, heightMode, viewportOffset, onNavigate, onSend, welcomeTitle, welcomeBody, locale, }: ExplorePageProps): react.JSX.Element;
250
308
 
251
- export { type AnswerRendererStrings as A, EXPLORE_PREFILL_MAX_LENGTH as E, ExploreAnswerBody as a, type ExploreAnswerBodyProps as b, ExploreAnswerStyles as c, ExplorePage as d, type ExplorePageProps as e, answerPlainText as f, answerSpeechText as g, exploreQuestionHref as h, inlineImageOrigins as i, isInlineImageAllowed as j, type ExploreProvenance as k, type ExploreReferenceKind as l, logoUrlOf as m, productNamesByOrigin as p, referenceKindOf as r, summariseReferences as s };
309
+ export { type AnswerRendererStrings as A, EXPLORE_PREFILL_MAX_LENGTH as E, ExploreAnswerBody as a, type ExploreAnswerBodyProps as b, ExploreAnswerStyles as c, ExplorePage as d, type ExplorePageProps as e, answerPlainText as f, answerSpeechText as g, exploreQuestionHref as h, inlineImageOrigins as i, isInlineImageAllowed as j, isInlineVideoAllowed as k, type ExploreProvenance as l, type ExploreReferenceKind as m, logoUrlOf as n, productNamesByOrigin as o, productSlugsByOrigin as p, referenceKindOf as r, summariseReferences as s };
@@ -2,6 +2,17 @@ import * as react from 'react';
2
2
  import { E as ExploreProduct, a as ExploreReference } from './explore-types-B5sdOC5M.mjs';
3
3
  import { Components } from 'react-markdown';
4
4
 
5
+ /** The words the player needs. `AnswerRendererStrings`, and so `ExploreStrings`, carry them. */
6
+ interface ExploreVideoStrings {
7
+ /** Accessible name of the Close button. */
8
+ closeVideo: string;
9
+ /** The full-screen toggle's label in each state — it says what pressing it does NOW. */
10
+ enterFullScreen: string;
11
+ exitFullScreen: string;
12
+ /** Shown in place of the player when the file cannot be played. */
13
+ videoUnavailable: string;
14
+ }
15
+
5
16
  /**
6
17
  * The answer stylesheet as an element. Safe to mount more than once — the rules are
7
18
  * idempotent — so a page that shows answers and nothing else still gets them.
@@ -9,7 +20,7 @@ import { Components } from 'react-markdown';
9
20
  declare function ExploreAnswerStyles(): react.JSX.Element;
10
21
  /**
11
22
  * 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
23
+ * `www.` alike (BotManor ships on www.botmanor.com, everything else on the apex, and either can
13
24
  * appear in the corpus). https only.
14
25
  */
15
26
  declare function inlineImageOrigins(products: readonly Pick<ExploreProduct, "website">[]): Set<string>;
@@ -17,8 +28,25 @@ declare function inlineImageOrigins(products: readonly Pick<ExploreProduct, "web
17
28
  * True when `src` is an https URL on a catalog product site. The backend already strips
18
29
  * every image it did not put in the answer; this is the browser's own check, so a model
19
30
  * that slips an arbitrary URL past it still cannot make the visitor's browser fetch it.
31
+ *
32
+ * A video or caption file is refused even on a catalog site: as an `<img src>` it downloads
33
+ * video bytes into a transcript that promises never to preload one (§7), and draws nothing.
34
+ * That covers a product-video card's poster as well as a plain `![..](..mp4)`.
20
35
  */
21
36
  declare function isInlineImageAllowed(src: unknown, origins: ReadonlySet<string>): src is string;
37
+ /**
38
+ * True when `href` may become a product-video card: the SAME origin rule as an inline image
39
+ * (https, no credentials, a catalog product site), and a path that is a playable video file.
40
+ */
41
+ declare function isInlineVideoAllowed(href: unknown, origins: ReadonlySet<string>): href is string;
42
+ /**
43
+ * Apex origin → catalog slug, so a video card can say WHICH product it is (`data-product`).
44
+ *
45
+ * The file's host is not the answer on its own: HealthyBowl ships on gethealthybowl.com and
46
+ * Workspaces on burdenoff.com, and an e2e selector written against the slug would miss both.
47
+ * First entry wins, as in `productNamesByOrigin`.
48
+ */
49
+ declare function productSlugsByOrigin(products: readonly Pick<ExploreProduct, "slug" | "website">[]): Map<string, string>;
22
50
  /**
23
51
  * An answer as READABLE TEXT, for the clipboard.
24
52
  *
@@ -30,6 +58,10 @@ declare function isInlineImageAllowed(src: unknown, origins: ReadonlySet<string>
30
58
  * stays.
31
59
  * - **Links become `label (url)`.** The destination is information the reader may need; the
32
60
  * bracket-paren syntax is not. Dropping the URL would quietly strip the page it came from.
61
+ * - **A linked image is a link like any other**, labelled by the image's alt text — so the
62
+ * product video is `Planmagnet product video (https://…mp4)`. The image itself goes as
63
+ * every image does; the link is kept because, unlike a bare illustration, it is somewhere
64
+ * the reader can GO — and a pasted answer has no card to click.
33
65
  * - **Citation markers `[3]` stay.** They are the only attribution the copied text carries,
34
66
  * and they are the answer's own — not something the UI added. The sources they point at are
35
67
  * listed right next to the button that copied this.
@@ -47,12 +79,15 @@ declare function answerPlainText(markdown: string): string;
47
79
  /**
48
80
  * The same answer as something to SAY, for read-aloud.
49
81
  *
50
- * It shares every rule above but the two that are right for a paste and wrong for a voice:
82
+ * It shares every rule above but the three that are right for a paste and wrong for a voice:
51
83
  *
52
84
  * - **A link is its label.** The clipboard keeps `label (https://…)` because a pasted answer
53
85
  * loses the page otherwise. Spoken, that is a URL spelled out character by character in
54
86
  * the middle of a sentence — and the prompt asks for two to five links in every answer, so
55
87
  * it is not an edge case. The sources are listed under the answer either way.
88
+ * - **A linked image is not read at all** — the product video included. Its label is a
89
+ * picture, which is never read, and its link is an address; the card or illustration is
90
+ * on screen for anyone who can click it.
56
91
  * - **A table is read as rows, not as pipes.** `| --- | --- |` is a line of punctuation and
57
92
  * the pipes are column rules, neither of which is a word. Each row is read with its cells
58
93
  * under their own column headings — the phone layout, out loud — so a plan list still says
@@ -67,30 +102,53 @@ declare function answerSpeechText(markdown: string): string;
67
102
  /**
68
103
  * The strings the markdown overrides render.
69
104
  *
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.
105
+ * Deliberately a handful of fields rather than the whole `ExploreStrings`: the chat and a
106
+ * published answer page carry different chrome vocabularies, and these are the only words
107
+ * the ANSWER itself needs — including the product-video card and the player it opens.
108
+ * `ExploreStrings` satisfies this structurally, so the chat passes its own `T` unchanged.
74
109
  */
75
- interface AnswerRendererStrings {
110
+ interface AnswerRendererStrings extends ExploreVideoStrings {
76
111
  /** Accessible name of the scrollable box a table sits in. */
77
112
  answerTable: string;
78
113
  /** The badge laid over a concept illustration embedded in an answer. */
79
114
  conceptIllustration: string;
80
115
  /** Read out after a link that opens a new tab. */
81
116
  opensInNewTab: string;
117
+ /** The badge on a product-video card, and its name when the answer gave the video none. */
118
+ productVideo: string;
119
+ /**
120
+ * A product video's name, in the page's language. Takes `{product}`, read out of the
121
+ * English `<Name> product video` alt text the canonical line always carries.
122
+ */
123
+ productVideoTitle: string;
124
+ /** Accessible name of the card's play button. Takes `{title}`. */
125
+ playVideo: string;
82
126
  }
83
127
  interface ExploreAnswerBodyProps {
84
128
  /** The answer, exactly as the harness wrote it: GitHub-flavoured markdown. */
85
129
  children: string;
86
- /** The three strings the overrides render. `ExploreStrings` satisfies this. */
130
+ /** The strings the overrides render. `ExploreStrings` satisfies this. */
87
131
  strings: AnswerRendererStrings;
88
132
  /** Origins an inline image may come from — see {@link inlineImageOrigins}. */
89
133
  imageOrigins: ReadonlySet<string>;
90
134
  /**
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`.
135
+ * Origins a product video and its poster may come from. Defaults to `imageOrigins`, which
136
+ * in the chat is already every catalog site. A published answer page allows fewer images
137
+ * than that and passes the whole catalog here: the video is the product's own file, and is
138
+ * never one of the sources the page's image list is built from. Pass a stable set.
139
+ */
140
+ videoOrigins?: ReadonlySet<string>;
141
+ /**
142
+ * Apex origin → catalog slug (see {@link productSlugsByOrigin}), so a video card can name
143
+ * its product in `data-product`. Without it the card names the file's host instead. Pass a
144
+ * stable map.
145
+ */
146
+ productSlugs?: ReadonlyMap<string, string>;
147
+ /**
148
+ * False while the answer is still streaming, which suppresses its images and its video
149
+ * card: a partial is cut mid-token, so a URL in it may be half-written, and the backend has
150
+ * not yet dropped what it does not allow. A settled answer — every published one — is
151
+ * `true`.
94
152
  */
95
153
  enabled?: boolean;
96
154
  /**
@@ -116,7 +174,7 @@ interface ExploreAnswerBodyProps {
116
174
  * The set of images that failed to load is held HERE rather than in each image, so a source
117
175
  * that 404s stays hidden even when the markdown re-renders from scratch underneath it.
118
176
  */
119
- declare function ExploreAnswerBody({ children, strings, imageOrigins, enabled, components, className, }: ExploreAnswerBodyProps): react.JSX.Element;
177
+ declare function ExploreAnswerBody({ children, strings, imageOrigins, videoOrigins, productSlugs, enabled, components, className, }: ExploreAnswerBodyProps): react.JSX.Element;
120
178
 
121
179
  interface ExplorePageProps {
122
180
  /**
@@ -248,4 +306,4 @@ declare const EXPLORE_PREFILL_MAX_LENGTH = 500;
248
306
  declare function exploreQuestionHref(question: string, base?: string): string;
249
307
  declare function ExplorePage({ seo, productSlug, examplePrompts, className, heightMode, viewportOffset, onNavigate, onSend, welcomeTitle, welcomeBody, locale, }: ExplorePageProps): react.JSX.Element;
250
308
 
251
- export { type AnswerRendererStrings as A, EXPLORE_PREFILL_MAX_LENGTH as E, ExploreAnswerBody as a, type ExploreAnswerBodyProps as b, ExploreAnswerStyles as c, ExplorePage as d, type ExplorePageProps as e, answerPlainText as f, answerSpeechText as g, exploreQuestionHref as h, inlineImageOrigins as i, isInlineImageAllowed as j, type ExploreProvenance as k, type ExploreReferenceKind as l, logoUrlOf as m, productNamesByOrigin as p, referenceKindOf as r, summariseReferences as s };
309
+ export { type AnswerRendererStrings as A, EXPLORE_PREFILL_MAX_LENGTH as E, ExploreAnswerBody as a, type ExploreAnswerBodyProps as b, ExploreAnswerStyles as c, ExplorePage as d, type ExplorePageProps as e, answerPlainText as f, answerSpeechText as g, exploreQuestionHref as h, inlineImageOrigins as i, isInlineImageAllowed as j, isInlineVideoAllowed as k, type ExploreProvenance as l, type ExploreReferenceKind as m, logoUrlOf as n, productNamesByOrigin as o, productSlugsByOrigin as p, referenceKindOf as r, summariseReferences as s };
@@ -446,8 +446,9 @@ var ECOSYSTEM_PRODUCTS = [
446
446
  name: "FluidGrids",
447
447
  websiteUrl: "https://www.fluidgrids.ai"
448
448
  },
449
- // BotManor ships on botlit.ai — botlit.com does not resolve.
450
- { slug: "botlit", name: "BotManor", websiteUrl: "https://www.botlit.ai" },
449
+ // BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain
450
+ // still serves the same site but is no longer the name we link to).
451
+ { slug: "botlit", name: "BotManor", websiteUrl: "https://www.botmanor.com" },
451
452
  {
452
453
  slug: "bigconsole",
453
454
  name: "BigConsole",
@@ -2071,7 +2072,7 @@ function isHighSurrogate(code) {
2071
2072
  }
2072
2073
  function safeInlineEnd(line) {
2073
2074
  let codeSpan = null;
2074
- let link = null;
2075
+ const links = [];
2075
2076
  const emphasis = [];
2076
2077
  let at = 0;
2077
2078
  while (at < line.length) {
@@ -2080,6 +2081,13 @@ function safeInlineEnd(line) {
2080
2081
  at += 2;
2081
2082
  continue;
2082
2083
  }
2084
+ const innermost = links[links.length - 1];
2085
+ if (innermost !== void 0 && innermost.kind !== "label") {
2086
+ if (char === "(") links.push({ kind: "paren", start: innermost.start });
2087
+ else if (char === ")") links.pop();
2088
+ at += 1;
2089
+ continue;
2090
+ }
2083
2091
  if (char === "`") {
2084
2092
  let run = 1;
2085
2093
  while (line[at + run] === "`") run += 1;
@@ -2092,17 +2100,17 @@ function safeInlineEnd(line) {
2092
2100
  continue;
2093
2101
  }
2094
2102
  if (char === "[") {
2095
- if (link === null) link = line[at - 1] === "!" ? at - 1 : at;
2103
+ links.push({ kind: "label", start: line[at - 1] === "!" ? at - 1 : at });
2096
2104
  at += 1;
2097
2105
  continue;
2098
2106
  }
2099
- if (char === "]") {
2100
- if (link !== null && line[at + 1] !== "(") link = null;
2101
- at += 1;
2102
- continue;
2103
- }
2104
- if (char === ")") {
2105
- link = null;
2107
+ if (char === "]" && innermost !== void 0) {
2108
+ links.pop();
2109
+ if (line[at + 1] === "(") {
2110
+ links.push({ kind: "destination", start: innermost.start });
2111
+ at += 2;
2112
+ continue;
2113
+ }
2106
2114
  at += 1;
2107
2115
  continue;
2108
2116
  }
@@ -2129,9 +2137,11 @@ function safeInlineEnd(line) {
2129
2137
  }
2130
2138
  at += 1;
2131
2139
  }
2132
- const open = [codeSpan, link, emphasis[0]?.start ?? null].filter(
2133
- (value) => value !== null
2134
- );
2140
+ const open = [
2141
+ codeSpan,
2142
+ links[0]?.start ?? null,
2143
+ emphasis[0]?.start ?? null
2144
+ ].filter((value) => value !== null);
2135
2145
  return open.length === 0 ? line.length : Math.min(...open);
2136
2146
  }
2137
2147
  function revealBoundary(text, limit) {