@vectojs/markdown 0.18.0 → 0.18.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.
@@ -574,6 +574,36 @@ export declare class Markdown extends UIComponent {
574
574
  */
575
575
  private affordanceButtonOptions;
576
576
  private paragraphImage;
577
+ /**
578
+ * Re-position every block after an image whose decode corrected its box.
579
+ *
580
+ * The 16:10 guess above is rarely the real aspect ratio, so without this every
581
+ * sibling below the image keeps the position computed from the guess, and an
582
+ * image taller than the guess renders *under* the paragraph that follows it.
583
+ *
584
+ * A bare `this.content.layout()` is not enough, and that is the part worth
585
+ * knowing: `Stack.layout()` is **not recursive**. It positions its children
586
+ * from the size each child reports at the moment it runs and never asks a
587
+ * child to recompute its own box first. Every image sits behind at least one
588
+ * intermediate container that caches a height — an image-bearing paragraph is
589
+ * a `Stack`, a list item's or blockquote's image is wrapped in a
590
+ * `MarkdownContainer` — so laying out `content` alone re-reads the very boxes
591
+ * the decode just invalidated. Measured on `94d6da3`: a 600x900 portrait in
592
+ * `![alt](…)\n\nAfter.` corrects `Image.height` 480 to 900 while its parent
593
+ * `Stack` stays 480 and the following paragraph stays at `y=496`.
594
+ *
595
+ * So resync bottom-up, from the image's own parent up to `content`, and each
596
+ * level sees a freshly-sized child before it positions anything.
597
+ */
598
+ private reflowAfterImageResize;
599
+ /**
600
+ * Re-derive one `MarkdownContainer`'s cached box from its children.
601
+ *
602
+ * Mirrors how {@link reflowToken}'s `blockquote` / `container` arms and every
603
+ * construction site derive it, so an image resize and a width change converge
604
+ * on the same geometry rather than each having its own arithmetic.
605
+ */
606
+ private resyncWrapperBox;
577
607
  /** One table cell entity, shared by the render arm and the streamed-table path. */
578
608
  private tableCellRichText;
579
609
  /**
package/dist/index.js CHANGED
@@ -4130,16 +4130,83 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
4130
4130
  radius: this.theme.imageRadius,
4131
4131
  onLoad: () => {
4132
4132
  const bmp = img.bitmap;
4133
+ const previousWidth = img.width;
4134
+ const previousHeight = img.height;
4133
4135
  if (bmp && bmp.naturalWidth && bmp.naturalHeight) {
4134
4136
  const aspect = bmp.naturalHeight / bmp.naturalWidth;
4135
4137
  img.width = Math.min(bmp.naturalWidth, availableWidth);
4136
4138
  img.height = Math.round(img.width * aspect);
4137
4139
  }
4140
+ if (img.width !== previousWidth || img.height !== previousHeight) {
4141
+ this.reflowAfterImageResize(img);
4142
+ }
4138
4143
  this.scene?.markDirty();
4139
4144
  }
4140
4145
  });
4141
4146
  return img;
4142
4147
  }
4148
+ /**
4149
+ * Re-position every block after an image whose decode corrected its box.
4150
+ *
4151
+ * The 16:10 guess above is rarely the real aspect ratio, so without this every
4152
+ * sibling below the image keeps the position computed from the guess, and an
4153
+ * image taller than the guess renders *under* the paragraph that follows it.
4154
+ *
4155
+ * A bare `this.content.layout()` is not enough, and that is the part worth
4156
+ * knowing: `Stack.layout()` is **not recursive**. It positions its children
4157
+ * from the size each child reports at the moment it runs and never asks a
4158
+ * child to recompute its own box first. Every image sits behind at least one
4159
+ * intermediate container that caches a height — an image-bearing paragraph is
4160
+ * a `Stack`, a list item's or blockquote's image is wrapped in a
4161
+ * `MarkdownContainer` — so laying out `content` alone re-reads the very boxes
4162
+ * the decode just invalidated. Measured on `94d6da3`: a 600x900 portrait in
4163
+ * `![alt](…)\n\nAfter.` corrects `Image.height` 480 to 900 while its parent
4164
+ * `Stack` stays 480 and the following paragraph stays at `y=496`.
4165
+ *
4166
+ * So resync bottom-up, from the image's own parent up to `content`, and each
4167
+ * level sees a freshly-sized child before it positions anything.
4168
+ */
4169
+ reflowAfterImageResize(img) {
4170
+ let reachesContent = false;
4171
+ for (let node = img.parent; node; node = node.parent) {
4172
+ if (node === this.content) {
4173
+ reachesContent = true;
4174
+ break;
4175
+ }
4176
+ }
4177
+ if (!reachesContent) return;
4178
+ for (let node = img.parent; node; node = node.parent) {
4179
+ if (node instanceof import_ui4.Stack) node.layout();
4180
+ else if (node instanceof MarkdownContainer) this.resyncWrapperBox(node);
4181
+ if (node === this.content) break;
4182
+ }
4183
+ this.width = this.content.width;
4184
+ this.height = this.content.height;
4185
+ this.onLayoutUpdated?.();
4186
+ }
4187
+ /**
4188
+ * Re-derive one `MarkdownContainer`'s cached box from its children.
4189
+ *
4190
+ * Mirrors how {@link reflowToken}'s `blockquote` / `container` arms and every
4191
+ * construction site derive it, so an image resize and a width change converge
4192
+ * on the same geometry rather than each having its own arithmetic.
4193
+ */
4194
+ resyncWrapperBox(wrapper) {
4195
+ const innerStack = wrapper.children.find((c) => c instanceof import_ui4.Stack);
4196
+ const border = wrapper.children.find((c) => c instanceof QuoteBorder);
4197
+ const background = wrapper.children.find((c) => c instanceof ContainerBackground);
4198
+ if (border || background) {
4199
+ const contentHeight = innerStack?.height || 20;
4200
+ if (border instanceof QuoteBorder) border.height = contentHeight;
4201
+ if (background instanceof ContainerBackground) background.height = contentHeight;
4202
+ wrapper.height = Math.max(background?.height ?? 0, border?.height ?? 0, contentHeight);
4203
+ return;
4204
+ }
4205
+ const child = wrapper.children[0];
4206
+ if (!child) return;
4207
+ wrapper.width = child.x + child.width;
4208
+ wrapper.height = child.height;
4209
+ }
4143
4210
  /** One table cell entity, shared by the render arm and the streamed-table path. */
4144
4211
  tableCellRichText(cell, header, t) {
4145
4212
  return new import_ui4.RichText(this.tableCellSpans(cell, t), {
@@ -4251,6 +4318,8 @@ var Markdown = class _Markdown extends import_ui4.UIComponent {
4251
4318
  const wrapper = new MarkdownContainer();
4252
4319
  el.x = indent;
4253
4320
  wrapper.add(el);
4321
+ wrapper.width = el.width + indent;
4322
+ wrapper.height = el.height;
4254
4323
  stack.add(wrapper);
4255
4324
  }
4256
4325
  for (let i = leadChildren.length; i < children.length; i++) {
package/dist/index.mjs CHANGED
@@ -4078,16 +4078,83 @@ var Markdown = class _Markdown extends UIComponent3 {
4078
4078
  radius: this.theme.imageRadius,
4079
4079
  onLoad: () => {
4080
4080
  const bmp = img.bitmap;
4081
+ const previousWidth = img.width;
4082
+ const previousHeight = img.height;
4081
4083
  if (bmp && bmp.naturalWidth && bmp.naturalHeight) {
4082
4084
  const aspect = bmp.naturalHeight / bmp.naturalWidth;
4083
4085
  img.width = Math.min(bmp.naturalWidth, availableWidth);
4084
4086
  img.height = Math.round(img.width * aspect);
4085
4087
  }
4088
+ if (img.width !== previousWidth || img.height !== previousHeight) {
4089
+ this.reflowAfterImageResize(img);
4090
+ }
4086
4091
  this.scene?.markDirty();
4087
4092
  }
4088
4093
  });
4089
4094
  return img;
4090
4095
  }
4096
+ /**
4097
+ * Re-position every block after an image whose decode corrected its box.
4098
+ *
4099
+ * The 16:10 guess above is rarely the real aspect ratio, so without this every
4100
+ * sibling below the image keeps the position computed from the guess, and an
4101
+ * image taller than the guess renders *under* the paragraph that follows it.
4102
+ *
4103
+ * A bare `this.content.layout()` is not enough, and that is the part worth
4104
+ * knowing: `Stack.layout()` is **not recursive**. It positions its children
4105
+ * from the size each child reports at the moment it runs and never asks a
4106
+ * child to recompute its own box first. Every image sits behind at least one
4107
+ * intermediate container that caches a height — an image-bearing paragraph is
4108
+ * a `Stack`, a list item's or blockquote's image is wrapped in a
4109
+ * `MarkdownContainer` — so laying out `content` alone re-reads the very boxes
4110
+ * the decode just invalidated. Measured on `94d6da3`: a 600x900 portrait in
4111
+ * `![alt](…)\n\nAfter.` corrects `Image.height` 480 to 900 while its parent
4112
+ * `Stack` stays 480 and the following paragraph stays at `y=496`.
4113
+ *
4114
+ * So resync bottom-up, from the image's own parent up to `content`, and each
4115
+ * level sees a freshly-sized child before it positions anything.
4116
+ */
4117
+ reflowAfterImageResize(img) {
4118
+ let reachesContent = false;
4119
+ for (let node = img.parent; node; node = node.parent) {
4120
+ if (node === this.content) {
4121
+ reachesContent = true;
4122
+ break;
4123
+ }
4124
+ }
4125
+ if (!reachesContent) return;
4126
+ for (let node = img.parent; node; node = node.parent) {
4127
+ if (node instanceof Stack) node.layout();
4128
+ else if (node instanceof MarkdownContainer) this.resyncWrapperBox(node);
4129
+ if (node === this.content) break;
4130
+ }
4131
+ this.width = this.content.width;
4132
+ this.height = this.content.height;
4133
+ this.onLayoutUpdated?.();
4134
+ }
4135
+ /**
4136
+ * Re-derive one `MarkdownContainer`'s cached box from its children.
4137
+ *
4138
+ * Mirrors how {@link reflowToken}'s `blockquote` / `container` arms and every
4139
+ * construction site derive it, so an image resize and a width change converge
4140
+ * on the same geometry rather than each having its own arithmetic.
4141
+ */
4142
+ resyncWrapperBox(wrapper) {
4143
+ const innerStack = wrapper.children.find((c) => c instanceof Stack);
4144
+ const border = wrapper.children.find((c) => c instanceof QuoteBorder);
4145
+ const background = wrapper.children.find((c) => c instanceof ContainerBackground);
4146
+ if (border || background) {
4147
+ const contentHeight = innerStack?.height || 20;
4148
+ if (border instanceof QuoteBorder) border.height = contentHeight;
4149
+ if (background instanceof ContainerBackground) background.height = contentHeight;
4150
+ wrapper.height = Math.max(background?.height ?? 0, border?.height ?? 0, contentHeight);
4151
+ return;
4152
+ }
4153
+ const child = wrapper.children[0];
4154
+ if (!child) return;
4155
+ wrapper.width = child.x + child.width;
4156
+ wrapper.height = child.height;
4157
+ }
4091
4158
  /** One table cell entity, shared by the render arm and the streamed-table path. */
4092
4159
  tableCellRichText(cell, header, t) {
4093
4160
  return new RichText2(this.tableCellSpans(cell, t), {
@@ -4199,6 +4266,8 @@ var Markdown = class _Markdown extends UIComponent3 {
4199
4266
  const wrapper = new MarkdownContainer();
4200
4267
  el.x = indent;
4201
4268
  wrapper.add(el);
4269
+ wrapper.width = el.width + indent;
4270
+ wrapper.height = el.height;
4202
4271
  stack.add(wrapper);
4203
4272
  }
4204
4273
  for (let i = leadChildren.length; i < children.length; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/markdown",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },