@vectojs/markdown 0.18.0 → 0.18.2

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.
@@ -94,14 +94,40 @@ export declare class Markdown extends UIComponent {
94
94
  saveFile: (filename: string, content: string, mimeType: string) => void;
95
95
  private activeBlockMetrics;
96
96
  /**
97
- * Called after a streamed append has re-laid-out the document.
98
- *
99
- * Not required for a `VirtualList` to track a streaming row's height: the list
100
- * re-reads `height` on every mounted row each frame, so it sees this entity grow
101
- * without being told. Prefer that over wiring this up — it fires from the append
102
- * path only, **not** from `setContent()`, so it is not a complete size signal.
97
+ * Called after this entity's own `width`/`height` changed because the document
98
+ * was re-laid-out. **This is the hook to wire up when a host has to move or
99
+ * resize anything positioned below the document.**
100
+ *
101
+ * Fires from three paths, all of which republish `width`/`height` from
102
+ * `content`:
103
+ *
104
+ * - a streamed append (`updateTokens`),
105
+ * - a width change ({@link setMaxWidth}),
106
+ * - a paragraph image whose decoded bitmap corrected the guessed aspect ratio
107
+ * (`reflowAfterImageResize`) — the guess is a flat 16:10, so this one fires
108
+ * on essentially every document containing an image, and a host that misses
109
+ * it leaves every block below the image overlapping it.
110
+ *
111
+ * It does **not** fire from `setContent()`, which replaces the whole document
112
+ * and is a call the host already made, so this is a re-layout signal rather
113
+ * than a complete size signal.
114
+ *
115
+ * A `VirtualList` needs none of this to track a streaming row: it re-reads
116
+ * `height` on every mounted row each frame, so it sees this entity grow without
117
+ * being told. Prefer that where it applies. Reach for this callback when the
118
+ * host owns absolute positions of its own — a page that stacks navigation, a
119
+ * footer and a scroll height under the document has to recompute them here.
120
+ *
121
+ * There is no `onHeightChanged`. That name has been assigned by real callers
122
+ * through an `as unknown as` cast, which compiles, silences the type error and
123
+ * then never fires; if a layout callback appears dead, check the name first.
103
124
  */
104
125
  onLayoutUpdated?: () => void;
126
+ /**
127
+ * Latch for the miswired-hook warning, so a streaming document warns once
128
+ * rather than on every chunk.
129
+ */
130
+ private hasWarnedLayoutHookName;
105
131
  /**
106
132
  * The document's BODY text — everything after any front matter block.
107
133
  *
@@ -574,6 +600,53 @@ export declare class Markdown extends UIComponent {
574
600
  */
575
601
  private affordanceButtonOptions;
576
602
  private paragraphImage;
603
+ /**
604
+ * Re-position every block after an image whose decode corrected its box.
605
+ *
606
+ * The 16:10 guess above is rarely the real aspect ratio, so without this every
607
+ * sibling below the image keeps the position computed from the guess, and an
608
+ * image taller than the guess renders *under* the paragraph that follows it.
609
+ *
610
+ * A bare `this.content.layout()` is not enough, and that is the part worth
611
+ * knowing: `Stack.layout()` is **not recursive**. It positions its children
612
+ * from the size each child reports at the moment it runs and never asks a
613
+ * child to recompute its own box first. Every image sits behind at least one
614
+ * intermediate container that caches a height — an image-bearing paragraph is
615
+ * a `Stack`, a list item's or blockquote's image is wrapped in a
616
+ * `MarkdownContainer` — so laying out `content` alone re-reads the very boxes
617
+ * the decode just invalidated. Measured on `94d6da3`: a 600x900 portrait in
618
+ * `![alt](…)\n\nAfter.` corrects `Image.height` 480 to 900 while its parent
619
+ * `Stack` stays 480 and the following paragraph stays at `y=496`.
620
+ *
621
+ * So resync bottom-up, from the image's own parent up to `content`, and each
622
+ * level sees a freshly-sized child before it positions anything.
623
+ */
624
+ private reflowAfterImageResize;
625
+ /**
626
+ * Publish a completed re-layout to the host.
627
+ *
628
+ * Every path that republishes `width`/`height` from `content` ends here rather
629
+ * than calling {@link onLayoutUpdated} directly, so the misuse check below is
630
+ * reached however the re-layout was triggered.
631
+ *
632
+ * The check exists because the failure it catches is silent and was found in
633
+ * production, not in review. A host wired its reflow to `onHeightChanged` — a
634
+ * name this class has never had — through an `as unknown as` cast. The cast
635
+ * satisfied the compiler, the callback never fired, and every post containing
636
+ * an image stayed laid out against the guessed 16:10 aspect ratio with a stale
637
+ * document scroll height. Nothing in the type system, the tests or the console
638
+ * said anything. A property that is *only ever assigned* has no read site to
639
+ * fail, so the one place that can notice is the moment we would have called it.
640
+ */
641
+ private notifyLayoutUpdated;
642
+ /**
643
+ * Re-derive one `MarkdownContainer`'s cached box from its children.
644
+ *
645
+ * Mirrors how {@link reflowToken}'s `blockquote` / `container` arms and every
646
+ * construction site derive it, so an image resize and a width change converge
647
+ * on the same geometry rather than each having its own arithmetic.
648
+ */
649
+ private resyncWrapperBox;
577
650
  /** One table cell entity, shared by the render arm and the streamed-table path. */
578
651
  private tableCellRichText;
579
652
  /**
package/dist/index.js CHANGED
@@ -2787,6 +2787,12 @@ import_marked.marked.use({
2787
2787
  }
2788
2788
  ]
2789
2789
  });
2790
+ var MISWIRED_LAYOUT_HOOK_NAMES = [
2791
+ "onHeightChanged",
2792
+ "onHeightChange",
2793
+ "onLayoutUpdate",
2794
+ "onResize"
2795
+ ];
2790
2796
  var markdownWorker = null;
2791
2797
  var workerIdCounter = 0;
2792
2798
  var workerInstanceCounter = 0;
@@ -2854,14 +2860,40 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
2854
2860
  saveFile;
2855
2861
  activeBlockMetrics = null;
2856
2862
  /**
2857
- * Called after a streamed append has re-laid-out the document.
2863
+ * Called after this entity's own `width`/`height` changed because the document
2864
+ * was re-laid-out. **This is the hook to wire up when a host has to move or
2865
+ * resize anything positioned below the document.**
2866
+ *
2867
+ * Fires from three paths, all of which republish `width`/`height` from
2868
+ * `content`:
2869
+ *
2870
+ * - a streamed append (`updateTokens`),
2871
+ * - a width change ({@link setMaxWidth}),
2872
+ * - a paragraph image whose decoded bitmap corrected the guessed aspect ratio
2873
+ * (`reflowAfterImageResize`) — the guess is a flat 16:10, so this one fires
2874
+ * on essentially every document containing an image, and a host that misses
2875
+ * it leaves every block below the image overlapping it.
2858
2876
  *
2859
- * Not required for a `VirtualList` to track a streaming row's height: the list
2860
- * re-reads `height` on every mounted row each frame, so it sees this entity grow
2861
- * without being told. Prefer that over wiring this up — it fires from the append
2862
- * path only, **not** from `setContent()`, so it is not a complete size signal.
2877
+ * It does **not** fire from `setContent()`, which replaces the whole document
2878
+ * and is a call the host already made, so this is a re-layout signal rather
2879
+ * than a complete size signal.
2880
+ *
2881
+ * A `VirtualList` needs none of this to track a streaming row: it re-reads
2882
+ * `height` on every mounted row each frame, so it sees this entity grow without
2883
+ * being told. Prefer that where it applies. Reach for this callback when the
2884
+ * host owns absolute positions of its own — a page that stacks navigation, a
2885
+ * footer and a scroll height under the document has to recompute them here.
2886
+ *
2887
+ * There is no `onHeightChanged`. That name has been assigned by real callers
2888
+ * through an `as unknown as` cast, which compiles, silences the type error and
2889
+ * then never fires; if a layout callback appears dead, check the name first.
2863
2890
  */
2864
2891
  onLayoutUpdated;
2892
+ /**
2893
+ * Latch for the miswired-hook warning, so a streaming document warns once
2894
+ * rather than on every chunk.
2895
+ */
2896
+ hasWarnedLayoutHookName = false;
2865
2897
  /**
2866
2898
  * The document's BODY text — everything after any front matter block.
2867
2899
  *
@@ -3312,7 +3344,7 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
3312
3344
  this.content.layout();
3313
3345
  this.width = this.content.width;
3314
3346
  this.height = this.content.height;
3315
- this.onLayoutUpdated?.();
3347
+ this.notifyLayoutUpdated();
3316
3348
  this.scene?.markDirty();
3317
3349
  return this;
3318
3350
  }
@@ -4130,16 +4162,116 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
4130
4162
  radius: this.theme.imageRadius,
4131
4163
  onLoad: () => {
4132
4164
  const bmp = img.bitmap;
4165
+ const previousWidth = img.width;
4166
+ const previousHeight = img.height;
4133
4167
  if (bmp && bmp.naturalWidth && bmp.naturalHeight) {
4134
4168
  const aspect = bmp.naturalHeight / bmp.naturalWidth;
4135
4169
  img.width = Math.min(bmp.naturalWidth, availableWidth);
4136
4170
  img.height = Math.round(img.width * aspect);
4137
4171
  }
4172
+ if (img.width !== previousWidth || img.height !== previousHeight) {
4173
+ this.reflowAfterImageResize(img);
4174
+ }
4138
4175
  this.scene?.markDirty();
4139
4176
  }
4140
4177
  });
4141
4178
  return img;
4142
4179
  }
4180
+ /**
4181
+ * Re-position every block after an image whose decode corrected its box.
4182
+ *
4183
+ * The 16:10 guess above is rarely the real aspect ratio, so without this every
4184
+ * sibling below the image keeps the position computed from the guess, and an
4185
+ * image taller than the guess renders *under* the paragraph that follows it.
4186
+ *
4187
+ * A bare `this.content.layout()` is not enough, and that is the part worth
4188
+ * knowing: `Stack.layout()` is **not recursive**. It positions its children
4189
+ * from the size each child reports at the moment it runs and never asks a
4190
+ * child to recompute its own box first. Every image sits behind at least one
4191
+ * intermediate container that caches a height — an image-bearing paragraph is
4192
+ * a `Stack`, a list item's or blockquote's image is wrapped in a
4193
+ * `MarkdownContainer` — so laying out `content` alone re-reads the very boxes
4194
+ * the decode just invalidated. Measured on `94d6da3`: a 600x900 portrait in
4195
+ * `![alt](…)\n\nAfter.` corrects `Image.height` 480 to 900 while its parent
4196
+ * `Stack` stays 480 and the following paragraph stays at `y=496`.
4197
+ *
4198
+ * So resync bottom-up, from the image's own parent up to `content`, and each
4199
+ * level sees a freshly-sized child before it positions anything.
4200
+ */
4201
+ reflowAfterImageResize(img) {
4202
+ let reachesContent = false;
4203
+ for (let node = img.parent; node; node = node.parent) {
4204
+ if (node === this.content) {
4205
+ reachesContent = true;
4206
+ break;
4207
+ }
4208
+ }
4209
+ if (!reachesContent) return;
4210
+ for (let node = img.parent; node; node = node.parent) {
4211
+ if (node instanceof import_ui4.Stack) node.layout();
4212
+ else if (node instanceof MarkdownContainer) this.resyncWrapperBox(node);
4213
+ if (node === this.content) break;
4214
+ }
4215
+ this.width = this.content.width;
4216
+ this.height = this.content.height;
4217
+ this.notifyLayoutUpdated();
4218
+ }
4219
+ /**
4220
+ * Publish a completed re-layout to the host.
4221
+ *
4222
+ * Every path that republishes `width`/`height` from `content` ends here rather
4223
+ * than calling {@link onLayoutUpdated} directly, so the misuse check below is
4224
+ * reached however the re-layout was triggered.
4225
+ *
4226
+ * The check exists because the failure it catches is silent and was found in
4227
+ * production, not in review. A host wired its reflow to `onHeightChanged` — a
4228
+ * name this class has never had — through an `as unknown as` cast. The cast
4229
+ * satisfied the compiler, the callback never fired, and every post containing
4230
+ * an image stayed laid out against the guessed 16:10 aspect ratio with a stale
4231
+ * document scroll height. Nothing in the type system, the tests or the console
4232
+ * said anything. A property that is *only ever assigned* has no read site to
4233
+ * fail, so the one place that can notice is the moment we would have called it.
4234
+ */
4235
+ notifyLayoutUpdated() {
4236
+ if (this.onLayoutUpdated) {
4237
+ this.onLayoutUpdated();
4238
+ return;
4239
+ }
4240
+ if (!this.hasWarnedLayoutHookName) {
4241
+ const wrongName = MISWIRED_LAYOUT_HOOK_NAMES.find(
4242
+ (name) => typeof this[name] === "function"
4243
+ );
4244
+ if (wrongName) {
4245
+ this.hasWarnedLayoutHookName = true;
4246
+ console.warn(
4247
+ `[VectoJS] Markdown.${wrongName} is not a VectoJS callback and will never fire. The document just re-laid-out and nothing was notified. Assign \`onLayoutUpdated\` instead \u2014 anything positioned below the document needs it, and a paragraph image corrects its guessed aspect ratio on decode, so a missed signal leaves following blocks overlapping the image.`
4248
+ );
4249
+ }
4250
+ }
4251
+ }
4252
+ /**
4253
+ * Re-derive one `MarkdownContainer`'s cached box from its children.
4254
+ *
4255
+ * Mirrors how {@link reflowToken}'s `blockquote` / `container` arms and every
4256
+ * construction site derive it, so an image resize and a width change converge
4257
+ * on the same geometry rather than each having its own arithmetic.
4258
+ */
4259
+ resyncWrapperBox(wrapper) {
4260
+ const innerStack = wrapper.children.find((c) => c instanceof import_ui4.Stack);
4261
+ const border = wrapper.children.find((c) => c instanceof QuoteBorder);
4262
+ const background = wrapper.children.find((c) => c instanceof ContainerBackground);
4263
+ if (border || background) {
4264
+ const contentHeight = innerStack?.height || 20;
4265
+ if (border instanceof QuoteBorder) border.height = contentHeight;
4266
+ if (background instanceof ContainerBackground) background.height = contentHeight;
4267
+ wrapper.height = Math.max(background?.height ?? 0, border?.height ?? 0, contentHeight);
4268
+ return;
4269
+ }
4270
+ const child = wrapper.children[0];
4271
+ if (!child) return;
4272
+ wrapper.width = child.x + child.width;
4273
+ wrapper.height = child.height;
4274
+ }
4143
4275
  /** One table cell entity, shared by the render arm and the streamed-table path. */
4144
4276
  tableCellRichText(cell, header, t) {
4145
4277
  return new import_ui4.RichText(this.tableCellSpans(cell, t), {
@@ -4251,6 +4383,8 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
4251
4383
  const wrapper = new MarkdownContainer();
4252
4384
  el.x = indent;
4253
4385
  wrapper.add(el);
4386
+ wrapper.width = el.width + indent;
4387
+ wrapper.height = el.height;
4254
4388
  stack.add(wrapper);
4255
4389
  }
4256
4390
  for (let i = leadChildren.length; i < children.length; i++) {
@@ -4924,9 +5058,7 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
4924
5058
  this.width = this.content.width;
4925
5059
  this.height = this.content.height;
4926
5060
  this.scene?.markDirty();
4927
- if (this.onLayoutUpdated) {
4928
- this.onLayoutUpdated();
4929
- }
5061
+ this.notifyLayoutUpdated();
4930
5062
  }
4931
5063
  /**
4932
5064
  * Render one nested block with a temporary width/margin context while
package/dist/index.mjs CHANGED
@@ -2735,6 +2735,12 @@ marked.use({
2735
2735
  }
2736
2736
  ]
2737
2737
  });
2738
+ var MISWIRED_LAYOUT_HOOK_NAMES = [
2739
+ "onHeightChanged",
2740
+ "onHeightChange",
2741
+ "onLayoutUpdate",
2742
+ "onResize"
2743
+ ];
2738
2744
  var markdownWorker = null;
2739
2745
  var workerIdCounter = 0;
2740
2746
  var workerInstanceCounter = 0;
@@ -2802,14 +2808,40 @@ var Markdown = class _Markdown extends UIComponent3 {
2802
2808
  saveFile;
2803
2809
  activeBlockMetrics = null;
2804
2810
  /**
2805
- * Called after a streamed append has re-laid-out the document.
2811
+ * Called after this entity's own `width`/`height` changed because the document
2812
+ * was re-laid-out. **This is the hook to wire up when a host has to move or
2813
+ * resize anything positioned below the document.**
2814
+ *
2815
+ * Fires from three paths, all of which republish `width`/`height` from
2816
+ * `content`:
2817
+ *
2818
+ * - a streamed append (`updateTokens`),
2819
+ * - a width change ({@link setMaxWidth}),
2820
+ * - a paragraph image whose decoded bitmap corrected the guessed aspect ratio
2821
+ * (`reflowAfterImageResize`) — the guess is a flat 16:10, so this one fires
2822
+ * on essentially every document containing an image, and a host that misses
2823
+ * it leaves every block below the image overlapping it.
2806
2824
  *
2807
- * Not required for a `VirtualList` to track a streaming row's height: the list
2808
- * re-reads `height` on every mounted row each frame, so it sees this entity grow
2809
- * without being told. Prefer that over wiring this up — it fires from the append
2810
- * path only, **not** from `setContent()`, so it is not a complete size signal.
2825
+ * It does **not** fire from `setContent()`, which replaces the whole document
2826
+ * and is a call the host already made, so this is a re-layout signal rather
2827
+ * than a complete size signal.
2828
+ *
2829
+ * A `VirtualList` needs none of this to track a streaming row: it re-reads
2830
+ * `height` on every mounted row each frame, so it sees this entity grow without
2831
+ * being told. Prefer that where it applies. Reach for this callback when the
2832
+ * host owns absolute positions of its own — a page that stacks navigation, a
2833
+ * footer and a scroll height under the document has to recompute them here.
2834
+ *
2835
+ * There is no `onHeightChanged`. That name has been assigned by real callers
2836
+ * through an `as unknown as` cast, which compiles, silences the type error and
2837
+ * then never fires; if a layout callback appears dead, check the name first.
2811
2838
  */
2812
2839
  onLayoutUpdated;
2840
+ /**
2841
+ * Latch for the miswired-hook warning, so a streaming document warns once
2842
+ * rather than on every chunk.
2843
+ */
2844
+ hasWarnedLayoutHookName = false;
2813
2845
  /**
2814
2846
  * The document's BODY text — everything after any front matter block.
2815
2847
  *
@@ -3260,7 +3292,7 @@ var Markdown = class _Markdown extends UIComponent3 {
3260
3292
  this.content.layout();
3261
3293
  this.width = this.content.width;
3262
3294
  this.height = this.content.height;
3263
- this.onLayoutUpdated?.();
3295
+ this.notifyLayoutUpdated();
3264
3296
  this.scene?.markDirty();
3265
3297
  return this;
3266
3298
  }
@@ -4078,16 +4110,116 @@ var Markdown = class _Markdown extends UIComponent3 {
4078
4110
  radius: this.theme.imageRadius,
4079
4111
  onLoad: () => {
4080
4112
  const bmp = img.bitmap;
4113
+ const previousWidth = img.width;
4114
+ const previousHeight = img.height;
4081
4115
  if (bmp && bmp.naturalWidth && bmp.naturalHeight) {
4082
4116
  const aspect = bmp.naturalHeight / bmp.naturalWidth;
4083
4117
  img.width = Math.min(bmp.naturalWidth, availableWidth);
4084
4118
  img.height = Math.round(img.width * aspect);
4085
4119
  }
4120
+ if (img.width !== previousWidth || img.height !== previousHeight) {
4121
+ this.reflowAfterImageResize(img);
4122
+ }
4086
4123
  this.scene?.markDirty();
4087
4124
  }
4088
4125
  });
4089
4126
  return img;
4090
4127
  }
4128
+ /**
4129
+ * Re-position every block after an image whose decode corrected its box.
4130
+ *
4131
+ * The 16:10 guess above is rarely the real aspect ratio, so without this every
4132
+ * sibling below the image keeps the position computed from the guess, and an
4133
+ * image taller than the guess renders *under* the paragraph that follows it.
4134
+ *
4135
+ * A bare `this.content.layout()` is not enough, and that is the part worth
4136
+ * knowing: `Stack.layout()` is **not recursive**. It positions its children
4137
+ * from the size each child reports at the moment it runs and never asks a
4138
+ * child to recompute its own box first. Every image sits behind at least one
4139
+ * intermediate container that caches a height — an image-bearing paragraph is
4140
+ * a `Stack`, a list item's or blockquote's image is wrapped in a
4141
+ * `MarkdownContainer` — so laying out `content` alone re-reads the very boxes
4142
+ * the decode just invalidated. Measured on `94d6da3`: a 600x900 portrait in
4143
+ * `![alt](…)\n\nAfter.` corrects `Image.height` 480 to 900 while its parent
4144
+ * `Stack` stays 480 and the following paragraph stays at `y=496`.
4145
+ *
4146
+ * So resync bottom-up, from the image's own parent up to `content`, and each
4147
+ * level sees a freshly-sized child before it positions anything.
4148
+ */
4149
+ reflowAfterImageResize(img) {
4150
+ let reachesContent = false;
4151
+ for (let node = img.parent; node; node = node.parent) {
4152
+ if (node === this.content) {
4153
+ reachesContent = true;
4154
+ break;
4155
+ }
4156
+ }
4157
+ if (!reachesContent) return;
4158
+ for (let node = img.parent; node; node = node.parent) {
4159
+ if (node instanceof Stack) node.layout();
4160
+ else if (node instanceof MarkdownContainer) this.resyncWrapperBox(node);
4161
+ if (node === this.content) break;
4162
+ }
4163
+ this.width = this.content.width;
4164
+ this.height = this.content.height;
4165
+ this.notifyLayoutUpdated();
4166
+ }
4167
+ /**
4168
+ * Publish a completed re-layout to the host.
4169
+ *
4170
+ * Every path that republishes `width`/`height` from `content` ends here rather
4171
+ * than calling {@link onLayoutUpdated} directly, so the misuse check below is
4172
+ * reached however the re-layout was triggered.
4173
+ *
4174
+ * The check exists because the failure it catches is silent and was found in
4175
+ * production, not in review. A host wired its reflow to `onHeightChanged` — a
4176
+ * name this class has never had — through an `as unknown as` cast. The cast
4177
+ * satisfied the compiler, the callback never fired, and every post containing
4178
+ * an image stayed laid out against the guessed 16:10 aspect ratio with a stale
4179
+ * document scroll height. Nothing in the type system, the tests or the console
4180
+ * said anything. A property that is *only ever assigned* has no read site to
4181
+ * fail, so the one place that can notice is the moment we would have called it.
4182
+ */
4183
+ notifyLayoutUpdated() {
4184
+ if (this.onLayoutUpdated) {
4185
+ this.onLayoutUpdated();
4186
+ return;
4187
+ }
4188
+ if (!this.hasWarnedLayoutHookName) {
4189
+ const wrongName = MISWIRED_LAYOUT_HOOK_NAMES.find(
4190
+ (name) => typeof this[name] === "function"
4191
+ );
4192
+ if (wrongName) {
4193
+ this.hasWarnedLayoutHookName = true;
4194
+ console.warn(
4195
+ `[VectoJS] Markdown.${wrongName} is not a VectoJS callback and will never fire. The document just re-laid-out and nothing was notified. Assign \`onLayoutUpdated\` instead \u2014 anything positioned below the document needs it, and a paragraph image corrects its guessed aspect ratio on decode, so a missed signal leaves following blocks overlapping the image.`
4196
+ );
4197
+ }
4198
+ }
4199
+ }
4200
+ /**
4201
+ * Re-derive one `MarkdownContainer`'s cached box from its children.
4202
+ *
4203
+ * Mirrors how {@link reflowToken}'s `blockquote` / `container` arms and every
4204
+ * construction site derive it, so an image resize and a width change converge
4205
+ * on the same geometry rather than each having its own arithmetic.
4206
+ */
4207
+ resyncWrapperBox(wrapper) {
4208
+ const innerStack = wrapper.children.find((c) => c instanceof Stack);
4209
+ const border = wrapper.children.find((c) => c instanceof QuoteBorder);
4210
+ const background = wrapper.children.find((c) => c instanceof ContainerBackground);
4211
+ if (border || background) {
4212
+ const contentHeight = innerStack?.height || 20;
4213
+ if (border instanceof QuoteBorder) border.height = contentHeight;
4214
+ if (background instanceof ContainerBackground) background.height = contentHeight;
4215
+ wrapper.height = Math.max(background?.height ?? 0, border?.height ?? 0, contentHeight);
4216
+ return;
4217
+ }
4218
+ const child = wrapper.children[0];
4219
+ if (!child) return;
4220
+ wrapper.width = child.x + child.width;
4221
+ wrapper.height = child.height;
4222
+ }
4091
4223
  /** One table cell entity, shared by the render arm and the streamed-table path. */
4092
4224
  tableCellRichText(cell, header, t) {
4093
4225
  return new RichText2(this.tableCellSpans(cell, t), {
@@ -4199,6 +4331,8 @@ var Markdown = class _Markdown extends UIComponent3 {
4199
4331
  const wrapper = new MarkdownContainer();
4200
4332
  el.x = indent;
4201
4333
  wrapper.add(el);
4334
+ wrapper.width = el.width + indent;
4335
+ wrapper.height = el.height;
4202
4336
  stack.add(wrapper);
4203
4337
  }
4204
4338
  for (let i = leadChildren.length; i < children.length; i++) {
@@ -4872,9 +5006,7 @@ var Markdown = class _Markdown extends UIComponent3 {
4872
5006
  this.width = this.content.width;
4873
5007
  this.height = this.content.height;
4874
5008
  this.scene?.markDirty();
4875
- if (this.onLayoutUpdated) {
4876
- this.onLayoutUpdated();
4877
- }
5009
+ this.notifyLayoutUpdated();
4878
5010
  }
4879
5011
  /**
4880
5012
  * Render one nested block with a temporary width/margin context while
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/markdown",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },