@vectojs/markdown 0.20.3 → 0.21.0

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.
@@ -83,6 +83,23 @@ export interface MarkdownOptions {
83
83
  * download behaviour to observe.
84
84
  */
85
85
  saveFile?: (filename: string, content: string, mimeType: string) => void;
86
+ /**
87
+ * Materialize only the top-level blocks near the viewport of a very large
88
+ * document, representing off-screen height as numeric offsets rather than
89
+ * entities. Off by default; pass `true` or `{ overscan }` to opt in.
90
+ *
91
+ * The host must drive it with {@link Markdown.setVisibleRange} each scroll
92
+ * frame (a `ScrollView` whose content is this `Markdown` does so
93
+ * automatically). Off-screen height is estimated from the token source and
94
+ * refined to the exact measured height when a block first mounts, so the
95
+ * scrollbar stays correct while only a window of blocks exists as entities.
96
+ *
97
+ * Not supported together with streaming (`createStream` / `appendMarkdown`):
98
+ * a document that virtualizes must be rendered whole.
99
+ */
100
+ virtualize?: boolean | {
101
+ overscan?: number;
102
+ };
86
103
  }
87
104
  /**
88
105
  * Renders Markdown content into a VectoJS entity tree using {@link marked}.
@@ -138,6 +155,19 @@ export declare class Markdown extends UIComponent {
138
155
  /** File saver used by the download controls. */
139
156
  saveFile: (filename: string, content: string, mimeType: string) => void;
140
157
  private activeBlockMetrics;
158
+ /** Whether `opts.virtualize` enabled viewport-culled block materialization. */
159
+ private readonly virtualizeBlocks;
160
+ /** Overscan margin (px) added above/below the visible window before mounting. */
161
+ private readonly virtualOverscan;
162
+ /** Top-level tokens that produce a child entity, in document order. */
163
+ private virtualTokens;
164
+ /** Fenwick tree over per-block strides (height + blockGap) when virtualizing. */
165
+ private virtualHeights;
166
+ /** token index → mounted entity, for blocks currently inside `content`. */
167
+ private readonly virtualMounted;
168
+ /** Visible window top (scroll offset) and height, last set via setVisibleRange. */
169
+ private virtualScrollY;
170
+ private virtualViewportH;
141
171
  /**
142
172
  * Called after this entity's own `width`/`height` changed because the document
143
173
  * was re-laid-out. **This is the hook to wire up when a host has to move or
@@ -388,6 +418,32 @@ export declare class Markdown extends UIComponent {
388
418
  */
389
419
  get frontMatterFields(): Readonly<Record<string, string>>;
390
420
  private renderMarkdown;
421
+ /**
422
+ * Drive the virtualized window from the host's scroll state.
423
+ *
424
+ * Only the blocks intersecting `[scrollY − overscan, scrollY + viewportHeight +
425
+ * overscan]` stay materialized; every other block is a numeric offset in the
426
+ * height tree, so the scrollbar still reports the full document height while
427
+ * only a window of blocks exists as entities. A no-op when `virtualize` was
428
+ * not enabled.
429
+ */
430
+ setVisibleRange(scrollY: number, viewportHeight: number): void;
431
+ /**
432
+ * Cheap per-block height estimate from the token source, refined to the exact
433
+ * measured height when the block first mounts. Coarse by design: the estimate
434
+ * only has to keep the scrollbar plausible before a block is first seen; the
435
+ * Fenwick tree corrects the rest.
436
+ */
437
+ private estimateBlockHeight;
438
+ /** Estimate a wrapped line count from raw character length against the max width. */
439
+ private estimateTextHeight;
440
+ /**
441
+ * Mount the blocks in the current window and unmount the rest. The `content`
442
+ * Stack lays the mounted window contiguously from y = 0; `content.y` is then
443
+ * set to the skipped prefix sum so the first mounted block lands at its true
444
+ * y — a numeric spacer, not a wrapper entity.
445
+ */
446
+ private reconcileVirtual;
391
447
  /** Create a frame-coalesced stream bound to this Markdown instance. */
392
448
  createStream(options?: StreamControllerOptions): StreamController;
393
449
  /**
package/dist/index.js CHANGED
@@ -3499,6 +3499,19 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
3499
3499
  /** File saver used by the download controls. */
3500
3500
  saveFile;
3501
3501
  activeBlockMetrics = null;
3502
+ /** Whether `opts.virtualize` enabled viewport-culled block materialization. */
3503
+ virtualizeBlocks;
3504
+ /** Overscan margin (px) added above/below the visible window before mounting. */
3505
+ virtualOverscan;
3506
+ /** Top-level tokens that produce a child entity, in document order. */
3507
+ virtualTokens = null;
3508
+ /** Fenwick tree over per-block strides (height + blockGap) when virtualizing. */
3509
+ virtualHeights = null;
3510
+ /** token index → mounted entity, for blocks currently inside `content`. */
3511
+ virtualMounted = /* @__PURE__ */ new Map();
3512
+ /** Visible window top (scroll offset) and height, last set via setVisibleRange. */
3513
+ virtualScrollY = 0;
3514
+ virtualViewportH = 0;
3502
3515
  /**
3503
3516
  * Called after this entity's own `width`/`height` changed because the document
3504
3517
  * was re-laid-out. **This is the hook to wire up when a host has to move or
@@ -3784,6 +3797,9 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
3784
3797
  this.showCodeLanguage = opts.showCodeLanguage ?? false;
3785
3798
  this.writeClipboard = opts.writeClipboard ?? defaultWriteClipboard;
3786
3799
  this.saveFile = opts.saveFile ?? defaultSaveFile;
3800
+ const virt = opts.virtualize;
3801
+ this.virtualizeBlocks = virt === true || typeof virt === "object" && virt !== null;
3802
+ this.virtualOverscan = typeof virt === "object" && virt !== null && typeof virt.overscan === "number" ? virt.overscan : 800;
3787
3803
  this.content = new import_ui4.Stack({
3788
3804
  direction: "vertical",
3789
3805
  gap: this.theme.blockGap
@@ -3896,6 +3912,20 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
3896
3912
  const tokens = lexMarkdown(text, this._userTiming);
3897
3913
  this.setTokens(tokens);
3898
3914
  this.abbreviations = collectAbbreviations(tokens);
3915
+ if (this.virtualizeBlocks) {
3916
+ this.virtualTokens = tokens.filter((token) => this.producesEntity(token));
3917
+ const n = this.virtualTokens.length;
3918
+ this.virtualHeights = new import_ui4.RowHeights(n, 0);
3919
+ for (let i = 0; i < n; i++) {
3920
+ this.virtualHeights.set(
3921
+ i,
3922
+ this.estimateBlockHeight(this.virtualTokens[i]) + this.theme.blockGap
3923
+ );
3924
+ }
3925
+ this.virtualMounted.clear();
3926
+ this.reconcileVirtual();
3927
+ return;
3928
+ }
3899
3929
  for (const token of tokens) {
3900
3930
  const el = this.renderToken(token);
3901
3931
  if (el) {
@@ -3905,8 +3935,123 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
3905
3935
  this.width = this.content.width;
3906
3936
  this.height = this.content.height;
3907
3937
  }
3938
+ /**
3939
+ * Drive the virtualized window from the host's scroll state.
3940
+ *
3941
+ * Only the blocks intersecting `[scrollY − overscan, scrollY + viewportHeight +
3942
+ * overscan]` stay materialized; every other block is a numeric offset in the
3943
+ * height tree, so the scrollbar still reports the full document height while
3944
+ * only a window of blocks exists as entities. A no-op when `virtualize` was
3945
+ * not enabled.
3946
+ */
3947
+ setVisibleRange(scrollY, viewportHeight) {
3948
+ if (!this.virtualizeBlocks) return;
3949
+ this.virtualScrollY = Math.max(0, scrollY);
3950
+ this.virtualViewportH = Math.max(0, viewportHeight);
3951
+ this.reconcileVirtual();
3952
+ }
3953
+ /**
3954
+ * Cheap per-block height estimate from the token source, refined to the exact
3955
+ * measured height when the block first mounts. Coarse by design: the estimate
3956
+ * only has to keep the scrollbar plausible before a block is first seen; the
3957
+ * Fenwick tree corrects the rest.
3958
+ */
3959
+ estimateBlockHeight(token) {
3960
+ const t = this.theme;
3961
+ switch (token.type) {
3962
+ case "heading": {
3963
+ const size = headingSize(t, token.depth);
3964
+ return Math.ceil(size * 1.5) + 8;
3965
+ }
3966
+ case "paragraph":
3967
+ return this.estimateTextHeight(token.text, t.bodyLineHeight);
3968
+ case "code": {
3969
+ const raw = token.text;
3970
+ const lines = Math.max(1, raw.split("\n").length - (raw.endsWith("\n") ? 1 : 0));
3971
+ let h = lines * t.codeLineHeight + t.codePadding * 2;
3972
+ if (this.showCodeLanguage) h += t.codeLangFontSize + 8;
3973
+ return h;
3974
+ }
3975
+ case "hr":
3976
+ return 24;
3977
+ case "blockquote":
3978
+ case "container": {
3979
+ const inner = token.tokens ?? [];
3980
+ let sum = 0;
3981
+ for (const tk of inner) sum += this.estimateBlockHeight(tk) + t.blockGap;
3982
+ return sum + t.quoteInnerGap * 2 + 8;
3983
+ }
3984
+ case "list": {
3985
+ const items = token.items ?? [];
3986
+ let sum = 0;
3987
+ for (const item of items) {
3988
+ for (const tk of item.tokens ?? []) sum += this.estimateBlockHeight(tk) + t.listItemGap;
3989
+ }
3990
+ return sum + t.listGap;
3991
+ }
3992
+ case "table": {
3993
+ const table = token;
3994
+ const rows = (table.rows?.length ?? 0) + (table.header?.length ?? 0);
3995
+ return rows * (t.tableFontSize + 16) + 16;
3996
+ }
3997
+ case "footnoteDef":
3998
+ return this.estimateTextHeight(token.text ?? "", t.bodyLineHeight);
3999
+ case "html":
4000
+ return 200;
4001
+ default:
4002
+ return t.bodyLineHeight;
4003
+ }
4004
+ }
4005
+ /** Estimate a wrapped line count from raw character length against the max width. */
4006
+ estimateTextHeight(text, lineHeight) {
4007
+ const charsPerLine = Math.max(20, Math.floor(this.maxWidth / (this.theme.fontSize * 0.5)));
4008
+ const lines = Math.max(1, Math.ceil(text.length / charsPerLine));
4009
+ return lines * lineHeight;
4010
+ }
4011
+ /**
4012
+ * Mount the blocks in the current window and unmount the rest. The `content`
4013
+ * Stack lays the mounted window contiguously from y = 0; `content.y` is then
4014
+ * set to the skipped prefix sum so the first mounted block lands at its true
4015
+ * y — a numeric spacer, not a wrapper entity.
4016
+ */
4017
+ reconcileVirtual() {
4018
+ const tree = this.virtualHeights;
4019
+ const tokens = this.virtualTokens;
4020
+ if (!tree || !tokens) return;
4021
+ const first = tree.indexAt(Math.max(0, this.virtualScrollY - this.virtualOverscan));
4022
+ const last = tree.indexAt(
4023
+ Math.max(0, this.virtualScrollY + this.virtualViewportH + this.virtualOverscan)
4024
+ );
4025
+ let changed = false;
4026
+ for (const [idx, el] of this.virtualMounted) {
4027
+ if (idx < first || idx > last) {
4028
+ el.destroy();
4029
+ this.virtualMounted.delete(idx);
4030
+ changed = true;
4031
+ }
4032
+ }
4033
+ for (let i = first; i <= last; i++) {
4034
+ if (this.virtualMounted.has(i)) continue;
4035
+ const el = this.renderToken(tokens[i]);
4036
+ if (!el) continue;
4037
+ this.content.add(el);
4038
+ tree.set(i, el.height + this.theme.blockGap);
4039
+ this.virtualMounted.set(i, el);
4040
+ changed = true;
4041
+ }
4042
+ if (changed) this.content.layout();
4043
+ this.content.y = tree.prefix(first);
4044
+ const nextHeight = Math.max(0, tree.total() - this.theme.blockGap);
4045
+ const heightChanged = nextHeight !== this.height;
4046
+ this.width = this.content.width;
4047
+ this.height = nextHeight;
4048
+ if (heightChanged) this.notifyLayoutUpdated();
4049
+ }
3908
4050
  /** Create a frame-coalesced stream bound to this Markdown instance. */
3909
4051
  createStream(options = {}) {
4052
+ if (this.virtualizeBlocks) {
4053
+ throw new Error("Markdown.createStream is not supported when virtualize is enabled");
4054
+ }
3910
4055
  if (this.streamController) {
3911
4056
  throw new Error("Markdown already has an active StreamController");
3912
4057
  }
@@ -3979,6 +4124,18 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
3979
4124
  const next = Math.max(0, maxWidth);
3980
4125
  if (next === this.maxWidth) return this;
3981
4126
  this.maxWidth = next;
4127
+ if (this.virtualizeBlocks && this.virtualTokens && this.virtualHeights) {
4128
+ const tokens = this.virtualTokens;
4129
+ const tree = this.virtualHeights;
4130
+ for (const el of this.virtualMounted.values()) el.destroy();
4131
+ this.virtualMounted.clear();
4132
+ for (let i = 0; i < tokens.length; i++) {
4133
+ tree.set(i, this.estimateBlockHeight(tokens[i]) + this.theme.blockGap);
4134
+ }
4135
+ this.reconcileVirtual();
4136
+ this.scene?.markDirty();
4137
+ return this;
4138
+ }
3982
4139
  let childIndex = 0;
3983
4140
  const children = this.content.children;
3984
4141
  for (const token of this.tokens) {
@@ -4513,6 +4670,9 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
4513
4670
  return this.appendMarkdownCore(chunk);
4514
4671
  }
4515
4672
  appendMarkdownCore(chunk) {
4673
+ if (this.virtualizeBlocks) {
4674
+ throw new Error("Markdown.appendMarkdown is not supported when virtualize is enabled");
4675
+ }
4516
4676
  const before = this.rawMarkdown.length;
4517
4677
  this.consumeFrontMatter(chunk);
4518
4678
  this.streamStats.appends++;
package/dist/index.mjs CHANGED
@@ -2948,7 +2948,15 @@ function renderFencedBlock(source, lang, options) {
2948
2948
  }
2949
2949
 
2950
2950
  // src/Markdown.ts
2951
- import { RichText as RichText2, Stack, Table, Text, Image, UIComponent as UIComponent3 } from "@vectojs/ui";
2951
+ import {
2952
+ RichText as RichText2,
2953
+ RowHeights,
2954
+ Stack,
2955
+ Table,
2956
+ Text,
2957
+ Image,
2958
+ UIComponent as UIComponent3
2959
+ } from "@vectojs/ui";
2952
2960
 
2953
2961
  // src/blockAffordances.ts
2954
2962
  import { Button, measureText as measureText2, UIComponent as UIComponent2 } from "@vectojs/ui";
@@ -3445,6 +3453,19 @@ var Markdown = class _Markdown extends UIComponent3 {
3445
3453
  /** File saver used by the download controls. */
3446
3454
  saveFile;
3447
3455
  activeBlockMetrics = null;
3456
+ /** Whether `opts.virtualize` enabled viewport-culled block materialization. */
3457
+ virtualizeBlocks;
3458
+ /** Overscan margin (px) added above/below the visible window before mounting. */
3459
+ virtualOverscan;
3460
+ /** Top-level tokens that produce a child entity, in document order. */
3461
+ virtualTokens = null;
3462
+ /** Fenwick tree over per-block strides (height + blockGap) when virtualizing. */
3463
+ virtualHeights = null;
3464
+ /** token index → mounted entity, for blocks currently inside `content`. */
3465
+ virtualMounted = /* @__PURE__ */ new Map();
3466
+ /** Visible window top (scroll offset) and height, last set via setVisibleRange. */
3467
+ virtualScrollY = 0;
3468
+ virtualViewportH = 0;
3448
3469
  /**
3449
3470
  * Called after this entity's own `width`/`height` changed because the document
3450
3471
  * was re-laid-out. **This is the hook to wire up when a host has to move or
@@ -3730,6 +3751,9 @@ var Markdown = class _Markdown extends UIComponent3 {
3730
3751
  this.showCodeLanguage = opts.showCodeLanguage ?? false;
3731
3752
  this.writeClipboard = opts.writeClipboard ?? defaultWriteClipboard;
3732
3753
  this.saveFile = opts.saveFile ?? defaultSaveFile;
3754
+ const virt = opts.virtualize;
3755
+ this.virtualizeBlocks = virt === true || typeof virt === "object" && virt !== null;
3756
+ this.virtualOverscan = typeof virt === "object" && virt !== null && typeof virt.overscan === "number" ? virt.overscan : 800;
3733
3757
  this.content = new Stack({
3734
3758
  direction: "vertical",
3735
3759
  gap: this.theme.blockGap
@@ -3842,6 +3866,20 @@ var Markdown = class _Markdown extends UIComponent3 {
3842
3866
  const tokens = lexMarkdown(text, this._userTiming);
3843
3867
  this.setTokens(tokens);
3844
3868
  this.abbreviations = collectAbbreviations(tokens);
3869
+ if (this.virtualizeBlocks) {
3870
+ this.virtualTokens = tokens.filter((token) => this.producesEntity(token));
3871
+ const n = this.virtualTokens.length;
3872
+ this.virtualHeights = new RowHeights(n, 0);
3873
+ for (let i = 0; i < n; i++) {
3874
+ this.virtualHeights.set(
3875
+ i,
3876
+ this.estimateBlockHeight(this.virtualTokens[i]) + this.theme.blockGap
3877
+ );
3878
+ }
3879
+ this.virtualMounted.clear();
3880
+ this.reconcileVirtual();
3881
+ return;
3882
+ }
3845
3883
  for (const token of tokens) {
3846
3884
  const el = this.renderToken(token);
3847
3885
  if (el) {
@@ -3851,8 +3889,123 @@ var Markdown = class _Markdown extends UIComponent3 {
3851
3889
  this.width = this.content.width;
3852
3890
  this.height = this.content.height;
3853
3891
  }
3892
+ /**
3893
+ * Drive the virtualized window from the host's scroll state.
3894
+ *
3895
+ * Only the blocks intersecting `[scrollY − overscan, scrollY + viewportHeight +
3896
+ * overscan]` stay materialized; every other block is a numeric offset in the
3897
+ * height tree, so the scrollbar still reports the full document height while
3898
+ * only a window of blocks exists as entities. A no-op when `virtualize` was
3899
+ * not enabled.
3900
+ */
3901
+ setVisibleRange(scrollY, viewportHeight) {
3902
+ if (!this.virtualizeBlocks) return;
3903
+ this.virtualScrollY = Math.max(0, scrollY);
3904
+ this.virtualViewportH = Math.max(0, viewportHeight);
3905
+ this.reconcileVirtual();
3906
+ }
3907
+ /**
3908
+ * Cheap per-block height estimate from the token source, refined to the exact
3909
+ * measured height when the block first mounts. Coarse by design: the estimate
3910
+ * only has to keep the scrollbar plausible before a block is first seen; the
3911
+ * Fenwick tree corrects the rest.
3912
+ */
3913
+ estimateBlockHeight(token) {
3914
+ const t = this.theme;
3915
+ switch (token.type) {
3916
+ case "heading": {
3917
+ const size = headingSize(t, token.depth);
3918
+ return Math.ceil(size * 1.5) + 8;
3919
+ }
3920
+ case "paragraph":
3921
+ return this.estimateTextHeight(token.text, t.bodyLineHeight);
3922
+ case "code": {
3923
+ const raw = token.text;
3924
+ const lines = Math.max(1, raw.split("\n").length - (raw.endsWith("\n") ? 1 : 0));
3925
+ let h = lines * t.codeLineHeight + t.codePadding * 2;
3926
+ if (this.showCodeLanguage) h += t.codeLangFontSize + 8;
3927
+ return h;
3928
+ }
3929
+ case "hr":
3930
+ return 24;
3931
+ case "blockquote":
3932
+ case "container": {
3933
+ const inner = token.tokens ?? [];
3934
+ let sum = 0;
3935
+ for (const tk of inner) sum += this.estimateBlockHeight(tk) + t.blockGap;
3936
+ return sum + t.quoteInnerGap * 2 + 8;
3937
+ }
3938
+ case "list": {
3939
+ const items = token.items ?? [];
3940
+ let sum = 0;
3941
+ for (const item of items) {
3942
+ for (const tk of item.tokens ?? []) sum += this.estimateBlockHeight(tk) + t.listItemGap;
3943
+ }
3944
+ return sum + t.listGap;
3945
+ }
3946
+ case "table": {
3947
+ const table = token;
3948
+ const rows = (table.rows?.length ?? 0) + (table.header?.length ?? 0);
3949
+ return rows * (t.tableFontSize + 16) + 16;
3950
+ }
3951
+ case "footnoteDef":
3952
+ return this.estimateTextHeight(token.text ?? "", t.bodyLineHeight);
3953
+ case "html":
3954
+ return 200;
3955
+ default:
3956
+ return t.bodyLineHeight;
3957
+ }
3958
+ }
3959
+ /** Estimate a wrapped line count from raw character length against the max width. */
3960
+ estimateTextHeight(text, lineHeight) {
3961
+ const charsPerLine = Math.max(20, Math.floor(this.maxWidth / (this.theme.fontSize * 0.5)));
3962
+ const lines = Math.max(1, Math.ceil(text.length / charsPerLine));
3963
+ return lines * lineHeight;
3964
+ }
3965
+ /**
3966
+ * Mount the blocks in the current window and unmount the rest. The `content`
3967
+ * Stack lays the mounted window contiguously from y = 0; `content.y` is then
3968
+ * set to the skipped prefix sum so the first mounted block lands at its true
3969
+ * y — a numeric spacer, not a wrapper entity.
3970
+ */
3971
+ reconcileVirtual() {
3972
+ const tree = this.virtualHeights;
3973
+ const tokens = this.virtualTokens;
3974
+ if (!tree || !tokens) return;
3975
+ const first = tree.indexAt(Math.max(0, this.virtualScrollY - this.virtualOverscan));
3976
+ const last = tree.indexAt(
3977
+ Math.max(0, this.virtualScrollY + this.virtualViewportH + this.virtualOverscan)
3978
+ );
3979
+ let changed = false;
3980
+ for (const [idx, el] of this.virtualMounted) {
3981
+ if (idx < first || idx > last) {
3982
+ el.destroy();
3983
+ this.virtualMounted.delete(idx);
3984
+ changed = true;
3985
+ }
3986
+ }
3987
+ for (let i = first; i <= last; i++) {
3988
+ if (this.virtualMounted.has(i)) continue;
3989
+ const el = this.renderToken(tokens[i]);
3990
+ if (!el) continue;
3991
+ this.content.add(el);
3992
+ tree.set(i, el.height + this.theme.blockGap);
3993
+ this.virtualMounted.set(i, el);
3994
+ changed = true;
3995
+ }
3996
+ if (changed) this.content.layout();
3997
+ this.content.y = tree.prefix(first);
3998
+ const nextHeight = Math.max(0, tree.total() - this.theme.blockGap);
3999
+ const heightChanged = nextHeight !== this.height;
4000
+ this.width = this.content.width;
4001
+ this.height = nextHeight;
4002
+ if (heightChanged) this.notifyLayoutUpdated();
4003
+ }
3854
4004
  /** Create a frame-coalesced stream bound to this Markdown instance. */
3855
4005
  createStream(options = {}) {
4006
+ if (this.virtualizeBlocks) {
4007
+ throw new Error("Markdown.createStream is not supported when virtualize is enabled");
4008
+ }
3856
4009
  if (this.streamController) {
3857
4010
  throw new Error("Markdown already has an active StreamController");
3858
4011
  }
@@ -3925,6 +4078,18 @@ var Markdown = class _Markdown extends UIComponent3 {
3925
4078
  const next = Math.max(0, maxWidth);
3926
4079
  if (next === this.maxWidth) return this;
3927
4080
  this.maxWidth = next;
4081
+ if (this.virtualizeBlocks && this.virtualTokens && this.virtualHeights) {
4082
+ const tokens = this.virtualTokens;
4083
+ const tree = this.virtualHeights;
4084
+ for (const el of this.virtualMounted.values()) el.destroy();
4085
+ this.virtualMounted.clear();
4086
+ for (let i = 0; i < tokens.length; i++) {
4087
+ tree.set(i, this.estimateBlockHeight(tokens[i]) + this.theme.blockGap);
4088
+ }
4089
+ this.reconcileVirtual();
4090
+ this.scene?.markDirty();
4091
+ return this;
4092
+ }
3928
4093
  let childIndex = 0;
3929
4094
  const children = this.content.children;
3930
4095
  for (const token of this.tokens) {
@@ -4459,6 +4624,9 @@ var Markdown = class _Markdown extends UIComponent3 {
4459
4624
  return this.appendMarkdownCore(chunk);
4460
4625
  }
4461
4626
  appendMarkdownCore(chunk) {
4627
+ if (this.virtualizeBlocks) {
4628
+ throw new Error("Markdown.appendMarkdown is not supported when virtualize is enabled");
4629
+ }
4462
4630
  const before = this.rawMarkdown.length;
4463
4631
  this.consumeFrontMatter(chunk);
4464
4632
  this.streamStats.appends++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/markdown",
3
- "version": "0.20.3",
3
+ "version": "0.21.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },