@vectojs/markdown 0.12.0 → 0.14.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 +99 -0
- package/dist/blockAffordances.d.ts +179 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +528 -69
- package/dist/index.mjs +498 -41
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -30,14 +30,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
BlockAffordanceButton: () => BlockAffordanceButton,
|
|
34
|
+
BlockWithAffordances: () => BlockWithAffordances,
|
|
33
35
|
CodeBlock: () => CodeBlock,
|
|
34
36
|
Markdown: () => Markdown,
|
|
37
|
+
MathBlock: () => MathBlock,
|
|
35
38
|
codeAtlas: () => codeAtlas,
|
|
36
39
|
codeAtlasStats: () => codeAtlasStats,
|
|
40
|
+
escapeCsvField: () => escapeCsvField,
|
|
41
|
+
escapeMarkdownTableCell: () => escapeMarkdownTableCell,
|
|
42
|
+
extensionForLanguage: () => extensionForLanguage,
|
|
37
43
|
isMathJaxReady: () => isMathJaxReady,
|
|
44
|
+
mimeForLanguage: () => mimeForLanguage,
|
|
38
45
|
parseFrontMatterFields: () => parseFrontMatterFields,
|
|
39
46
|
preloadMathJax: () => preloadMathJax,
|
|
40
|
-
scanFrontMatter: () => scanFrontMatter
|
|
47
|
+
scanFrontMatter: () => scanFrontMatter,
|
|
48
|
+
tableContentOf: () => tableContentOf,
|
|
49
|
+
tableToCsv: () => tableToCsv,
|
|
50
|
+
tableToMarkdown: () => tableToMarkdown
|
|
41
51
|
});
|
|
42
52
|
module.exports = __toCommonJS(index_exports);
|
|
43
53
|
|
|
@@ -455,7 +465,251 @@ function createStreamController(host, options = {}) {
|
|
|
455
465
|
}
|
|
456
466
|
|
|
457
467
|
// src/Markdown.ts
|
|
468
|
+
var import_ui2 = require("@vectojs/ui");
|
|
469
|
+
|
|
470
|
+
// src/blockAffordances.ts
|
|
458
471
|
var import_ui = require("@vectojs/ui");
|
|
472
|
+
var LANGUAGE_EXTENSIONS = {
|
|
473
|
+
bash: "sh",
|
|
474
|
+
c: "c",
|
|
475
|
+
cpp: "cpp",
|
|
476
|
+
cs: "cs",
|
|
477
|
+
css: "css",
|
|
478
|
+
diff: "diff",
|
|
479
|
+
dockerfile: "dockerfile",
|
|
480
|
+
go: "go",
|
|
481
|
+
graphql: "graphql",
|
|
482
|
+
haskell: "hs",
|
|
483
|
+
html: "html",
|
|
484
|
+
java: "java",
|
|
485
|
+
javascript: "js",
|
|
486
|
+
js: "js",
|
|
487
|
+
json: "json",
|
|
488
|
+
jsonc: "jsonc",
|
|
489
|
+
jsx: "jsx",
|
|
490
|
+
kotlin: "kt",
|
|
491
|
+
latex: "tex",
|
|
492
|
+
lua: "lua",
|
|
493
|
+
make: "mk",
|
|
494
|
+
markdown: "md",
|
|
495
|
+
md: "md",
|
|
496
|
+
nix: "nix",
|
|
497
|
+
php: "php",
|
|
498
|
+
python: "py",
|
|
499
|
+
py: "py",
|
|
500
|
+
ruby: "rb",
|
|
501
|
+
rust: "rs",
|
|
502
|
+
rs: "rs",
|
|
503
|
+
scss: "scss",
|
|
504
|
+
sh: "sh",
|
|
505
|
+
shell: "sh",
|
|
506
|
+
sql: "sql",
|
|
507
|
+
svelte: "svelte",
|
|
508
|
+
swift: "swift",
|
|
509
|
+
tex: "tex",
|
|
510
|
+
toml: "toml",
|
|
511
|
+
ts: "ts",
|
|
512
|
+
tsx: "tsx",
|
|
513
|
+
typescript: "ts",
|
|
514
|
+
vue: "vue",
|
|
515
|
+
xml: "xml",
|
|
516
|
+
yaml: "yaml",
|
|
517
|
+
yml: "yaml",
|
|
518
|
+
zig: "zig",
|
|
519
|
+
zsh: "sh"
|
|
520
|
+
};
|
|
521
|
+
function extensionForLanguage(lang) {
|
|
522
|
+
const first = lang.trim().toLowerCase().split(/[\s:,{]/)[0] ?? "";
|
|
523
|
+
return LANGUAGE_EXTENSIONS[first] ?? "txt";
|
|
524
|
+
}
|
|
525
|
+
function mimeForLanguage(lang) {
|
|
526
|
+
const ext = extensionForLanguage(lang);
|
|
527
|
+
if (ext === "json" || ext === "jsonc") return "application/json";
|
|
528
|
+
if (ext === "html") return "text/html";
|
|
529
|
+
if (ext === "css") return "text/css";
|
|
530
|
+
if (ext === "xml" || ext === "svelte" || ext === "vue") return "text/plain";
|
|
531
|
+
return "text/plain";
|
|
532
|
+
}
|
|
533
|
+
function escapeCsvField(value) {
|
|
534
|
+
let needsQuoting = false;
|
|
535
|
+
let hasQuote = false;
|
|
536
|
+
for (const char of value) {
|
|
537
|
+
if (char === '"') {
|
|
538
|
+
hasQuote = true;
|
|
539
|
+
needsQuoting = true;
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
if (char === "," || char === "\n" || char === "\r") needsQuoting = true;
|
|
543
|
+
}
|
|
544
|
+
if (!needsQuoting) return value;
|
|
545
|
+
return hasQuote ? `"${value.replace(/"/g, '""')}"` : `"${value}"`;
|
|
546
|
+
}
|
|
547
|
+
function escapeMarkdownTableCell(cell) {
|
|
548
|
+
let needsEscaping = false;
|
|
549
|
+
for (const char of cell) {
|
|
550
|
+
if (char === "\\" || char === "|") {
|
|
551
|
+
needsEscaping = true;
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (!needsEscaping) return cell;
|
|
556
|
+
return cell.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
|
|
557
|
+
}
|
|
558
|
+
function tableToCsv(table) {
|
|
559
|
+
const lines = [table.headers.map(escapeCsvField).join(",")];
|
|
560
|
+
for (const row of table.rows) lines.push(row.map(escapeCsvField).join(","));
|
|
561
|
+
return `\uFEFF${lines.join("\r\n")}`;
|
|
562
|
+
}
|
|
563
|
+
function tableToMarkdown(table) {
|
|
564
|
+
const header = `| ${table.headers.map(escapeMarkdownTableCell).join(" | ")} |`;
|
|
565
|
+
const divider = `| ${table.headers.map((_cell, index) => {
|
|
566
|
+
switch (table.align[index]) {
|
|
567
|
+
case "left":
|
|
568
|
+
return ":---";
|
|
569
|
+
case "center":
|
|
570
|
+
return ":---:";
|
|
571
|
+
case "right":
|
|
572
|
+
return "---:";
|
|
573
|
+
default:
|
|
574
|
+
return "---";
|
|
575
|
+
}
|
|
576
|
+
}).join(" | ")} |`;
|
|
577
|
+
const body = table.rows.map(
|
|
578
|
+
(row) => `| ${table.headers.map((_cell, index) => escapeMarkdownTableCell(row[index] ?? "")).join(" | ")} |`
|
|
579
|
+
);
|
|
580
|
+
return [header, divider, ...body].join("\n");
|
|
581
|
+
}
|
|
582
|
+
function defaultWriteClipboard(text) {
|
|
583
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
584
|
+
clipboard?.writeText?.(text);
|
|
585
|
+
}
|
|
586
|
+
function defaultSaveFile(filename, content, mimeType) {
|
|
587
|
+
const doc = globalThis.document;
|
|
588
|
+
if (!doc?.body) return;
|
|
589
|
+
const blob = new Blob([content], { type: mimeType });
|
|
590
|
+
const url = URL.createObjectURL(blob);
|
|
591
|
+
const anchor = doc.createElement("a");
|
|
592
|
+
anchor.href = url;
|
|
593
|
+
anchor.download = filename;
|
|
594
|
+
doc.body.appendChild(anchor);
|
|
595
|
+
anchor.click();
|
|
596
|
+
doc.body.removeChild(anchor);
|
|
597
|
+
URL.revokeObjectURL(url);
|
|
598
|
+
}
|
|
599
|
+
var BlockAffordanceButton = class _BlockAffordanceButton extends import_ui.Button {
|
|
600
|
+
constructor(label, successLabel, act, opts = {}) {
|
|
601
|
+
super(label, { ...opts, onClick: () => this.run() });
|
|
602
|
+
this.act = act;
|
|
603
|
+
this.restingLabel = label;
|
|
604
|
+
this.successLabel = successLabel;
|
|
605
|
+
this.width = Math.max(this.width, (0, import_ui.measureText)(successLabel, this.font) + 24);
|
|
606
|
+
}
|
|
607
|
+
act;
|
|
608
|
+
/** How long the confirmation label stays up, in ms. */
|
|
609
|
+
static FEEDBACK_MS = 1600;
|
|
610
|
+
restingLabel;
|
|
611
|
+
successLabel;
|
|
612
|
+
feedbackTimer;
|
|
613
|
+
/**
|
|
614
|
+
* Runs the action, then shows the confirmation.
|
|
615
|
+
*
|
|
616
|
+
* The action runs first and a throw propagates: a clipboard write the browser
|
|
617
|
+
* rejected must not be reported as a success.
|
|
618
|
+
*/
|
|
619
|
+
run() {
|
|
620
|
+
this.act();
|
|
621
|
+
this.setTransientLabel(this.successLabel);
|
|
622
|
+
if (this.feedbackTimer !== void 0) clearTimeout(this.feedbackTimer);
|
|
623
|
+
this.feedbackTimer = setTimeout(() => {
|
|
624
|
+
this.setTransientLabel(this.restingLabel);
|
|
625
|
+
this.feedbackTimer = void 0;
|
|
626
|
+
}, _BlockAffordanceButton.FEEDBACK_MS);
|
|
627
|
+
}
|
|
628
|
+
setTransientLabel(label) {
|
|
629
|
+
this.label = label;
|
|
630
|
+
this.textWidth = (0, import_ui.measureText)(label, this.font);
|
|
631
|
+
this.scene?.markDirty();
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* The label a reader hears is the one they see, transient confirmation
|
|
635
|
+
* included, so an AT user gets the same feedback a sighted user does.
|
|
636
|
+
*/
|
|
637
|
+
getA11yAttributes() {
|
|
638
|
+
return { ...super.getA11yAttributes(), label: this.label };
|
|
639
|
+
}
|
|
640
|
+
/** Clears the pending revert so a destroyed block leaves no timer behind. */
|
|
641
|
+
destroy() {
|
|
642
|
+
if (this.feedbackTimer !== void 0) {
|
|
643
|
+
clearTimeout(this.feedbackTimer);
|
|
644
|
+
this.feedbackTimer = void 0;
|
|
645
|
+
}
|
|
646
|
+
super.destroy();
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
var BlockWithAffordances = class _BlockWithAffordances extends import_ui.UIComponent {
|
|
650
|
+
constructor(block, controls) {
|
|
651
|
+
super();
|
|
652
|
+
this.block = block;
|
|
653
|
+
this.controls = controls;
|
|
654
|
+
this.add(block);
|
|
655
|
+
for (const control of controls) this.add(control);
|
|
656
|
+
this.layoutAffordances();
|
|
657
|
+
}
|
|
658
|
+
block;
|
|
659
|
+
controls;
|
|
660
|
+
/** Gap between the block's edges and the controls, in px. */
|
|
661
|
+
static INSET = 8;
|
|
662
|
+
/** Gap between adjacent controls, in px. */
|
|
663
|
+
static GAP = 6;
|
|
664
|
+
/**
|
|
665
|
+
* Places the controls right-aligned along the block's top edge.
|
|
666
|
+
*
|
|
667
|
+
* Laid out right-to-left from the block's right edge so the first control in
|
|
668
|
+
* the list ends up leftmost, which keeps DOM order (and therefore tab order and
|
|
669
|
+
* the a11y reading order) matching the visual order.
|
|
670
|
+
*/
|
|
671
|
+
layoutAffordances() {
|
|
672
|
+
this.width = this.block.width;
|
|
673
|
+
this.height = this.block.height;
|
|
674
|
+
let right = this.block.width - _BlockWithAffordances.INSET;
|
|
675
|
+
for (let i = this.controls.length - 1; i >= 0; i--) {
|
|
676
|
+
const control = this.controls[i];
|
|
677
|
+
control.x = right - control.width;
|
|
678
|
+
control.y = _BlockWithAffordances.INSET;
|
|
679
|
+
right = control.x - _BlockWithAffordances.GAP;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Re-places the controls after the block's own box changed.
|
|
684
|
+
*
|
|
685
|
+
* Called by the owner when a block is resized or its content grew; the controls
|
|
686
|
+
* are anchored to the right edge, so a width change moves them.
|
|
687
|
+
*/
|
|
688
|
+
refreshAffordances() {
|
|
689
|
+
this.layoutAffordances();
|
|
690
|
+
this.scene?.markDirty();
|
|
691
|
+
}
|
|
692
|
+
/** The wrapper is a pass-through: its size is the block's size. */
|
|
693
|
+
getLayoutControlledProperties() {
|
|
694
|
+
return ["x", "y"];
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Projected as a group so assistive technology reports one labelled region
|
|
698
|
+
* containing the block and its controls, rather than two unrelated siblings.
|
|
699
|
+
*/
|
|
700
|
+
getA11yAttributes() {
|
|
701
|
+
return { role: "group", pointerEvents: "none" };
|
|
702
|
+
}
|
|
703
|
+
render() {
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
function tableContentOf(token) {
|
|
707
|
+
return {
|
|
708
|
+
headers: token.header.map((cell) => cell.text),
|
|
709
|
+
rows: token.rows.map((row) => row.map((cell) => cell.text)),
|
|
710
|
+
align: token.align
|
|
711
|
+
};
|
|
712
|
+
}
|
|
459
713
|
|
|
460
714
|
// src/frontMatter.ts
|
|
461
715
|
var OPEN_RE = /^---[ \t]*\r?\n/;
|
|
@@ -700,7 +954,59 @@ function isFenceClosed(raw) {
|
|
|
700
954
|
return false;
|
|
701
955
|
}
|
|
702
956
|
function paragraphHasImage(token) {
|
|
703
|
-
return token.tokens
|
|
957
|
+
return containsImage(token.tokens);
|
|
958
|
+
}
|
|
959
|
+
function containsImage(tokens) {
|
|
960
|
+
if (!tokens) return false;
|
|
961
|
+
for (const token of tokens) {
|
|
962
|
+
if (token.type === "image") return true;
|
|
963
|
+
if (containsImage(token.tokens)) return true;
|
|
964
|
+
}
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
function imagesOf(tokens) {
|
|
968
|
+
const images = [];
|
|
969
|
+
for (const token of tokens ?? []) {
|
|
970
|
+
if (token.type === "image") {
|
|
971
|
+
images.push(token);
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
images.push(...imagesOf(token.tokens));
|
|
975
|
+
}
|
|
976
|
+
return images;
|
|
977
|
+
}
|
|
978
|
+
function stripImages(token) {
|
|
979
|
+
const children = token.tokens;
|
|
980
|
+
if (!children) return token;
|
|
981
|
+
const kept = [];
|
|
982
|
+
for (const child of children) {
|
|
983
|
+
if (child.type === "image") continue;
|
|
984
|
+
const grandchildren = child.tokens;
|
|
985
|
+
if (grandchildren && containsImage(grandchildren)) {
|
|
986
|
+
const stripped = stripImages(child);
|
|
987
|
+
const remaining = stripped.tokens;
|
|
988
|
+
if (remaining && remaining.length > 0) kept.push(stripped);
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
kept.push(child);
|
|
992
|
+
}
|
|
993
|
+
return { ...token, tokens: kept };
|
|
994
|
+
}
|
|
995
|
+
function liftNestedImages(tokens) {
|
|
996
|
+
const lifted = [];
|
|
997
|
+
for (const token of tokens) {
|
|
998
|
+
if (token.type === "image") {
|
|
999
|
+
lifted.push(token);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const children = token.tokens;
|
|
1003
|
+
if (children && containsImage(children)) {
|
|
1004
|
+
lifted.push(...liftNestedImages(children));
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
lifted.push(token);
|
|
1008
|
+
}
|
|
1009
|
+
return lifted;
|
|
704
1010
|
}
|
|
705
1011
|
function lastIndexOfImage(tokens) {
|
|
706
1012
|
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
@@ -711,7 +1017,7 @@ function lastIndexOfImage(tokens) {
|
|
|
711
1017
|
function expectedImageParagraphChildren(tokens) {
|
|
712
1018
|
let children = 0;
|
|
713
1019
|
let inTextRun = false;
|
|
714
|
-
for (const token of tokens) {
|
|
1020
|
+
for (const token of liftNestedImages(tokens)) {
|
|
715
1021
|
if (token.type === "image") {
|
|
716
1022
|
children++;
|
|
717
1023
|
inTextRun = false;
|
|
@@ -871,6 +1177,33 @@ var MarkdownContainer = class extends import_core.Entity {
|
|
|
871
1177
|
render(_r) {
|
|
872
1178
|
}
|
|
873
1179
|
};
|
|
1180
|
+
var MathBlock = class extends MarkdownContainer {
|
|
1181
|
+
/**
|
|
1182
|
+
* The TeX source, exactly as written between the delimiters.
|
|
1183
|
+
*
|
|
1184
|
+
* Also the projected text and the accessible name, so this is the one string a
|
|
1185
|
+
* reader can find, select, and copy.
|
|
1186
|
+
*/
|
|
1187
|
+
formula;
|
|
1188
|
+
/** The `data:image/svg+xml` URI of the typeset glyphs. */
|
|
1189
|
+
svgUri;
|
|
1190
|
+
constructor(formula, svgUri) {
|
|
1191
|
+
super();
|
|
1192
|
+
this.formula = formula;
|
|
1193
|
+
this.svgUri = svgUri;
|
|
1194
|
+
}
|
|
1195
|
+
getDevtoolsDescriptor() {
|
|
1196
|
+
return {
|
|
1197
|
+
kind: "MathBlock",
|
|
1198
|
+
groups: [
|
|
1199
|
+
{
|
|
1200
|
+
label: "Math",
|
|
1201
|
+
fields: [{ label: "formula", value: this.formula, readOnly: true }]
|
|
1202
|
+
}
|
|
1203
|
+
]
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
874
1207
|
var KEYWORD_SETS = {
|
|
875
1208
|
js: /* @__PURE__ */ new Set([
|
|
876
1209
|
"const",
|
|
@@ -1120,7 +1453,7 @@ function highlightLine(line, lang, theme) {
|
|
|
1120
1453
|
flush(theme.codeColor);
|
|
1121
1454
|
return segments;
|
|
1122
1455
|
}
|
|
1123
|
-
var CodeBlock = class extends
|
|
1456
|
+
var CodeBlock = class extends import_ui2.UIComponent {
|
|
1124
1457
|
lines;
|
|
1125
1458
|
grid = null;
|
|
1126
1459
|
/** Raw (unhighlighted) lines of the last build, for prefix reuse in buildLines. */
|
|
@@ -1253,7 +1586,7 @@ var CodeBlock = class extends import_ui.UIComponent {
|
|
|
1253
1586
|
this.height = this.pad * 2 + rawLines.length * this.lineH;
|
|
1254
1587
|
}
|
|
1255
1588
|
ensureGrid() {
|
|
1256
|
-
const cellWidth = this.cellWidth || Math.max(1, (0,
|
|
1589
|
+
const cellWidth = this.cellWidth || Math.max(1, (0, import_ui2.measureText)("M", this.codeFont));
|
|
1257
1590
|
if (!this.grid || this.grid.source !== this.source || this.grid.font !== this.codeFont || this.grid.cellWidth !== cellWidth) {
|
|
1258
1591
|
this.grid = (0, import_core.prepareContentGrid)(this.source, {
|
|
1259
1592
|
font: this.codeFont,
|
|
@@ -1522,7 +1855,7 @@ function renderInlineToRichText(tokens, fallbackText, font, color, maxWidth, the
|
|
|
1522
1855
|
if (spans.length === 0) {
|
|
1523
1856
|
spans.push({ text: decodeEntities(fallbackText) });
|
|
1524
1857
|
}
|
|
1525
|
-
return new
|
|
1858
|
+
return new import_ui2.RichText(spans, {
|
|
1526
1859
|
font,
|
|
1527
1860
|
color,
|
|
1528
1861
|
maxWidth,
|
|
@@ -1531,12 +1864,24 @@ function renderInlineToRichText(tokens, fallbackText, font, color, maxWidth, the
|
|
|
1531
1864
|
onLinkClick
|
|
1532
1865
|
});
|
|
1533
1866
|
}
|
|
1534
|
-
var Markdown = class _Markdown extends
|
|
1867
|
+
var Markdown = class _Markdown extends import_ui2.UIComponent {
|
|
1535
1868
|
content;
|
|
1536
1869
|
maxWidth;
|
|
1537
1870
|
theme;
|
|
1538
1871
|
onLinkClick;
|
|
1539
1872
|
selectable;
|
|
1873
|
+
/**
|
|
1874
|
+
* Whether code blocks and tables carry copy / download controls.
|
|
1875
|
+
*
|
|
1876
|
+
* Read when a block entity is built, so it affects blocks rendered from here on
|
|
1877
|
+
* rather than retroactively; a document does not rebuild to gain or lose an
|
|
1878
|
+
* affordance.
|
|
1879
|
+
*/
|
|
1880
|
+
blockAffordances;
|
|
1881
|
+
/** Clipboard writer used by the copy controls. */
|
|
1882
|
+
writeClipboard;
|
|
1883
|
+
/** File saver used by the download controls. */
|
|
1884
|
+
saveFile;
|
|
1540
1885
|
activeBlockMetrics = null;
|
|
1541
1886
|
/**
|
|
1542
1887
|
* Called after a streamed append has re-laid-out the document.
|
|
@@ -1761,7 +2106,10 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
1761
2106
|
this.onLinkClick = opts.onLinkClick;
|
|
1762
2107
|
this.selectable = opts.selectable ?? true;
|
|
1763
2108
|
this._userTiming = opts.userTiming ?? false;
|
|
1764
|
-
this.
|
|
2109
|
+
this.blockAffordances = opts.blockAffordances ?? false;
|
|
2110
|
+
this.writeClipboard = opts.writeClipboard ?? defaultWriteClipboard;
|
|
2111
|
+
this.saveFile = opts.saveFile ?? defaultSaveFile;
|
|
2112
|
+
this.content = new import_ui2.Stack({ direction: "vertical", gap: 16 });
|
|
1765
2113
|
this.add(this.content);
|
|
1766
2114
|
this.rawMarkdown = "";
|
|
1767
2115
|
this.setTokens([]);
|
|
@@ -1980,15 +2328,15 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
1980
2328
|
switch (token.type) {
|
|
1981
2329
|
case "heading":
|
|
1982
2330
|
case "paragraph": {
|
|
1983
|
-
if (entity instanceof
|
|
2331
|
+
if (entity instanceof import_ui2.RichText) {
|
|
1984
2332
|
entity.setMaxWidth(availableWidth);
|
|
1985
2333
|
return;
|
|
1986
2334
|
}
|
|
1987
|
-
if (entity instanceof
|
|
2335
|
+
if (entity instanceof import_ui2.Stack) {
|
|
1988
2336
|
entity.maxWidth = availableWidth;
|
|
1989
2337
|
for (const run of entity.children) {
|
|
1990
|
-
if (run instanceof
|
|
1991
|
-
else if (run instanceof
|
|
2338
|
+
if (run instanceof import_ui2.RichText) run.setMaxWidth(availableWidth);
|
|
2339
|
+
else if (run instanceof import_ui2.Image) this.refitParagraphImage(run, availableWidth);
|
|
1992
2340
|
}
|
|
1993
2341
|
entity.layout();
|
|
1994
2342
|
}
|
|
@@ -2001,11 +2349,11 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2001
2349
|
}
|
|
2002
2350
|
case "blockquote": {
|
|
2003
2351
|
const bqToken = token;
|
|
2004
|
-
const innerStack = entity.children.find((c) => c instanceof
|
|
2352
|
+
const innerStack = entity.children.find((c) => c instanceof import_ui2.Stack);
|
|
2005
2353
|
const border = entity.children.find((c) => c instanceof QuoteBorder);
|
|
2006
2354
|
const indentStart = Math.min(16, availableWidth);
|
|
2007
2355
|
const childWidth = Math.max(0, availableWidth - indentStart);
|
|
2008
|
-
if (innerStack instanceof
|
|
2356
|
+
if (innerStack instanceof import_ui2.Stack && bqToken.tokens) {
|
|
2009
2357
|
let index = 0;
|
|
2010
2358
|
for (const inner of bqToken.tokens) {
|
|
2011
2359
|
if (!this.producesEntity(inner)) continue;
|
|
@@ -2028,15 +2376,15 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2028
2376
|
return;
|
|
2029
2377
|
}
|
|
2030
2378
|
case "list": {
|
|
2031
|
-
if (!(entity instanceof
|
|
2379
|
+
if (!(entity instanceof import_ui2.Stack)) return;
|
|
2032
2380
|
for (const item of entity.children) {
|
|
2033
|
-
if (item instanceof
|
|
2381
|
+
if (item instanceof import_ui2.RichText) item.setMaxWidth(availableWidth);
|
|
2034
2382
|
}
|
|
2035
2383
|
entity.layout();
|
|
2036
2384
|
return;
|
|
2037
2385
|
}
|
|
2038
2386
|
case "table": {
|
|
2039
|
-
if (entity instanceof
|
|
2387
|
+
if (entity instanceof import_ui2.Table) entity.setWidth(availableWidth);
|
|
2040
2388
|
return;
|
|
2041
2389
|
}
|
|
2042
2390
|
case "hr": {
|
|
@@ -2044,7 +2392,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2044
2392
|
return;
|
|
2045
2393
|
}
|
|
2046
2394
|
default: {
|
|
2047
|
-
if (entity instanceof
|
|
2395
|
+
if (entity instanceof import_ui2.Text) entity.setMaxWidth(availableWidth);
|
|
2048
2396
|
return;
|
|
2049
2397
|
}
|
|
2050
2398
|
}
|
|
@@ -2515,7 +2863,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2515
2863
|
}
|
|
2516
2864
|
/** One text run of an image-bearing paragraph, as both paths build it. */
|
|
2517
2865
|
inlineRunRichText(tokens, availableWidth, t) {
|
|
2518
|
-
return new
|
|
2866
|
+
return new import_ui2.RichText(this.inlineRunSpans(tokens, t), {
|
|
2519
2867
|
font: `${t.fontSize}px ${t.bodyFont}`,
|
|
2520
2868
|
color: t.textColor,
|
|
2521
2869
|
maxWidth: availableWidth,
|
|
@@ -2549,10 +2897,77 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2549
2897
|
* policy for a zero-dimension source is a separate decision from notifying
|
|
2550
2898
|
* the scene, which is the actual defect here.
|
|
2551
2899
|
*/
|
|
2900
|
+
/**
|
|
2901
|
+
* Wraps a block in its copy / download controls, or returns it untouched.
|
|
2902
|
+
*
|
|
2903
|
+
* The controls are built lazily through `make` so a document with
|
|
2904
|
+
* `blockAffordances` off pays nothing — not the closures, not the measurement
|
|
2905
|
+
* `BlockAffordanceButton` does in its constructor.
|
|
2906
|
+
*/
|
|
2907
|
+
withBlockAffordances(block, make) {
|
|
2908
|
+
if (!this.blockAffordances) return block;
|
|
2909
|
+
const controls = make();
|
|
2910
|
+
return controls.length > 0 ? new BlockWithAffordances(block, controls) : block;
|
|
2911
|
+
}
|
|
2912
|
+
/** Copy and download controls for one fenced code block. */
|
|
2913
|
+
codeBlockAffordances(source, lang) {
|
|
2914
|
+
const opts = this.affordanceButtonOptions();
|
|
2915
|
+
return [
|
|
2916
|
+
new BlockAffordanceButton("Copy code", "Copied", () => this.writeClipboard(source), opts),
|
|
2917
|
+
new BlockAffordanceButton(
|
|
2918
|
+
"Download code",
|
|
2919
|
+
"Saved",
|
|
2920
|
+
() => this.saveFile(`code.${extensionForLanguage(lang)}`, source, mimeForLanguage(lang)),
|
|
2921
|
+
opts
|
|
2922
|
+
)
|
|
2923
|
+
];
|
|
2924
|
+
}
|
|
2925
|
+
/** Copy (as Markdown) and download (as CSV) controls for one table. */
|
|
2926
|
+
tableAffordances(tblToken) {
|
|
2927
|
+
const content = tableContentOf(tblToken);
|
|
2928
|
+
const opts = this.affordanceButtonOptions();
|
|
2929
|
+
return [
|
|
2930
|
+
// Markdown rather than CSV for the clipboard: the reader copied it out of a
|
|
2931
|
+
// Markdown document and the overwhelmingly likely destination is another
|
|
2932
|
+
// one. CSV is what the download is for, where a spreadsheet is the target.
|
|
2933
|
+
new BlockAffordanceButton(
|
|
2934
|
+
"Copy table",
|
|
2935
|
+
"Copied",
|
|
2936
|
+
() => this.writeClipboard(tableToMarkdown(content)),
|
|
2937
|
+
opts
|
|
2938
|
+
),
|
|
2939
|
+
new BlockAffordanceButton(
|
|
2940
|
+
"Download table",
|
|
2941
|
+
"Saved",
|
|
2942
|
+
() => this.saveFile("table.csv", tableToCsv(content), "text/csv;charset=utf-8"),
|
|
2943
|
+
opts
|
|
2944
|
+
)
|
|
2945
|
+
];
|
|
2946
|
+
}
|
|
2947
|
+
/**
|
|
2948
|
+
* Button styling for the affordances, derived from the document theme.
|
|
2949
|
+
*
|
|
2950
|
+
* Themed rather than hardcoded so a light-theme document does not get the dark
|
|
2951
|
+
* default palette. `focusColor` is set explicitly from the theme's accent
|
|
2952
|
+
* because `Button`'s default cyan is tuned for the dark palette and reads as
|
|
2953
|
+
* off-brand elsewhere — while a focus ring is the one affordance a keyboard
|
|
2954
|
+
* user cannot do without.
|
|
2955
|
+
*/
|
|
2956
|
+
affordanceButtonOptions() {
|
|
2957
|
+
return {
|
|
2958
|
+
font: `600 12px ${this.theme.bodyFont}`,
|
|
2959
|
+
padding: 6,
|
|
2960
|
+
radius: 6,
|
|
2961
|
+
bg: this.theme.codeBgColor,
|
|
2962
|
+
hoverBg: this.theme.tableHeaderBgColor,
|
|
2963
|
+
color: this.theme.textColor,
|
|
2964
|
+
focusColor: this.theme.codeColor
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2552
2967
|
paragraphImage(imgToken, availableWidth) {
|
|
2553
2968
|
const initialWidth = Math.min(800, availableWidth);
|
|
2554
2969
|
const initialHeight = Math.round(initialWidth * 0.6);
|
|
2555
|
-
const img = new
|
|
2970
|
+
const img = new import_ui2.Image(imgToken.href, {
|
|
2556
2971
|
width: initialWidth,
|
|
2557
2972
|
height: initialHeight,
|
|
2558
2973
|
alt: imgToken.text,
|
|
@@ -2571,7 +2986,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2571
2986
|
}
|
|
2572
2987
|
/** One table cell entity, shared by the render arm and the streamed-table path. */
|
|
2573
2988
|
tableCellRichText(cell, header, t) {
|
|
2574
|
-
return new
|
|
2989
|
+
return new import_ui2.RichText(this.tableCellSpans(cell, t), {
|
|
2575
2990
|
font: `${t.fontSize - 2}px ${t.bodyFont}`,
|
|
2576
2991
|
color: header ? t.headingColor : t.textColor,
|
|
2577
2992
|
baseStyle: header ? { bold: true } : void 0,
|
|
@@ -2629,6 +3044,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2629
3044
|
itemIsInlineOnly(item) {
|
|
2630
3045
|
const children = item.tokens;
|
|
2631
3046
|
if (!children || children.length === 0) return true;
|
|
3047
|
+
if (containsImage(children)) return false;
|
|
2632
3048
|
if (children.length === 1 && children[0].type === "paragraph") return true;
|
|
2633
3049
|
return children.every((child) => _Markdown.INLINE_ITEM_TOKENS.has(child.type));
|
|
2634
3050
|
}
|
|
@@ -2656,9 +3072,12 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2656
3072
|
listItemBlockStack(token, index, availableWidth, t) {
|
|
2657
3073
|
const item = token.items[index];
|
|
2658
3074
|
const children = item.tokens ?? [];
|
|
2659
|
-
const stack = new
|
|
3075
|
+
const stack = new import_ui2.Stack({ direction: "vertical", gap: 4 });
|
|
2660
3076
|
const first = children[0];
|
|
2661
|
-
const
|
|
3077
|
+
const firstIsInline = Boolean(first) && (first.type === "text" || first.type === "paragraph");
|
|
3078
|
+
const leadHasImage = firstIsInline && containsImage(first.tokens);
|
|
3079
|
+
const leadChildren = firstIsInline ? [leadHasImage ? stripImages(first) : first] : [];
|
|
3080
|
+
const leadImages = leadHasImage ? imagesOf(first.tokens) : [];
|
|
2662
3081
|
const leadToken = {
|
|
2663
3082
|
...token,
|
|
2664
3083
|
items: token.items.map((it, i) => i === index ? { ...it, tokens: leadChildren } : it)
|
|
@@ -2671,6 +3090,13 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2671
3090
|
indentStart: indent,
|
|
2672
3091
|
availableWidth: Math.max(1, availableWidth - indent)
|
|
2673
3092
|
};
|
|
3093
|
+
for (const image of leadImages) {
|
|
3094
|
+
const el = this.paragraphImage(image, childMetrics.availableWidth);
|
|
3095
|
+
const wrapper = new MarkdownContainer();
|
|
3096
|
+
el.x = indent;
|
|
3097
|
+
wrapper.add(el);
|
|
3098
|
+
stack.add(wrapper);
|
|
3099
|
+
}
|
|
2674
3100
|
for (let i = leadChildren.length; i < children.length; i++) {
|
|
2675
3101
|
const el = this.renderTokenWithMetrics(children[i], childMetrics);
|
|
2676
3102
|
if (!el) continue;
|
|
@@ -2718,7 +3144,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2718
3144
|
}
|
|
2719
3145
|
/** Construct the `RichText` for one list item. */
|
|
2720
3146
|
listItemRichText(token, index, availableWidth, t) {
|
|
2721
|
-
return new
|
|
3147
|
+
return new import_ui2.RichText(this.listItemSpans(token, index), {
|
|
2722
3148
|
font: `${t.fontSize}px ${t.bodyFont}`,
|
|
2723
3149
|
color: t.textColor,
|
|
2724
3150
|
maxWidth: availableWidth,
|
|
@@ -2753,7 +3179,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2753
3179
|
* and keep stale spans. Bail when `loose` flips.
|
|
2754
3180
|
*/
|
|
2755
3181
|
updateStreamedList(stack, oldToken, newToken) {
|
|
2756
|
-
if (!(stack instanceof
|
|
3182
|
+
if (!(stack instanceof import_ui2.Stack)) return false;
|
|
2757
3183
|
if (newToken.items.length < oldToken.items.length || oldToken.items.length === 0) return false;
|
|
2758
3184
|
if (oldToken.ordered !== newToken.ordered) return false;
|
|
2759
3185
|
if ((oldToken.start ?? 1) !== (newToken.start ?? 1)) return false;
|
|
@@ -2762,7 +3188,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2762
3188
|
const lastRetained = oldToken.items.length - 1;
|
|
2763
3189
|
for (let i = 0; i < lastRetained; i++) {
|
|
2764
3190
|
if (oldToken.items[i].text !== newToken.items[i].text) return false;
|
|
2765
|
-
const isStack = stack.children[i] instanceof
|
|
3191
|
+
const isStack = stack.children[i] instanceof import_ui2.Stack;
|
|
2766
3192
|
if (isStack !== !this.itemIsInlineOnly(newToken.items[i])) return false;
|
|
2767
3193
|
}
|
|
2768
3194
|
const availableWidth = this.activeBlockMetrics?.availableWidth ?? this.maxWidth;
|
|
@@ -2821,7 +3247,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2821
3247
|
* *token runs* split at the last image, never token index against child index.
|
|
2822
3248
|
*/
|
|
2823
3249
|
updateImageParagraph(entity, oldToken, newToken) {
|
|
2824
|
-
if (!(entity instanceof
|
|
3250
|
+
if (!(entity instanceof import_ui2.Stack)) return false;
|
|
2825
3251
|
const oldTokens = oldToken.tokens;
|
|
2826
3252
|
const newTokens = newToken.tokens;
|
|
2827
3253
|
if (!oldTokens || !newTokens) return false;
|
|
@@ -2846,7 +3272,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2846
3272
|
entity.add(this.inlineRunRichText(newTail, availableWidth, t));
|
|
2847
3273
|
} else {
|
|
2848
3274
|
const tailEntity = entity.children[entity.children.length - 1];
|
|
2849
|
-
if (!(tailEntity instanceof
|
|
3275
|
+
if (!(tailEntity instanceof import_ui2.RichText)) return false;
|
|
2850
3276
|
tailEntity.setSpans(this.inlineRunSpans(newTail, t));
|
|
2851
3277
|
}
|
|
2852
3278
|
const last = entity.children[entity.children.length - 1];
|
|
@@ -2878,7 +3304,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2878
3304
|
* (its keys are `text`/`tokens`/`header`/`align`).
|
|
2879
3305
|
*/
|
|
2880
3306
|
updateStreamedTable(entity, oldToken, newToken) {
|
|
2881
|
-
if (!(entity instanceof
|
|
3307
|
+
if (!(entity instanceof import_ui2.Table)) return false;
|
|
2882
3308
|
if (oldToken.header.length !== newToken.header.length) return false;
|
|
2883
3309
|
for (let c = 0; c < oldToken.header.length; c++) {
|
|
2884
3310
|
if (oldToken.header[c].text !== newToken.header[c].text) return false;
|
|
@@ -2899,7 +3325,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2899
3325
|
if (lastRetained >= 0) {
|
|
2900
3326
|
for (let c = 0; c < oldToken.header.length; c++) {
|
|
2901
3327
|
const cell = entity.rows[lastRetained]?.[c];
|
|
2902
|
-
if (!(cell instanceof
|
|
3328
|
+
if (!(cell instanceof import_ui2.RichText)) return false;
|
|
2903
3329
|
}
|
|
2904
3330
|
}
|
|
2905
3331
|
const t = this.theme;
|
|
@@ -2932,7 +3358,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
2932
3358
|
const newTail = newInner[tail];
|
|
2933
3359
|
if (oldTail.type !== newTail.type) return false;
|
|
2934
3360
|
const innerStack = container.children[1];
|
|
2935
|
-
if (!(innerStack instanceof
|
|
3361
|
+
if (!(innerStack instanceof import_ui2.Stack)) return false;
|
|
2936
3362
|
const wrapper = innerStack.children.at(-1);
|
|
2937
3363
|
if (!wrapper || wrapper.children.length !== 1) return false;
|
|
2938
3364
|
const entity = wrapper.children[0];
|
|
@@ -3393,23 +3819,40 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3393
3819
|
if (!mathData) return null;
|
|
3394
3820
|
const intrinsicW = exToPx(mathData.widthEx, t.fontSize);
|
|
3395
3821
|
const intrinsicH = exToPx(mathData.heightEx, t.fontSize);
|
|
3396
|
-
const
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3822
|
+
const scale = Math.min(1, availableWidth / intrinsicW);
|
|
3823
|
+
const width = intrinsicW * scale;
|
|
3824
|
+
const height = intrinsicH * scale;
|
|
3825
|
+
const uri = mathData.uri;
|
|
3826
|
+
const math = new import_ui2.RichText(
|
|
3827
|
+
[
|
|
3828
|
+
{
|
|
3829
|
+
text: import_core.OBJECT_REPLACEMENT,
|
|
3830
|
+
object: {
|
|
3831
|
+
width,
|
|
3832
|
+
height,
|
|
3833
|
+
// The TeX source is what a reader copies and what a screen reader
|
|
3834
|
+
// announces. KaTeX's dual-layer contract carries the same string in an
|
|
3835
|
+
// `<annotation encoding="application/x-tex">`; here the projection is
|
|
3836
|
+
// the semantic layer, so one copy of the source serves both.
|
|
3837
|
+
alt: formula,
|
|
3838
|
+
paint: (surface, box) => paintInlineMath(uri, surface, box)
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
],
|
|
3842
|
+
{
|
|
3843
|
+
font: `${t.fontSize}px ${t.bodyFont}`,
|
|
3844
|
+
color: t.textColor,
|
|
3845
|
+
maxWidth: availableWidth,
|
|
3846
|
+
selectable: this.selectable
|
|
3405
3847
|
}
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
wrapper.
|
|
3412
|
-
wrapper.
|
|
3848
|
+
);
|
|
3849
|
+
this.subscribeInlineMathRepaint();
|
|
3850
|
+
const wrapper = new MathBlock(formula, uri);
|
|
3851
|
+
math.x = 16;
|
|
3852
|
+
math.y = 8;
|
|
3853
|
+
wrapper.add(math);
|
|
3854
|
+
wrapper.width = width + 16;
|
|
3855
|
+
wrapper.height = height + 16;
|
|
3413
3856
|
return wrapper;
|
|
3414
3857
|
}
|
|
3415
3858
|
renderToken(token) {
|
|
@@ -3459,7 +3902,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3459
3902
|
this.onLinkClick
|
|
3460
3903
|
);
|
|
3461
3904
|
}
|
|
3462
|
-
const stack = new
|
|
3905
|
+
const stack = new import_ui2.Stack({
|
|
3463
3906
|
direction: "vertical",
|
|
3464
3907
|
gap: 16,
|
|
3465
3908
|
maxWidth: availableWidth
|
|
@@ -3471,7 +3914,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3471
3914
|
currentTokens = [];
|
|
3472
3915
|
}
|
|
3473
3916
|
};
|
|
3474
|
-
for (const child of pToken.tokens) {
|
|
3917
|
+
for (const child of liftNestedImages(pToken.tokens)) {
|
|
3475
3918
|
if (child.type === "image") {
|
|
3476
3919
|
flushText();
|
|
3477
3920
|
stack.add(this.paragraphImage(child, availableWidth));
|
|
@@ -3499,12 +3942,15 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3499
3942
|
const mathBlock = this.renderDisplayMath(codeToken.text, availableWidth);
|
|
3500
3943
|
if (mathBlock) return mathBlock;
|
|
3501
3944
|
}
|
|
3502
|
-
return
|
|
3945
|
+
return this.withBlockAffordances(
|
|
3946
|
+
new CodeBlock(codeToken.text, lang, availableWidth, t, this.selectable),
|
|
3947
|
+
() => this.codeBlockAffordances(codeToken.text, lang)
|
|
3948
|
+
);
|
|
3503
3949
|
}
|
|
3504
3950
|
// ── Blockquotes ──────────────────────────────────────────────────
|
|
3505
3951
|
case "blockquote": {
|
|
3506
3952
|
const bqToken = token;
|
|
3507
|
-
const innerStack = new
|
|
3953
|
+
const innerStack = new import_ui2.Stack({ direction: "vertical", gap: 8 });
|
|
3508
3954
|
const indentStart = Math.min(16, availableWidth);
|
|
3509
3955
|
const childMetrics = {
|
|
3510
3956
|
marginBefore: 0,
|
|
@@ -3540,7 +3986,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3540
3986
|
// ── Lists ────────────────────────────────────────────────
|
|
3541
3987
|
case "list": {
|
|
3542
3988
|
const listToken = token;
|
|
3543
|
-
const listStack = new
|
|
3989
|
+
const listStack = new import_ui2.Stack({ direction: "vertical", gap: 6 });
|
|
3544
3990
|
for (let i = 0; i < listToken.items.length; i++) {
|
|
3545
3991
|
listStack.add(
|
|
3546
3992
|
this.itemIsInlineOnly(listToken.items[i]) ? this.listItemRichText(listToken, i, availableWidth, t) : this.listItemBlockStack(listToken, i, availableWidth, t)
|
|
@@ -3555,21 +4001,24 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3555
4001
|
const rows = tblToken.rows.map(
|
|
3556
4002
|
(row) => row.map((cell) => this.tableCellRichText(cell, false, t))
|
|
3557
4003
|
);
|
|
3558
|
-
return
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
4004
|
+
return this.withBlockAffordances(
|
|
4005
|
+
new import_ui2.Table({
|
|
4006
|
+
headers,
|
|
4007
|
+
rows,
|
|
4008
|
+
// `| :--- | :---: | ---: |` already resolves to this on the token; it
|
|
4009
|
+
// was previously discarded, so every column rendered left-aligned.
|
|
4010
|
+
align: tblToken.align,
|
|
4011
|
+
width: availableWidth,
|
|
4012
|
+
textColor: t.textColor,
|
|
4013
|
+
headerTextColor: t.headingColor,
|
|
4014
|
+
font: `${t.fontSize - 2}px ${t.bodyFont}`,
|
|
4015
|
+
borderColor: t.hrColor,
|
|
4016
|
+
bg: t.tableBgColor,
|
|
4017
|
+
headerBg: t.tableHeaderBgColor,
|
|
4018
|
+
selectable: this.selectable
|
|
4019
|
+
}),
|
|
4020
|
+
() => this.tableAffordances(tblToken)
|
|
4021
|
+
);
|
|
3573
4022
|
}
|
|
3574
4023
|
// ── Horizontal rule ──────────────────────────────────────────────
|
|
3575
4024
|
case "hr":
|
|
@@ -3588,7 +4037,7 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3588
4037
|
// ── Fallback ─────────────────────────────────────────────────────
|
|
3589
4038
|
default:
|
|
3590
4039
|
if ("text" in token) {
|
|
3591
|
-
return new
|
|
4040
|
+
return new import_ui2.Text(token.text, {
|
|
3592
4041
|
font: bodyFont,
|
|
3593
4042
|
color: t.textColor,
|
|
3594
4043
|
maxWidth: availableWidth,
|
|
@@ -3605,12 +4054,22 @@ var Markdown = class _Markdown extends import_ui.UIComponent {
|
|
|
3605
4054
|
};
|
|
3606
4055
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3607
4056
|
0 && (module.exports = {
|
|
4057
|
+
BlockAffordanceButton,
|
|
4058
|
+
BlockWithAffordances,
|
|
3608
4059
|
CodeBlock,
|
|
3609
4060
|
Markdown,
|
|
4061
|
+
MathBlock,
|
|
3610
4062
|
codeAtlas,
|
|
3611
4063
|
codeAtlasStats,
|
|
4064
|
+
escapeCsvField,
|
|
4065
|
+
escapeMarkdownTableCell,
|
|
4066
|
+
extensionForLanguage,
|
|
3612
4067
|
isMathJaxReady,
|
|
4068
|
+
mimeForLanguage,
|
|
3613
4069
|
parseFrontMatterFields,
|
|
3614
4070
|
preloadMathJax,
|
|
3615
|
-
scanFrontMatter
|
|
4071
|
+
scanFrontMatter,
|
|
4072
|
+
tableContentOf,
|
|
4073
|
+
tableToCsv,
|
|
4074
|
+
tableToMarkdown
|
|
3616
4075
|
});
|