@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.
- package/dist/Markdown.d.ts +79 -6
- package/dist/index.js +141 -9
- package/dist/index.mjs +141 -9
- package/package.json +1 -1
package/dist/Markdown.d.ts
CHANGED
|
@@ -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
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
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
|
+
* `\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
|
|
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
|
-
*
|
|
2860
|
-
*
|
|
2861
|
-
*
|
|
2862
|
-
*
|
|
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.
|
|
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
|
+
* `\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
|
-
|
|
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
|
|
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
|
-
*
|
|
2808
|
-
*
|
|
2809
|
-
*
|
|
2810
|
-
*
|
|
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.
|
|
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
|
+
* `\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
|
-
|
|
4876
|
-
this.onLayoutUpdated();
|
|
4877
|
-
}
|
|
5009
|
+
this.notifyLayoutUpdated();
|
|
4878
5010
|
}
|
|
4879
5011
|
/**
|
|
4880
5012
|
* Render one nested block with a temporary width/margin context while
|