@vectojs/markdown 0.22.0 → 0.23.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/README.md CHANGED
@@ -1,243 +1,71 @@
1
1
  # @vectojs/markdown
2
2
 
3
- Canvas-native Markdown (with TeX math) rendering for [VectoJS](https://github.com/vectojs/vectojs).
4
-
5
- `Markdown` is a high-level entity that parses Markdown with
6
- [`marked`](https://marked.js.org/), renders TeX math to SVG with
7
- `@vectojs/tex`, and lays the result out using `@vectojs/ui` components
8
- (`RichText`, `Stack`, `Table`, `Text`, `Image`). It also exports `CodeBlock`.
9
-
10
- This package was split out of `@vectojs/ui` so that the heavy `marked` +
11
- `@vectojs/tex` dependencies are only pulled in by apps that actually render
12
- Markdown. Because it depends on `@vectojs/ui` components, it sits **above** `ui`
13
- in the dependency graph — install it alongside `@vectojs/ui` and `@vectojs/core`.
3
+ `@vectojs/markdown` renders Markdown (with TeX math) as a canvas-native entity tree: the
4
+ `Markdown` entity parses with `marked`, typesets math through `@vectojs/tex`, and lays the result
5
+ out using `@vectojs/ui` components. It was split out of `@vectojs/ui` precisely so the heavy
6
+ `marked` + `@vectojs/tex` dependencies load only for apps that render documents — it sits **above**
7
+ `ui` in the dependency graph and takes `@vectojs/core` and `@vectojs/ui` as peer dependencies.
14
8
 
15
9
  ## Install
16
10
 
17
- ```sh
11
+ ```bash
18
12
  bun add @vectojs/markdown @vectojs/ui @vectojs/core
19
13
  ```
20
14
 
21
- ## Usage
22
-
23
- ```ts
24
- import { Markdown } from "@vectojs/markdown";
25
-
26
- const md = new Markdown("# Hello\n\nInline math $E = mc^2$.");
27
- scene.add(md);
28
- ```
29
-
30
- ## Streaming
15
+ Peers: `@vectojs/core >=1.34.0 <2`, `@vectojs/ui >=2.6.0 <3`.
31
16
 
32
- `createStream()` coalesces accepted chunks into at most one Markdown
33
- parse/layout commit per animation frame. `write()` applies backpressure when its
34
- bounded buffer is full; await it when consuming an async token source.
35
-
36
- ```ts
37
- const md = new Markdown("");
38
- scene.add(md);
39
-
40
- const stream = md.createStream();
41
- for await (const token of tokens) {
42
- await stream.write(token);
43
- }
44
- // Commits the final text, then waits for the parse to be applied — once this
45
- // resolves, the rendered document reflects everything written.
46
- await stream.close();
47
- ```
48
-
49
- Add fixed-rate typewriter pacing without changing producer code:
50
-
51
- ```ts
52
- const stream = md.createStream({
53
- pacing: { graphemesPerSecond: 48 },
54
- maxBufferedChars: 64 * 1024,
55
- signal: abortController.signal,
56
- });
57
- ```
58
-
59
- Pacing slices by grapheme cluster, so combining marks, emoji ZWJ sequences,
60
- flags, and surrogate pairs stay intact across ordinary chunk/frame boundaries.
61
- `abort()` discards uncommitted text; `Markdown.destroy()` does the same cleanup
62
- automatically. The existing `appendMarkdown()` API remains synchronous and
63
- flushes submitted controller text before a direct append.
64
-
65
- ### Incomplete Markdown while streaming
66
-
67
- Mid-stream, the trailing text of a paragraph is often an unclosed inline
68
- construct — `**bo`, `` `cod ``, `[text](url`. `marked` has no signal that more
69
- characters are coming, so it lexes those as plain text. `incompleteMode` chooses
70
- what you show in the meantime:
17
+ ## Usage
71
18
 
72
19
  ```ts
73
- const stream = md.createStream({ incompleteMode: "optimistic" });
74
- ```
75
-
76
- | Mode | Unclosed `**bo` renders as |
77
- | --------------------- | ------------------------------------ |
78
- | `'literal'` (default) | `**bo` — the literal characters |
79
- | `'optimistic'` | **bo** — guessed bold, syntax hidden |
80
-
81
- `'optimistic'` covers strong, emphasis, and inline code. An unclosed link shows
82
- its label as plain text: with no closing `)` there is no URL yet, so nothing is
83
- made clickable. Only the document's **last paragraph** is ever guessed at, and
84
- only while the stream is open — headings, list items, table cells, and any
85
- earlier paragraph are always literal.
20
+ import { Scene } from '@vectojs/core';
21
+ import { Markdown } from '@vectojs/markdown';
86
22
 
87
- The guess is display-only. `Markdown.tokens` is never affected, and `close()`
88
- unwinds it, so a `'literal'` and an `'optimistic'` stream of the same source end
89
- at an identical document. `'literal'` remains the default: it is what every prior
90
- release rendered.
23
+ const scene = new Scene(document.querySelector<HTMLCanvasElement>('canvas')!);
24
+ scene.renderMode = 'onDemand';
91
25
 
92
- ### Knowing when the document is final
93
-
94
- `onStable` fires once, after `close()` has committed the last chunk _and_ the
95
- parse has been applied. Use it for one-time work you do not want repeated against
96
- content still in flight:
97
-
98
- ```ts
99
- const stream = md.createStream({
100
- onStable: (blocks) => {
101
- for (const block of blocks) fadeIn(block);
26
+ const md = new Markdown('# Hello\n\nInline math $E = mc^2$.', {
27
+ maxWidth: 640,
28
+ theme: 'githubDark', // or a full MarkdownTheme object
29
+ onLinkClick(href) {
30
+ console.log('navigate', href);
102
31
  },
103
32
  });
104
- ```
105
-
106
- It never fires on `flush()`, `abort()`, or `destroy()` — none of those mean the
107
- content stopped changing. It receives a snapshot array, not a live reference.
108
- Calling `appendMarkdown()` or `setContent()` from inside the callback throws; a
109
- throw from the callback rejects the `close()` promise. `onStable` is independent
110
- of `incompleteMode` and works with the `'literal'` default.
111
-
112
- ### Streamed TeX math
113
-
114
- A fenced math block is typeset only once its closing fence arrives:
33
+ scene.add(md.setPosition(24, 24));
115
34
 
116
- ````md
117
- ```math
118
- \int_0^1 x\,dx = \frac{1}{2}
35
+ // Stream LLM output: chunks coalesce into one parse per frame.
36
+ const stream = md.createStream({ incompleteMode: 'optimistic' });
37
+ stream.write('# Ti');
38
+ await stream.close(); // resolves after the final parse is applied
119
39
  ```
120
- ````
121
-
122
- While the fence is still open the block renders as an ordinary code block showing
123
- the TeX source, then becomes the formula on the chunk that closes it. This is
124
- deliberate. `marked` lexes an unterminated fence as a _complete_ token as soon as
125
- it reads the info string, so a formula streamed a few characters at a time
126
- arrives as a long run of whole tokens, nearly all of them invalid TeX — typesetting
127
- each one spends the most expensive call in this package rendering an error glyph
128
- that is replaced by the next chunk. Showing the source is both cheaper and more
129
- honest: the formula genuinely is not finished.
130
-
131
- Converted formulas are cached (bounded, process-wide), so a repeated formula is
132
- converted once no matter how many documents or instances render it.
133
-
134
- Inline `$...$` math is a separate path: it is currently shown as styled source
135
- text, not typeset.
136
-
137
- #### The math engine is loaded on demand
138
-
139
- TeX math is typeset by `@vectojs/tex`, which is imported dynamically the first
140
- time a document actually has a formula. It is by far the heaviest thing this
141
- package can pull in — measured against a browser bundle of a consumer that renders
142
- only prose, built with code splitting and minification:
143
-
144
- | prose-only consumer | raw | gzip | chunks |
145
- | ---------------------- | --------: | ------: | -----: |
146
- | `mathjax-full` | 2,199,869 | 748,713 | 19 |
147
- | `@vectojs/tex` (now) | 758,249 | 273,754 | 3 |
148
- | no math at all (floor) | 379,224 | 118,670 | 3 |
149
-
150
- Against that floor the math path itself is 630,043 gzip under `mathjax-full` and
151
- 155,033 under `@vectojs/tex` — **4.06x smaller**. The eagerly-downloaded entry
152
- chunk a prose-only consumer actually pays for is 117,889 gzip, within 1 KB of the
153
- no-math floor.
154
-
155
- Your bundler needs code splitting enabled to see this; without it the bytes are
156
- still in the output, just not evaluated until first use.
157
-
158
- The tradeoff is that **the first formula on a page cannot be typeset
159
- synchronously.** It renders as a code block of TeX source — the same state an
160
- unclosed fence already shows — and is replaced once the module resolves. Every
161
- formula after that is synchronous again. (The engine itself is synchronous; the
162
- lazy import is what defers it, and it is kept for the bundle size above.)
163
-
164
- While streaming this is invisible: the load starts as soon as an _opening_ math
165
- fence appears, several chunks before the closing one, so the formula is typeset on
166
- the chunk that closes it. `await stream.close()` and `onStable` also wait for a
167
- pending load, so a "final" document never hands you an untypeset formula.
168
-
169
- If you need the very first formula typeset in the same tick — measuring layout
170
- immediately after construction, for instance — preload it:
171
-
172
- ````ts
173
- import { Markdown, preloadMathJax } from "@vectojs/markdown";
174
-
175
- await preloadMathJax();
176
- const md = new Markdown("```math\n\\int_0^1 x\\,dx\n```"); // typeset synchronously
177
- ````
178
-
179
- `preloadMathJax()` is idempotent and shared across every document, so calling it
180
- from several places starts one load. `isMathJaxReady()` reports whether formulas
181
- currently typeset without waiting. If the load fails, formulas keep rendering as
182
- TeX source rather than throwing.
183
-
184
- Both names are historical: they date from when `mathjax-full` was the engine and
185
- mean "the math engine", whichever one that is. They keep those names because they
186
- are public API and a rename would break every consumer for cosmetics.
187
-
188
- A formula containing a symbol outside the engine's shipped glyph corpus also
189
- renders as TeX source rather than being drawn with that symbol missing.
190
-
191
- ## Images
192
-
193
- An image renders in one of two ways, decided by where it is written.
194
-
195
- **On its own, or in a paragraph, blockquote or list item**, the paragraph splits
196
- into blocks and the image becomes an `Image` entity at its natural size, capped
197
- to the available width. This is the ordinary `![alt](url)` case.
198
-
199
- **On a line it shares with text** — in a heading, or in a table cell — it renders
200
- as an inline box in the text run, so the prose flows around it and selection and
201
- the accessible name still work. Its height is a multiple of the run's font size
202
- (`theme.inlineImageScale`, default `1.15`) and its width follows the image's
203
- natural aspect ratio, so a badge stays wide and a square icon stays square:
204
-
205
- ```ts
206
- const md = new Markdown("# Build ![passing](https://img.example/badge.svg)");
207
- ```
208
-
209
- This is a deliberate departure from HTML, which would render an inline image at
210
- its intrinsic size. A 512px logo written into an `h1` would otherwise tower over
211
- its own heading, and an inline box has to be sized before the image has decoded.
212
- The height is fixed up front for the same reason: the line box never moves, and
213
- only the width settles once the aspect ratio is known.
214
-
215
- The `alt` text is the accessible name and the copied text — never painted as
216
- visible prose. If the image fails to load, the box is replaced by the alt text
217
- rather than left as an invisible gap.
218
-
219
- ## Syntax coverage
220
-
221
- Everything in [CommonMark](https://spec.commonmark.org/) plus the
222
- [GFM](https://github.github.com/gfm/) extensions this renderer draws: tables,
223
- strikethrough, task lists, autolinks, plus `$…$` / `$$…$$` TeX math and
224
- ` ```math ` fences.
225
-
226
- Two constructs are deliberately **not** supported, and both are pinned by tests
227
- so the behaviour cannot drift silently:
228
-
229
- - **Definition lists.** `Term` then `: definition` renders as the two literal
230
- lines the source contains, colon included.
231
- - **Raw HTML blocks.** `<details>`, `<div>`, `<iframe>` and HTML comments render
232
- nothing at all.
233
-
234
- Definition lists are neither CommonMark nor GFM; when they arrive it will be
235
- through the same syntax-extension mechanism footnotes need. Raw HTML blocks
236
- cannot work in a zero-DOM renderer — there is no DOM to hand markup to. `<svg>`
237
- is the one exception, because a self-contained SVG document can be rasterized.
238
-
239
- Footnotes (`[^1]`) are **not yet parsed** and currently render as literal source.
240
40
 
241
- > Migrating from `@vectojs/ui` ≤ 1.x? `Markdown` and `CodeBlock` used to be
242
- > exported from `@vectojs/ui`. As of `@vectojs/ui@2.0.0` they live here — change
243
- > `import { Markdown } from '@vectojs/ui'` to `from '@vectojs/markdown'`.
41
+ ## Highlights
42
+
43
+ - CommonMark plus GFM tables, strikethrough, task lists, and autolinks; paragraphs become
44
+ `RichText`, fences become `CodeBlock`, GFM tables become `Table`.
45
+ - Frame-coalesced streaming via `createStream()`: at most one parse/layout commit per animation
46
+ frame, grapheme-cluster-safe typewriter pacing (`graphemesPerSecond`), bounded-buffer
47
+ backpressure on `write()`, `onStable` firing once when the document is final, and an
48
+ `incompleteMode` of `'literal'` (default) or `'optimistic'` for unclosed markers mid-stream.
49
+ - TeX math through `@vectojs/tex`, dynamically imported on the first formula so prose-only bundles
50
+ stay small; `preloadMathJax()` / `isMathJaxReady()` control the lazy load, converted formulas are
51
+ cached process-wide, and fenced math blocks typeset only once their closing fence arrives.
52
+ - Themes: constructor accepts a preset name (`'githubDark' | 'githubLight' | 'dracula' |
53
+ 'solarizedDark' | 'solarizedLight'`) or a full theme object; post-construction changes go through
54
+ `setTheme()` — `theme` itself is getter-only (#657).
55
+ - Selectable content: rendered text projects browser-native drag selection, copy, and find-in-page;
56
+ toggle at runtime with `setSelectable(false)`.
57
+ - Block affordances: opt-in copy/download controls on code blocks and tables with injectable
58
+ `writeClipboard` / `saveFile`, per-kind overrides, and CSV export helpers (`tableToCsv`,
59
+ `tableToMarkdown`).
60
+ - Front matter: `scanFrontMatter` / `parseFrontMatterFields` expose YAML-ish metadata before the
61
+ document body renders.
62
+ - Large-document virtualization: pass `virtualize` and drive `setVisibleRange(scrollY, viewportHeight)`
63
+ to materialize only top-level blocks near the viewport.
64
+
65
+ > Documents @vectojs/markdown@0.23.0.
66
+
67
+ ## Documentation
68
+
69
+ - [Markdown reference](https://vectojs.org/reference/ui-markdown/)
70
+ - [CodeBlock reference](https://vectojs.org/reference/ui-codeblock/)
71
+ - [Streaming guide](https://vectojs.org/learn/streaming/)
@@ -150,7 +150,38 @@ export interface MarkdownOptions {
150
150
  export declare class Markdown extends UIComponent {
151
151
  content: Stack;
152
152
  maxWidth: number;
153
- theme: Required<MarkdownTheme>;
153
+ /**
154
+ * Resolved once in the constructor and never re-applied: entities capture
155
+ * colors, fonts and sizes at build time, so assigning this after construction
156
+ * would paint blocks built afterwards in the new palette while everything
157
+ * earlier kept the old one. Exposed read-only to keep that trap unreachable
158
+ * (#657); the supported post-construction path is {@link setTheme}, which
159
+ * re-renders instead of leaving blocks half-migrated.
160
+ *
161
+ * Backed by {@link currentTheme} rather than a `readonly` field because the
162
+ * blockquote arm legally swaps the render-time theme for its own subtree and
163
+ * restores it afterwards — an internal, exception-safe scope, not a re-theme.
164
+ */
165
+ get theme(): Required<MarkdownTheme>;
166
+ private currentTheme;
167
+ /**
168
+ * Swap the document theme and rebuild every rendered block.
169
+ *
170
+ * Accepts the same shapes as {@link MarkdownOptions.theme}: a preset name or
171
+ * a full/partial {@link MarkdownTheme}, resolved through
172
+ * {@link resolvePresetTheme}. This is the only supported way to change theme
173
+ * after construction — {@link theme} is a read-only view (#657) precisely so
174
+ * tokens cannot be swapped underneath already-built blocks without the
175
+ * rebuild performed here.
176
+ *
177
+ * The rebuild goes through {@link setContent} rather than an in-place
178
+ * repaint: entities capture colors and fonts at build time, so nothing short
179
+ * of re-rendering applies a palette, and an active stream needs the same
180
+ * teardown an explicit content replacement gets.
181
+ *
182
+ * @returns `this` for chaining.
183
+ */
184
+ setTheme(theme: MarkdownThemePresetName | MarkdownTheme): this;
154
185
  onLinkClick?: (url: string) => void;
155
186
  selectable: boolean;
156
187
  /**
@@ -535,12 +566,6 @@ export declare class Markdown extends UIComponent {
535
566
  private refitParagraphImage;
536
567
  /** Replace all markdown content (full rebuild). */
537
568
  setContent(markdown: string): this;
538
- /**
539
- * Tear down this Markdown block: drop any in-flight worker callbacks (each
540
- * pins `this` via its closure, so a mid-stream destroy would otherwise keep
541
- * the whole subtree alive until the worker replied), then recurse into the
542
- * content subtree via `super.destroy()` so every block's resources are freed.
543
- */
544
569
  /**
545
570
  * Repaint this document when an inline formula's raster finishes decoding.
546
571
  *
@@ -591,6 +616,12 @@ export declare class Markdown extends UIComponent {
591
616
  * this predicate.
592
617
  */
593
618
  private inlineImageBoxesStale;
619
+ /**
620
+ * Tear down this Markdown block: drop any in-flight worker callbacks (each
621
+ * pins `this` via its closure, so a mid-stream destroy would otherwise keep
622
+ * the whole subtree alive until the worker replied), then recurse into the
623
+ * content subtree via `super.destroy()` so every block's resources are freed.
624
+ */
594
625
  destroy(): void;
595
626
  /**
596
627
  * Streaming and parse state — the markdown streaming inspector.
@@ -643,30 +674,6 @@ export declare class Markdown extends UIComponent {
643
674
  * guess is unwound back to.
644
675
  */
645
676
  private literalParagraphSpans;
646
- /**
647
- * Update a reused blockquote's tail child in place, or report that it cannot be.
648
- *
649
- * The render arm builds `container[border, innerStack]` where every inner block
650
- * sits in its own single-child `wrapper`, so the tail entity is
651
- * `innerStack.children.at(-1).children[0]`. Only the LAST inner block may be
652
- * updated: the inner token list is prefix-stable exactly like the top level (a
653
- * growing quote keeps its earlier blocks byte-identical), so anything before the
654
- * tail is untouched and anything more complicated than a changed tail falls back
655
- * to the caller's rebuild.
656
- *
657
- * Returns `false` without mutating anything when the shape is not the simple
658
- * grow-the-tail case, which is the signal for the caller to rebuild. Every early
659
- * return has to leave the entity untouched, or a rejected reuse would leave a
660
- * half-updated quote on screen.
661
- */
662
- /**
663
- * Build one list item's spans: inline content plus its marker.
664
- *
665
- * Shared by the `list` render arm and the streamed-reuse path below, because
666
- * the two must produce byte-identical spans — a reused list that disagreed with
667
- * a rebuilt one about its marker or its entity decoding would make a streamed
668
- * document differ from the same source pasted at once.
669
- */
670
677
  /**
671
678
  * Inline spans for one table cell.
672
679
  *
@@ -697,6 +704,28 @@ export declare class Markdown extends UIComponent {
697
704
  private inlineRunSpans;
698
705
  /** One text run of an image-bearing paragraph, as both paths build it. */
699
706
  private inlineRunRichText;
707
+ /**
708
+ * Wraps a block in its copy / download controls, or returns it untouched.
709
+ *
710
+ * The controls are built lazily through `make` so a document with
711
+ * `blockAffordances` off pays nothing — not the closures, not the measurement
712
+ * `BlockAffordanceButton` does in its constructor.
713
+ */
714
+ private withBlockAffordances;
715
+ /** Copy and download controls for one fenced code block, per {@link affordanceConfig}. */
716
+ private codeBlockAffordances;
717
+ /** Copy (as Markdown) and download (as CSV) controls for one table, per {@link affordanceConfig}. */
718
+ private tableAffordances;
719
+ /**
720
+ * Button styling for the affordances, derived from the document theme.
721
+ *
722
+ * Themed rather than hardcoded so a light-theme document does not get the dark
723
+ * default palette. `focusColor` is set explicitly from the theme's accent
724
+ * because `Button`'s default cyan is tuned for the dark palette and reads as
725
+ * off-brand elsewhere — while a focus ring is the one affordance a keyboard
726
+ * user cannot do without.
727
+ */
728
+ private affordanceButtonOptions;
700
729
  /**
701
730
  * One image inside a paragraph, sized by a guess until its bitmap decodes.
702
731
  *
@@ -722,28 +751,6 @@ export declare class Markdown extends UIComponent {
722
751
  * policy for a zero-dimension source is a separate decision from notifying
723
752
  * the scene, which is the actual defect here.
724
753
  */
725
- /**
726
- * Wraps a block in its copy / download controls, or returns it untouched.
727
- *
728
- * The controls are built lazily through `make` so a document with
729
- * `blockAffordances` off pays nothing — not the closures, not the measurement
730
- * `BlockAffordanceButton` does in its constructor.
731
- */
732
- private withBlockAffordances;
733
- /** Copy and download controls for one fenced code block, per {@link affordanceConfig}. */
734
- private codeBlockAffordances;
735
- /** Copy (as Markdown) and download (as CSV) controls for one table, per {@link affordanceConfig}. */
736
- private tableAffordances;
737
- /**
738
- * Button styling for the affordances, derived from the document theme.
739
- *
740
- * Themed rather than hardcoded so a light-theme document does not get the dark
741
- * default palette. `focusColor` is set explicitly from the theme's accent
742
- * because `Button`'s default cyan is tuned for the dark palette and reads as
743
- * off-brand elsewhere — while a focus ring is the one affordance a keyboard
744
- * user cannot do without.
745
- */
746
- private affordanceButtonOptions;
747
754
  private paragraphImage;
748
755
  /**
749
756
  * Re-position every block after an image whose decode corrected its box.
@@ -859,6 +866,14 @@ export declare class Markdown extends UIComponent {
859
866
  * bullet or ordinal.
860
867
  */
861
868
  private listItemBlockStack;
869
+ /**
870
+ * Build one list item's spans: inline content plus its marker.
871
+ *
872
+ * Shared by the `list` render arm and the streamed-reuse path below, because
873
+ * the two must produce byte-identical spans — a reused list that disagreed with
874
+ * a rebuilt one about its marker or its entity decoding would make a streamed
875
+ * document differ from the same source pasted at once.
876
+ */
862
877
  private listItemSpans;
863
878
  /** Construct the `RichText` for one list item. */
864
879
  private listItemRichText;
@@ -950,6 +965,22 @@ export declare class Markdown extends UIComponent {
950
965
  * (its keys are `text`/`tokens`/`header`/`align`).
951
966
  */
952
967
  private updateStreamedTable;
968
+ /**
969
+ * Update a reused blockquote's tail child in place, or report that it cannot be.
970
+ *
971
+ * The render arm builds `container[border, innerStack]` where every inner block
972
+ * sits in its own single-child `wrapper`, so the tail entity is
973
+ * `innerStack.children.at(-1).children[0]`. Only the LAST inner block may be
974
+ * updated: the inner token list is prefix-stable exactly like the top level (a
975
+ * growing quote keeps its earlier blocks byte-identical), so anything before the
976
+ * tail is untouched and anything more complicated than a changed tail falls back
977
+ * to the caller's rebuild.
978
+ *
979
+ * Returns `false` without mutating anything when the shape is not the simple
980
+ * grow-the-tail case, which is the signal for the caller to rebuild. Every early
981
+ * return has to leave the entity untouched, or a rejected reuse would leave a
982
+ * half-updated quote on screen.
983
+ */
953
984
  private updateBlockquoteTail;
954
985
  /**
955
986
  * Spans for a heading being updated in place.
@@ -979,13 +1010,6 @@ export declare class Markdown extends UIComponent {
979
1010
  private optimisticParagraphSpans;
980
1011
  /** Display style for a guessed-closed construct. */
981
1012
  private optimisticStyle;
982
- /**
983
- * Re-render the paragraph currently showing a guess from its own tokens, with
984
- * no overlay, and forget it.
985
- *
986
- * Idempotent and free when no guess is live, which is what lets `close()`,
987
- * `abort()`, and a mid-stream staleness check all call it unconditionally.
988
- */
989
1013
  /**
990
1014
  * Start the MathJax load, and re-typeset this document once it resolves.
991
1015
  *
@@ -1023,6 +1047,13 @@ export declare class Markdown extends UIComponent {
1023
1047
  * stream is still open the next chunk re-applies a guess.
1024
1048
  */
1025
1049
  private retypesetFromTokens;
1050
+ /**
1051
+ * Re-render the paragraph currently showing a guess from its own tokens, with
1052
+ * no overlay, and forget it.
1053
+ *
1054
+ * Idempotent and free when no guess is live, which is what lets `close()`,
1055
+ * `abort()`, and a mid-stream staleness check all call it unconditionally.
1056
+ */
1026
1057
  private unwindOptimisticTail;
1027
1058
  /**
1028
1059
  * Drop a guess that is no longer on the document's trailing paragraph.