@vectojs/markdown 0.10.0 → 0.12.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.
- package/dist/Markdown.d.ts +68 -0
- package/dist/index.js +120 -3
- package/dist/index.mjs +120 -3
- package/package.json +1 -1
package/dist/Markdown.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export declare class CodeBlock extends UIComponent {
|
|
|
45
45
|
private rawLines;
|
|
46
46
|
private cellWidth;
|
|
47
47
|
private source;
|
|
48
|
+
/** Bumped by {@link buildLines} and {@link setSelectable}; read by `Scene`. */
|
|
49
|
+
private contentEpoch;
|
|
48
50
|
private lang;
|
|
49
51
|
private theme;
|
|
50
52
|
private lineH;
|
|
@@ -56,6 +58,7 @@ export declare class CodeBlock extends UIComponent {
|
|
|
56
58
|
setCode(code: string, lang?: string): this;
|
|
57
59
|
/** Enable or disable browser-native selection for this code block. */
|
|
58
60
|
setSelectable(selectable: boolean): this;
|
|
61
|
+
getContentEpoch(): number;
|
|
59
62
|
/**
|
|
60
63
|
* Change the block's box width.
|
|
61
64
|
*
|
|
@@ -542,6 +545,71 @@ export declare class Markdown extends UIComponent {
|
|
|
542
545
|
private paragraphImage;
|
|
543
546
|
/** One table cell entity, shared by the render arm and the streamed-table path. */
|
|
544
547
|
private tableCellRichText;
|
|
548
|
+
/**
|
|
549
|
+
* Token types a list item's fast path can render as one `RichText`.
|
|
550
|
+
*
|
|
551
|
+
* An ALLOWLIST, deliberately, following `markstream-vue`'s
|
|
552
|
+
* `SIMPLE_INLINE_TYPES` (`SimpleInlineRenderer/simpleInline.ts:15-31`): a block
|
|
553
|
+
* type is excluded by OMISSION, so a token this renderer has never heard of
|
|
554
|
+
* falls out of the fast path automatically instead of being silently flattened
|
|
555
|
+
* to its raw text. A denylist fails the other way, and the failure is quiet —
|
|
556
|
+
* a formula painted as literal TeX rather than an error — which is why this
|
|
557
|
+
* defect survived so long.
|
|
558
|
+
*
|
|
559
|
+
* Deliberately small, because a list item's DIRECT children are far less varied
|
|
560
|
+
* than they look. Probed against marked 18.0.7: every inline construct
|
|
561
|
+
* (`strong`, `em`, `del`, `codespan`, `link`, `image`, `br`, `escape`, `html`,
|
|
562
|
+
* `inlineMath`) arrives nested one level DEEPER, inside a container whose own
|
|
563
|
+
* type is `text` — so a tight item's direct child list is `text` and nothing
|
|
564
|
+
* else. Listing those inline types here would be dead code.
|
|
565
|
+
*
|
|
566
|
+
* `space` and `checkbox` are included because both are inert here. A blank line
|
|
567
|
+
* between an item's paragraph and its block sibling produces a `space`, and
|
|
568
|
+
* marked unshifts a `checkbox` into every TIGHT GFM task item — the box itself
|
|
569
|
+
* is drawn from `item.task`/`item.checked` by `listItemSpans`, so the token
|
|
570
|
+
* renders nothing on its own. Omitting `checkbox` sent every task item down the
|
|
571
|
+
* block path and moved its marker into a nested entity, which broke four
|
|
572
|
+
* task-list assertions in `Markdown.test.ts`.
|
|
573
|
+
*/
|
|
574
|
+
private static readonly INLINE_ITEM_TOKENS;
|
|
575
|
+
/**
|
|
576
|
+
* Does this item consist purely of inline content?
|
|
577
|
+
*
|
|
578
|
+
* True keeps the single-`RichText` fast path, which is not merely an
|
|
579
|
+
* optimization: `updateStreamedList` reuses `stack.children[i]` by calling
|
|
580
|
+
* `setSpans` on it, so an item that becomes a `Stack` forfeits streamed reuse
|
|
581
|
+
* for its entire list. Only pay for a block container when an item holds a
|
|
582
|
+
* block.
|
|
583
|
+
*
|
|
584
|
+
* A lone `paragraph` counts as inline. A LOOSE list re-lexes every item's
|
|
585
|
+
* inline content from `text` to `paragraph` — adding one blank line anywhere
|
|
586
|
+
* flips `token.loose` for the whole list — so treating a single paragraph as a
|
|
587
|
+
* block would drop the fast path for every item of every loose list, the common
|
|
588
|
+
* shape in real prose, for no rendering benefit.
|
|
589
|
+
*/
|
|
590
|
+
private itemIsInlineOnly;
|
|
591
|
+
/**
|
|
592
|
+
* Build a list item that holds block-level children.
|
|
593
|
+
*
|
|
594
|
+
* The item becomes a vertical `Stack`: its leading inline run (carrying the
|
|
595
|
+
* marker) first, then every remaining child rendered through the same
|
|
596
|
+
* `renderToken` the document level uses, indented to clear the marker.
|
|
597
|
+
*
|
|
598
|
+
* Recursing rather than special-casing the types we know about is the point — a
|
|
599
|
+
* display formula, a fence, a table, a blockquote, a nested list, an `hr` and a
|
|
600
|
+
* second paragraph all render exactly as they would at indent 0, and a block
|
|
601
|
+
* type added later works here for free.
|
|
602
|
+
*
|
|
603
|
+
* Only the FIRST child can be the lead. Everything after it becomes a block,
|
|
604
|
+
* including a second `paragraph`: an item's two paragraphs are two blocks, and
|
|
605
|
+
* folding them into the lead run would concatenate them into one line with no
|
|
606
|
+
* separation.
|
|
607
|
+
*
|
|
608
|
+
* The lead `RichText` is emitted even when the item has no inline text, because
|
|
609
|
+
* it carries the marker — an item that is nothing but a formula still shows its
|
|
610
|
+
* bullet or ordinal.
|
|
611
|
+
*/
|
|
612
|
+
private listItemBlockStack;
|
|
545
613
|
private listItemSpans;
|
|
546
614
|
/** Construct the `RichText` for one list item. */
|
|
547
615
|
private listItemRichText;
|
package/dist/index.js
CHANGED
|
@@ -1127,6 +1127,8 @@ var CodeBlock = class extends import_ui.UIComponent {
|
|
|
1127
1127
|
rawLines = null;
|
|
1128
1128
|
cellWidth = 0;
|
|
1129
1129
|
source;
|
|
1130
|
+
/** Bumped by {@link buildLines} and {@link setSelectable}; read by `Scene`. */
|
|
1131
|
+
contentEpoch = 0;
|
|
1130
1132
|
lang;
|
|
1131
1133
|
theme;
|
|
1132
1134
|
lineH = 24;
|
|
@@ -1155,9 +1157,13 @@ var CodeBlock = class extends import_ui.UIComponent {
|
|
|
1155
1157
|
/** Enable or disable browser-native selection for this code block. */
|
|
1156
1158
|
setSelectable(selectable) {
|
|
1157
1159
|
this.selectable = selectable;
|
|
1160
|
+
this.contentEpoch++;
|
|
1158
1161
|
this.scene?.markDirty();
|
|
1159
1162
|
return this;
|
|
1160
1163
|
}
|
|
1164
|
+
getContentEpoch() {
|
|
1165
|
+
return this.contentEpoch;
|
|
1166
|
+
}
|
|
1161
1167
|
/**
|
|
1162
1168
|
* Change the block's box width.
|
|
1163
1169
|
*
|
|
@@ -1225,6 +1231,7 @@ var CodeBlock = class extends import_ui.UIComponent {
|
|
|
1225
1231
|
* lands mid-line, so that line's text (and therefore its tokenization) changes.
|
|
1226
1232
|
*/
|
|
1227
1233
|
buildLines(code) {
|
|
1234
|
+
this.contentEpoch++;
|
|
1228
1235
|
const rawLines = code.split(/\r\n|\r|\n/);
|
|
1229
1236
|
const previous = this.rawLines;
|
|
1230
1237
|
let reusable = 0;
|
|
@@ -1524,7 +1531,7 @@ function renderInlineToRichText(tokens, fallbackText, font, color, maxWidth, the
|
|
|
1524
1531
|
onLinkClick
|
|
1525
1532
|
});
|
|
1526
1533
|
}
|
|
1527
|
-
var Markdown = class extends import_ui.UIComponent {
|
|
1534
|
+
var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
1528
1535
|
content;
|
|
1529
1536
|
maxWidth;
|
|
1530
1537
|
theme;
|
|
@@ -2573,6 +2580,109 @@ var Markdown = class extends import_ui.UIComponent {
|
|
|
2573
2580
|
onLinkClick: this.onLinkClick
|
|
2574
2581
|
});
|
|
2575
2582
|
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Token types a list item's fast path can render as one `RichText`.
|
|
2585
|
+
*
|
|
2586
|
+
* An ALLOWLIST, deliberately, following `markstream-vue`'s
|
|
2587
|
+
* `SIMPLE_INLINE_TYPES` (`SimpleInlineRenderer/simpleInline.ts:15-31`): a block
|
|
2588
|
+
* type is excluded by OMISSION, so a token this renderer has never heard of
|
|
2589
|
+
* falls out of the fast path automatically instead of being silently flattened
|
|
2590
|
+
* to its raw text. A denylist fails the other way, and the failure is quiet —
|
|
2591
|
+
* a formula painted as literal TeX rather than an error — which is why this
|
|
2592
|
+
* defect survived so long.
|
|
2593
|
+
*
|
|
2594
|
+
* Deliberately small, because a list item's DIRECT children are far less varied
|
|
2595
|
+
* than they look. Probed against marked 18.0.7: every inline construct
|
|
2596
|
+
* (`strong`, `em`, `del`, `codespan`, `link`, `image`, `br`, `escape`, `html`,
|
|
2597
|
+
* `inlineMath`) arrives nested one level DEEPER, inside a container whose own
|
|
2598
|
+
* type is `text` — so a tight item's direct child list is `text` and nothing
|
|
2599
|
+
* else. Listing those inline types here would be dead code.
|
|
2600
|
+
*
|
|
2601
|
+
* `space` and `checkbox` are included because both are inert here. A blank line
|
|
2602
|
+
* between an item's paragraph and its block sibling produces a `space`, and
|
|
2603
|
+
* marked unshifts a `checkbox` into every TIGHT GFM task item — the box itself
|
|
2604
|
+
* is drawn from `item.task`/`item.checked` by `listItemSpans`, so the token
|
|
2605
|
+
* renders nothing on its own. Omitting `checkbox` sent every task item down the
|
|
2606
|
+
* block path and moved its marker into a nested entity, which broke four
|
|
2607
|
+
* task-list assertions in `Markdown.test.ts`.
|
|
2608
|
+
*/
|
|
2609
|
+
static INLINE_ITEM_TOKENS = /* @__PURE__ */ new Set([
|
|
2610
|
+
"text",
|
|
2611
|
+
"space",
|
|
2612
|
+
"checkbox"
|
|
2613
|
+
]);
|
|
2614
|
+
/**
|
|
2615
|
+
* Does this item consist purely of inline content?
|
|
2616
|
+
*
|
|
2617
|
+
* True keeps the single-`RichText` fast path, which is not merely an
|
|
2618
|
+
* optimization: `updateStreamedList` reuses `stack.children[i]` by calling
|
|
2619
|
+
* `setSpans` on it, so an item that becomes a `Stack` forfeits streamed reuse
|
|
2620
|
+
* for its entire list. Only pay for a block container when an item holds a
|
|
2621
|
+
* block.
|
|
2622
|
+
*
|
|
2623
|
+
* A lone `paragraph` counts as inline. A LOOSE list re-lexes every item's
|
|
2624
|
+
* inline content from `text` to `paragraph` — adding one blank line anywhere
|
|
2625
|
+
* flips `token.loose` for the whole list — so treating a single paragraph as a
|
|
2626
|
+
* block would drop the fast path for every item of every loose list, the common
|
|
2627
|
+
* shape in real prose, for no rendering benefit.
|
|
2628
|
+
*/
|
|
2629
|
+
itemIsInlineOnly(item) {
|
|
2630
|
+
const children = item.tokens;
|
|
2631
|
+
if (!children || children.length === 0) return true;
|
|
2632
|
+
if (children.length === 1 && children[0].type === "paragraph") return true;
|
|
2633
|
+
return children.every((child) => _Markdown.INLINE_ITEM_TOKENS.has(child.type));
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* Build a list item that holds block-level children.
|
|
2637
|
+
*
|
|
2638
|
+
* The item becomes a vertical `Stack`: its leading inline run (carrying the
|
|
2639
|
+
* marker) first, then every remaining child rendered through the same
|
|
2640
|
+
* `renderToken` the document level uses, indented to clear the marker.
|
|
2641
|
+
*
|
|
2642
|
+
* Recursing rather than special-casing the types we know about is the point — a
|
|
2643
|
+
* display formula, a fence, a table, a blockquote, a nested list, an `hr` and a
|
|
2644
|
+
* second paragraph all render exactly as they would at indent 0, and a block
|
|
2645
|
+
* type added later works here for free.
|
|
2646
|
+
*
|
|
2647
|
+
* Only the FIRST child can be the lead. Everything after it becomes a block,
|
|
2648
|
+
* including a second `paragraph`: an item's two paragraphs are two blocks, and
|
|
2649
|
+
* folding them into the lead run would concatenate them into one line with no
|
|
2650
|
+
* separation.
|
|
2651
|
+
*
|
|
2652
|
+
* The lead `RichText` is emitted even when the item has no inline text, because
|
|
2653
|
+
* it carries the marker — an item that is nothing but a formula still shows its
|
|
2654
|
+
* bullet or ordinal.
|
|
2655
|
+
*/
|
|
2656
|
+
listItemBlockStack(token, index, availableWidth, t) {
|
|
2657
|
+
const item = token.items[index];
|
|
2658
|
+
const children = item.tokens ?? [];
|
|
2659
|
+
const stack = new import_ui.Stack({ direction: "vertical", gap: 4 });
|
|
2660
|
+
const first = children[0];
|
|
2661
|
+
const leadChildren = first && (first.type === "text" || first.type === "paragraph") ? [first] : [];
|
|
2662
|
+
const leadToken = {
|
|
2663
|
+
...token,
|
|
2664
|
+
items: token.items.map((it, i) => i === index ? { ...it, tokens: leadChildren } : it)
|
|
2665
|
+
};
|
|
2666
|
+
stack.add(this.listItemRichText(leadToken, index, availableWidth, t));
|
|
2667
|
+
const indent = Math.round(t.fontSize);
|
|
2668
|
+
const childMetrics = {
|
|
2669
|
+
marginBefore: 0,
|
|
2670
|
+
marginAfter: 0,
|
|
2671
|
+
indentStart: indent,
|
|
2672
|
+
availableWidth: Math.max(1, availableWidth - indent)
|
|
2673
|
+
};
|
|
2674
|
+
for (let i = leadChildren.length; i < children.length; i++) {
|
|
2675
|
+
const el = this.renderTokenWithMetrics(children[i], childMetrics);
|
|
2676
|
+
if (!el) continue;
|
|
2677
|
+
const wrapper = new MarkdownContainer();
|
|
2678
|
+
el.x = indent;
|
|
2679
|
+
wrapper.add(el);
|
|
2680
|
+
wrapper.width = el.width + indent;
|
|
2681
|
+
wrapper.height = el.height;
|
|
2682
|
+
stack.add(wrapper);
|
|
2683
|
+
}
|
|
2684
|
+
return stack;
|
|
2685
|
+
}
|
|
2576
2686
|
listItemSpans(token, index) {
|
|
2577
2687
|
const item = token.items[index];
|
|
2578
2688
|
const num = Number(token.start ?? 1) + index;
|
|
@@ -2652,18 +2762,23 @@ var Markdown = class extends import_ui.UIComponent {
|
|
|
2652
2762
|
const lastRetained = oldToken.items.length - 1;
|
|
2653
2763
|
for (let i = 0; i < lastRetained; i++) {
|
|
2654
2764
|
if (oldToken.items[i].text !== newToken.items[i].text) return false;
|
|
2765
|
+
const isStack = stack.children[i] instanceof import_ui.Stack;
|
|
2766
|
+
if (isStack !== !this.itemIsInlineOnly(newToken.items[i])) return false;
|
|
2655
2767
|
}
|
|
2656
2768
|
const availableWidth = this.activeBlockMetrics?.availableWidth ?? this.maxWidth;
|
|
2657
2769
|
const t = this.theme;
|
|
2658
2770
|
const tailEntity = stack.children[lastRetained];
|
|
2659
2771
|
if (oldToken.items[lastRetained].text !== newToken.items[lastRetained].text) {
|
|
2772
|
+
if (!this.itemIsInlineOnly(newToken.items[lastRetained])) return false;
|
|
2660
2773
|
if (!("setSpans" in tailEntity)) return false;
|
|
2661
2774
|
tailEntity.setSpans(
|
|
2662
2775
|
this.listItemSpans(newToken, lastRetained)
|
|
2663
2776
|
);
|
|
2664
2777
|
}
|
|
2665
2778
|
for (let i = oldToken.items.length; i < newToken.items.length; i++) {
|
|
2666
|
-
stack.add(
|
|
2779
|
+
stack.add(
|
|
2780
|
+
this.itemIsInlineOnly(newToken.items[i]) ? this.listItemRichText(newToken, i, availableWidth, t) : this.listItemBlockStack(newToken, i, availableWidth, t)
|
|
2781
|
+
);
|
|
2667
2782
|
}
|
|
2668
2783
|
const last = stack.children.at(-1);
|
|
2669
2784
|
if (last) stack.resizeLastChild(last);
|
|
@@ -3427,7 +3542,9 @@ var Markdown = class extends import_ui.UIComponent {
|
|
|
3427
3542
|
const listToken = token;
|
|
3428
3543
|
const listStack = new import_ui.Stack({ direction: "vertical", gap: 6 });
|
|
3429
3544
|
for (let i = 0; i < listToken.items.length; i++) {
|
|
3430
|
-
listStack.add(
|
|
3545
|
+
listStack.add(
|
|
3546
|
+
this.itemIsInlineOnly(listToken.items[i]) ? this.listItemRichText(listToken, i, availableWidth, t) : this.listItemBlockStack(listToken, i, availableWidth, t)
|
|
3547
|
+
);
|
|
3431
3548
|
}
|
|
3432
3549
|
return listStack;
|
|
3433
3550
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1095,6 +1095,8 @@ var CodeBlock = class extends UIComponent {
|
|
|
1095
1095
|
rawLines = null;
|
|
1096
1096
|
cellWidth = 0;
|
|
1097
1097
|
source;
|
|
1098
|
+
/** Bumped by {@link buildLines} and {@link setSelectable}; read by `Scene`. */
|
|
1099
|
+
contentEpoch = 0;
|
|
1098
1100
|
lang;
|
|
1099
1101
|
theme;
|
|
1100
1102
|
lineH = 24;
|
|
@@ -1123,9 +1125,13 @@ var CodeBlock = class extends UIComponent {
|
|
|
1123
1125
|
/** Enable or disable browser-native selection for this code block. */
|
|
1124
1126
|
setSelectable(selectable) {
|
|
1125
1127
|
this.selectable = selectable;
|
|
1128
|
+
this.contentEpoch++;
|
|
1126
1129
|
this.scene?.markDirty();
|
|
1127
1130
|
return this;
|
|
1128
1131
|
}
|
|
1132
|
+
getContentEpoch() {
|
|
1133
|
+
return this.contentEpoch;
|
|
1134
|
+
}
|
|
1129
1135
|
/**
|
|
1130
1136
|
* Change the block's box width.
|
|
1131
1137
|
*
|
|
@@ -1193,6 +1199,7 @@ var CodeBlock = class extends UIComponent {
|
|
|
1193
1199
|
* lands mid-line, so that line's text (and therefore its tokenization) changes.
|
|
1194
1200
|
*/
|
|
1195
1201
|
buildLines(code) {
|
|
1202
|
+
this.contentEpoch++;
|
|
1196
1203
|
const rawLines = code.split(/\r\n|\r|\n/);
|
|
1197
1204
|
const previous = this.rawLines;
|
|
1198
1205
|
let reusable = 0;
|
|
@@ -1492,7 +1499,7 @@ function renderInlineToRichText(tokens, fallbackText, font, color, maxWidth, the
|
|
|
1492
1499
|
onLinkClick
|
|
1493
1500
|
});
|
|
1494
1501
|
}
|
|
1495
|
-
var Markdown = class extends UIComponent {
|
|
1502
|
+
var Markdown = class _Markdown extends UIComponent {
|
|
1496
1503
|
content;
|
|
1497
1504
|
maxWidth;
|
|
1498
1505
|
theme;
|
|
@@ -2541,6 +2548,109 @@ var Markdown = class extends UIComponent {
|
|
|
2541
2548
|
onLinkClick: this.onLinkClick
|
|
2542
2549
|
});
|
|
2543
2550
|
}
|
|
2551
|
+
/**
|
|
2552
|
+
* Token types a list item's fast path can render as one `RichText`.
|
|
2553
|
+
*
|
|
2554
|
+
* An ALLOWLIST, deliberately, following `markstream-vue`'s
|
|
2555
|
+
* `SIMPLE_INLINE_TYPES` (`SimpleInlineRenderer/simpleInline.ts:15-31`): a block
|
|
2556
|
+
* type is excluded by OMISSION, so a token this renderer has never heard of
|
|
2557
|
+
* falls out of the fast path automatically instead of being silently flattened
|
|
2558
|
+
* to its raw text. A denylist fails the other way, and the failure is quiet —
|
|
2559
|
+
* a formula painted as literal TeX rather than an error — which is why this
|
|
2560
|
+
* defect survived so long.
|
|
2561
|
+
*
|
|
2562
|
+
* Deliberately small, because a list item's DIRECT children are far less varied
|
|
2563
|
+
* than they look. Probed against marked 18.0.7: every inline construct
|
|
2564
|
+
* (`strong`, `em`, `del`, `codespan`, `link`, `image`, `br`, `escape`, `html`,
|
|
2565
|
+
* `inlineMath`) arrives nested one level DEEPER, inside a container whose own
|
|
2566
|
+
* type is `text` — so a tight item's direct child list is `text` and nothing
|
|
2567
|
+
* else. Listing those inline types here would be dead code.
|
|
2568
|
+
*
|
|
2569
|
+
* `space` and `checkbox` are included because both are inert here. A blank line
|
|
2570
|
+
* between an item's paragraph and its block sibling produces a `space`, and
|
|
2571
|
+
* marked unshifts a `checkbox` into every TIGHT GFM task item — the box itself
|
|
2572
|
+
* is drawn from `item.task`/`item.checked` by `listItemSpans`, so the token
|
|
2573
|
+
* renders nothing on its own. Omitting `checkbox` sent every task item down the
|
|
2574
|
+
* block path and moved its marker into a nested entity, which broke four
|
|
2575
|
+
* task-list assertions in `Markdown.test.ts`.
|
|
2576
|
+
*/
|
|
2577
|
+
static INLINE_ITEM_TOKENS = /* @__PURE__ */ new Set([
|
|
2578
|
+
"text",
|
|
2579
|
+
"space",
|
|
2580
|
+
"checkbox"
|
|
2581
|
+
]);
|
|
2582
|
+
/**
|
|
2583
|
+
* Does this item consist purely of inline content?
|
|
2584
|
+
*
|
|
2585
|
+
* True keeps the single-`RichText` fast path, which is not merely an
|
|
2586
|
+
* optimization: `updateStreamedList` reuses `stack.children[i]` by calling
|
|
2587
|
+
* `setSpans` on it, so an item that becomes a `Stack` forfeits streamed reuse
|
|
2588
|
+
* for its entire list. Only pay for a block container when an item holds a
|
|
2589
|
+
* block.
|
|
2590
|
+
*
|
|
2591
|
+
* A lone `paragraph` counts as inline. A LOOSE list re-lexes every item's
|
|
2592
|
+
* inline content from `text` to `paragraph` — adding one blank line anywhere
|
|
2593
|
+
* flips `token.loose` for the whole list — so treating a single paragraph as a
|
|
2594
|
+
* block would drop the fast path for every item of every loose list, the common
|
|
2595
|
+
* shape in real prose, for no rendering benefit.
|
|
2596
|
+
*/
|
|
2597
|
+
itemIsInlineOnly(item) {
|
|
2598
|
+
const children = item.tokens;
|
|
2599
|
+
if (!children || children.length === 0) return true;
|
|
2600
|
+
if (children.length === 1 && children[0].type === "paragraph") return true;
|
|
2601
|
+
return children.every((child) => _Markdown.INLINE_ITEM_TOKENS.has(child.type));
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Build a list item that holds block-level children.
|
|
2605
|
+
*
|
|
2606
|
+
* The item becomes a vertical `Stack`: its leading inline run (carrying the
|
|
2607
|
+
* marker) first, then every remaining child rendered through the same
|
|
2608
|
+
* `renderToken` the document level uses, indented to clear the marker.
|
|
2609
|
+
*
|
|
2610
|
+
* Recursing rather than special-casing the types we know about is the point — a
|
|
2611
|
+
* display formula, a fence, a table, a blockquote, a nested list, an `hr` and a
|
|
2612
|
+
* second paragraph all render exactly as they would at indent 0, and a block
|
|
2613
|
+
* type added later works here for free.
|
|
2614
|
+
*
|
|
2615
|
+
* Only the FIRST child can be the lead. Everything after it becomes a block,
|
|
2616
|
+
* including a second `paragraph`: an item's two paragraphs are two blocks, and
|
|
2617
|
+
* folding them into the lead run would concatenate them into one line with no
|
|
2618
|
+
* separation.
|
|
2619
|
+
*
|
|
2620
|
+
* The lead `RichText` is emitted even when the item has no inline text, because
|
|
2621
|
+
* it carries the marker — an item that is nothing but a formula still shows its
|
|
2622
|
+
* bullet or ordinal.
|
|
2623
|
+
*/
|
|
2624
|
+
listItemBlockStack(token, index, availableWidth, t) {
|
|
2625
|
+
const item = token.items[index];
|
|
2626
|
+
const children = item.tokens ?? [];
|
|
2627
|
+
const stack = new Stack({ direction: "vertical", gap: 4 });
|
|
2628
|
+
const first = children[0];
|
|
2629
|
+
const leadChildren = first && (first.type === "text" || first.type === "paragraph") ? [first] : [];
|
|
2630
|
+
const leadToken = {
|
|
2631
|
+
...token,
|
|
2632
|
+
items: token.items.map((it, i) => i === index ? { ...it, tokens: leadChildren } : it)
|
|
2633
|
+
};
|
|
2634
|
+
stack.add(this.listItemRichText(leadToken, index, availableWidth, t));
|
|
2635
|
+
const indent = Math.round(t.fontSize);
|
|
2636
|
+
const childMetrics = {
|
|
2637
|
+
marginBefore: 0,
|
|
2638
|
+
marginAfter: 0,
|
|
2639
|
+
indentStart: indent,
|
|
2640
|
+
availableWidth: Math.max(1, availableWidth - indent)
|
|
2641
|
+
};
|
|
2642
|
+
for (let i = leadChildren.length; i < children.length; i++) {
|
|
2643
|
+
const el = this.renderTokenWithMetrics(children[i], childMetrics);
|
|
2644
|
+
if (!el) continue;
|
|
2645
|
+
const wrapper = new MarkdownContainer();
|
|
2646
|
+
el.x = indent;
|
|
2647
|
+
wrapper.add(el);
|
|
2648
|
+
wrapper.width = el.width + indent;
|
|
2649
|
+
wrapper.height = el.height;
|
|
2650
|
+
stack.add(wrapper);
|
|
2651
|
+
}
|
|
2652
|
+
return stack;
|
|
2653
|
+
}
|
|
2544
2654
|
listItemSpans(token, index) {
|
|
2545
2655
|
const item = token.items[index];
|
|
2546
2656
|
const num = Number(token.start ?? 1) + index;
|
|
@@ -2620,18 +2730,23 @@ var Markdown = class extends UIComponent {
|
|
|
2620
2730
|
const lastRetained = oldToken.items.length - 1;
|
|
2621
2731
|
for (let i = 0; i < lastRetained; i++) {
|
|
2622
2732
|
if (oldToken.items[i].text !== newToken.items[i].text) return false;
|
|
2733
|
+
const isStack = stack.children[i] instanceof Stack;
|
|
2734
|
+
if (isStack !== !this.itemIsInlineOnly(newToken.items[i])) return false;
|
|
2623
2735
|
}
|
|
2624
2736
|
const availableWidth = this.activeBlockMetrics?.availableWidth ?? this.maxWidth;
|
|
2625
2737
|
const t = this.theme;
|
|
2626
2738
|
const tailEntity = stack.children[lastRetained];
|
|
2627
2739
|
if (oldToken.items[lastRetained].text !== newToken.items[lastRetained].text) {
|
|
2740
|
+
if (!this.itemIsInlineOnly(newToken.items[lastRetained])) return false;
|
|
2628
2741
|
if (!("setSpans" in tailEntity)) return false;
|
|
2629
2742
|
tailEntity.setSpans(
|
|
2630
2743
|
this.listItemSpans(newToken, lastRetained)
|
|
2631
2744
|
);
|
|
2632
2745
|
}
|
|
2633
2746
|
for (let i = oldToken.items.length; i < newToken.items.length; i++) {
|
|
2634
|
-
stack.add(
|
|
2747
|
+
stack.add(
|
|
2748
|
+
this.itemIsInlineOnly(newToken.items[i]) ? this.listItemRichText(newToken, i, availableWidth, t) : this.listItemBlockStack(newToken, i, availableWidth, t)
|
|
2749
|
+
);
|
|
2635
2750
|
}
|
|
2636
2751
|
const last = stack.children.at(-1);
|
|
2637
2752
|
if (last) stack.resizeLastChild(last);
|
|
@@ -3395,7 +3510,9 @@ var Markdown = class extends UIComponent {
|
|
|
3395
3510
|
const listToken = token;
|
|
3396
3511
|
const listStack = new Stack({ direction: "vertical", gap: 6 });
|
|
3397
3512
|
for (let i = 0; i < listToken.items.length; i++) {
|
|
3398
|
-
listStack.add(
|
|
3513
|
+
listStack.add(
|
|
3514
|
+
this.itemIsInlineOnly(listToken.items[i]) ? this.listItemRichText(listToken, i, availableWidth, t) : this.listItemBlockStack(listToken, i, availableWidth, t)
|
|
3515
|
+
);
|
|
3399
3516
|
}
|
|
3400
3517
|
return listStack;
|
|
3401
3518
|
}
|