@rathkit/rte-core 0.1.0-alpha.3 → 0.1.0-alpha.4
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/dom/render.js +25 -14
- package/dist/dom/view.d.ts +5 -3
- package/dist/dom/view.js +41 -23
- package/dist/edit.js +6 -3
- package/dist/engine.d.ts +7 -1
- package/dist/engine.js +19 -3
- package/dist/export.d.ts +2 -2
- package/dist/export.js +22 -6
- package/dist/footnotes/index.d.ts +2 -0
- package/dist/footnotes/index.js +2 -0
- package/dist/footnotes/plugin.d.ts +6 -0
- package/dist/footnotes/plugin.js +130 -0
- package/dist/footnotes/reconcile.d.ts +4 -0
- package/dist/footnotes/reconcile.js +155 -0
- package/dist/images/images.js +4 -3
- package/dist/images/index.d.ts +5 -1
- package/dist/images/index.js +3 -1
- package/dist/{dom/imageview.d.ts → images/view.d.ts} +8 -5
- package/dist/{dom/imageview.js → images/view.js} +16 -10
- package/dist/import.js +55 -18
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/lists/lists.js +45 -30
- package/dist/markdown/detect.d.ts +1 -0
- package/dist/markdown/detect.js +81 -0
- package/dist/markdown/export.d.ts +10 -0
- package/dist/markdown/export.js +252 -0
- package/dist/markdown/index.d.ts +5 -0
- package/dist/markdown/index.js +4 -0
- package/dist/markdown/inline.d.ts +35 -0
- package/dist/markdown/inline.js +604 -0
- package/dist/markdown/parse.d.ts +4 -0
- package/dist/markdown/parse.js +586 -0
- package/dist/markdown/plugin.d.ts +20 -0
- package/dist/markdown/plugin.js +53 -0
- package/dist/model.d.ts +25 -2
- package/dist/model.js +2 -0
- package/dist/notes.d.ts +6 -0
- package/dist/notes.js +49 -0
- package/dist/ops.d.ts +6 -1
- package/dist/ops.js +5 -4
- package/dist/path.d.ts +3 -1
- package/dist/path.js +91 -5
- package/dist/plugin.d.ts +52 -1
- package/dist/standard/index.d.ts +5 -2
- package/dist/standard/index.js +6 -4
- package/dist/{dom/tableview.d.ts → tables/cells.d.ts} +6 -5
- package/dist/{dom/tableview.js → tables/cells.js} +13 -9
- package/dist/{dom/tablegrips.d.ts → tables/grips.d.ts} +5 -4
- package/dist/{dom/tablegrips.js → tables/grips.js} +8 -7
- package/dist/tables/index.d.ts +5 -1
- package/dist/tables/index.js +3 -1
- package/dist/tables/view.d.ts +2 -0
- package/dist/tables/view.js +15 -0
- package/package.json +23 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const LIMIT = 200_000;
|
|
2
|
+
const LINE_LIMIT = 2_000;
|
|
3
|
+
const isList = (line) => /^ {0,6}(?:[-*+]|\d{1,9}[.)])[ \t]+\S/.test(line.slice(0, 64));
|
|
4
|
+
const isRow = (line) => line.includes("|") && /^[\s|:-]+$/.test(line) && line.includes("-");
|
|
5
|
+
function pair(text, mark) {
|
|
6
|
+
let from = 0;
|
|
7
|
+
for (let tries = 0; tries < 50; tries++) {
|
|
8
|
+
const a = text.indexOf(mark, from);
|
|
9
|
+
if (a === -1)
|
|
10
|
+
return false;
|
|
11
|
+
const inner = a + mark.length;
|
|
12
|
+
if (!/\s/.test(text.charAt(inner)) && inner < text.length) {
|
|
13
|
+
const b = text.indexOf(mark, inner + 1);
|
|
14
|
+
if (b !== -1 && !/\s/.test(text.charAt(b - 1)) && text.slice(inner, b).indexOf("\n\n") === -1)
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
from = a + mark.length;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function link(text) {
|
|
22
|
+
let from = 0;
|
|
23
|
+
for (let tries = 0; tries < 50; tries++) {
|
|
24
|
+
const mid = text.indexOf("](", from);
|
|
25
|
+
if (mid === -1)
|
|
26
|
+
return false;
|
|
27
|
+
const open = text.lastIndexOf("[", mid);
|
|
28
|
+
const close = text.indexOf(")", mid);
|
|
29
|
+
const label = open === -1 ? "" : text.slice(open + 1, mid);
|
|
30
|
+
if (open !== -1 && close !== -1 && label !== "" && !label.includes("\n") && close - mid < 2000)
|
|
31
|
+
return true;
|
|
32
|
+
from = mid + 2;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
export function looksLikeMarkdown(input) {
|
|
37
|
+
const text = input.length > LIMIT ? input.slice(0, LIMIT) : input;
|
|
38
|
+
const lines = text.split(/\r\n?|\n/);
|
|
39
|
+
let headings = 0, listLines = 0, tasks = 0, quotes = 0, notes = 0;
|
|
40
|
+
let fence = false, table = false;
|
|
41
|
+
for (let i = 0; i < lines.length; i++) {
|
|
42
|
+
const line = lines[i].slice(0, LINE_LIMIT);
|
|
43
|
+
if (/^ {0,3}(?:`{3,}|~{3,})/.test(line))
|
|
44
|
+
fence = true;
|
|
45
|
+
else if (/^ {0,3}#{1,6}[ \t]+\S/.test(line))
|
|
46
|
+
headings++;
|
|
47
|
+
else if (/^ {0,3}>/.test(line))
|
|
48
|
+
quotes++;
|
|
49
|
+
else if (/^ {0,3}\[\^[^\]\s]{1,64}\]:[ \t]*\S/.test(line))
|
|
50
|
+
notes++;
|
|
51
|
+
else if (isList(line)) {
|
|
52
|
+
listLines++;
|
|
53
|
+
if (/^\s*(?:[-*+]|\d+[.)])[ \t]+\[[ xX]\][ \t]/.test(line))
|
|
54
|
+
tasks++;
|
|
55
|
+
}
|
|
56
|
+
else if (line.includes("|") && i + 1 < lines.length && isRow(lines[i + 1].slice(0, LINE_LIMIT)) && line.replace(/[\s|]/g, "") !== "")
|
|
57
|
+
table = true;
|
|
58
|
+
}
|
|
59
|
+
let score = 0;
|
|
60
|
+
if (fence)
|
|
61
|
+
score += 3;
|
|
62
|
+
if (table)
|
|
63
|
+
score += 3;
|
|
64
|
+
if (headings > 0)
|
|
65
|
+
score += headings > 1 ? 2 : 1;
|
|
66
|
+
if (listLines >= 2)
|
|
67
|
+
score += 2;
|
|
68
|
+
if (tasks > 0)
|
|
69
|
+
score += 2;
|
|
70
|
+
if (quotes > 0)
|
|
71
|
+
score += 1;
|
|
72
|
+
if (notes > 0)
|
|
73
|
+
score += 2;
|
|
74
|
+
if (link(text))
|
|
75
|
+
score += 2;
|
|
76
|
+
if (pair(text, "**") || pair(text, "~~"))
|
|
77
|
+
score += 2;
|
|
78
|
+
if (pair(text, "`"))
|
|
79
|
+
score += 1;
|
|
80
|
+
return score >= 2;
|
|
81
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Doc } from "../model.ts";
|
|
2
|
+
/** The document as Markdown (CommonMark with the GitHub table, task item and strikethrough additions). */
|
|
3
|
+
export declare function exportMarkdown(doc: Doc, options?: MarkdownExportOptions): string;
|
|
4
|
+
export interface MarkdownExportOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Write underline, superscript and subscript as `<u>`, `<sup>` and `<sub>`, and a line break in a table cell or heading as `<br>`. The
|
|
7
|
+
* reader here understands those tags, so they come back; other Markdown readers show them as HTML. Default false: those details are dropped.
|
|
8
|
+
*/
|
|
9
|
+
readonly inlineHtml?: boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { noteIds } from "../notes.js";
|
|
2
|
+
const SPAN_ORDER = ["link", "bold", "italic", "strike"];
|
|
3
|
+
const HTML_SPAN_ORDER = ["link", "bold", "italic", "underline", "strike", "sup", "sub"];
|
|
4
|
+
const SPAN_MARK = {
|
|
5
|
+
bold: ["**", "**"], italic: ["*", "*"], strike: ["~~", "~~"], underline: ["<u>", "</u>"], sup: ["<sup>", "</sup>"], sub: ["<sub>", "</sub>"],
|
|
6
|
+
};
|
|
7
|
+
let inlineHtml = false;
|
|
8
|
+
let labels = new Map();
|
|
9
|
+
const spanMarks = (marks) => (inlineHtml ? HTML_SPAN_ORDER : SPAN_ORDER).flatMap((type) => marks.filter((m) => m.type === type));
|
|
10
|
+
const sameSpan = (a, b) => a.type === b.type && (a.type !== "link" || (b.type === "link" && a.href === b.href));
|
|
11
|
+
const hasSpan = (list, m) => list.some((x) => sameSpan(x, m));
|
|
12
|
+
function destination(href) {
|
|
13
|
+
if (/[\s()<>\x00-\x1f]/.test(href))
|
|
14
|
+
return `<${href.replace(/</g, "%3C").replace(/>/g, "%3E").replace(/[\x00-\x1f]/g, (c) => encodeURIComponent(c)).replace(/\n/g, "%0A")}>`;
|
|
15
|
+
return href.replace(/\\/g, "\\\\");
|
|
16
|
+
}
|
|
17
|
+
function escapeText(text, inLink, table) {
|
|
18
|
+
let out = "";
|
|
19
|
+
for (let i = 0; i < text.length; i++) {
|
|
20
|
+
const c = text[i];
|
|
21
|
+
if (c === "\\" || c === "`" || c === "*" || c === "[" || c === "]" || c === "<" || c === "~")
|
|
22
|
+
out += `\\${c}`;
|
|
23
|
+
else if (c === "|" && table)
|
|
24
|
+
out += "\\|";
|
|
25
|
+
else if (c === "_") {
|
|
26
|
+
const intraword = /[\p{L}\p{N}]/u.test(text[i - 1] ?? "") && /[\p{L}\p{N}]/u.test(text[i + 1] ?? "");
|
|
27
|
+
out += intraword ? c : "\\_";
|
|
28
|
+
}
|
|
29
|
+
else if (c === "&" && /^&(?:#\d{1,7}|#[xX][0-9a-fA-F]{1,6}|[A-Za-z][A-Za-z0-9]{1,31});/.test(text.slice(i, i + 40)))
|
|
30
|
+
out += "\\&";
|
|
31
|
+
else
|
|
32
|
+
out += c;
|
|
33
|
+
}
|
|
34
|
+
if (!inLink)
|
|
35
|
+
out = out.replace(/\b(https?):\/\//gi, "$1\\://").replace(/\bwww\./gi, "www\\.");
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function codeSpan(text) {
|
|
39
|
+
let longest = 0;
|
|
40
|
+
for (const run of text.match(/`+/g) ?? [])
|
|
41
|
+
longest = Math.max(longest, run.length);
|
|
42
|
+
const fence = "`".repeat(longest === 0 ? 1 : longest + 1);
|
|
43
|
+
const pad = text.startsWith("`") || text.endsWith("`") || (text.startsWith(" ") && text.endsWith(" ") && text.trim() !== "");
|
|
44
|
+
return pad ? `${fence} ${text} ${fence}` : `${fence}${text}${fence}`;
|
|
45
|
+
}
|
|
46
|
+
function inline(content, flat, table) {
|
|
47
|
+
const segments = [];
|
|
48
|
+
const runs = content.map((node) => (node.type === "text" ? { node, spans: spanMarks(node.marks), code: node.marks.some((m) => m.type === "code") } : { node, spans: [], code: false }));
|
|
49
|
+
runs.forEach((run, r) => {
|
|
50
|
+
const node = run.node;
|
|
51
|
+
if (node.type === "br") {
|
|
52
|
+
segments.push({ text: "", spans: [], code: false, br: true });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (node.type === "image") {
|
|
56
|
+
segments.push({ text: "", spans: [], code: false, image: node });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (node.type === "note") {
|
|
60
|
+
segments.push({ text: "", spans: [], code: false, raw: `[^${labels.get(node.id) ?? node.id}]` });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const text = node.text;
|
|
64
|
+
if (run.code) {
|
|
65
|
+
segments.push({ text, spans: run.spans, code: true });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const lead = /^\s*/.exec(text)?.[0] ?? "";
|
|
69
|
+
const trail = lead.length === text.length ? "" : /\s*$/.exec(text)?.[0] ?? "";
|
|
70
|
+
const core = text.slice(lead.length, text.length - trail.length);
|
|
71
|
+
const prev = runs[r - 1];
|
|
72
|
+
const next = runs[r + 1];
|
|
73
|
+
const shared = (other) => (other && other.node.type === "text" ? run.spans.filter((m) => hasSpan(other.spans, m)) : []);
|
|
74
|
+
if (lead)
|
|
75
|
+
segments.push({ text: lead, spans: shared(prev), code: false });
|
|
76
|
+
if (core)
|
|
77
|
+
segments.push({ text: core, spans: run.spans, code: false });
|
|
78
|
+
if (trail)
|
|
79
|
+
segments.push({ text: trail, spans: shared(next), code: false });
|
|
80
|
+
});
|
|
81
|
+
let out = "";
|
|
82
|
+
let active = [];
|
|
83
|
+
const open = (m) => { out += m.type === "link" ? "[" : SPAN_MARK[m.type][0]; };
|
|
84
|
+
const close = (m) => { out += m.type === "link" ? `](${destination(m.href)})` : SPAN_MARK[m.type][1]; };
|
|
85
|
+
const move = (target) => {
|
|
86
|
+
let keep = 0;
|
|
87
|
+
while (keep < active.length && keep < target.length && sameSpan(active[keep], target[keep]))
|
|
88
|
+
keep++;
|
|
89
|
+
for (let k = active.length - 1; k >= keep; k--)
|
|
90
|
+
close(active[k]);
|
|
91
|
+
for (let k = keep; k < target.length; k++)
|
|
92
|
+
open(target[k]);
|
|
93
|
+
active = [...target];
|
|
94
|
+
};
|
|
95
|
+
for (const seg of segments) {
|
|
96
|
+
if (seg.br) {
|
|
97
|
+
move([]);
|
|
98
|
+
out += flat ? (inlineHtml ? "<br>" : " ") : "\\\n";
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (seg.raw !== undefined) {
|
|
102
|
+
move([]);
|
|
103
|
+
out += seg.raw;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (seg.image) {
|
|
107
|
+
move(seg.spans);
|
|
108
|
+
out += `})`;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
move(seg.spans);
|
|
112
|
+
out += seg.code ? codeSpan(seg.text) : escapeText(seg.text, active.some((m) => m.type === "link"), table);
|
|
113
|
+
}
|
|
114
|
+
move([]);
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
function guardLines(text) {
|
|
118
|
+
return text.split("\n").map((line) => {
|
|
119
|
+
if (/^\s{4}/.test(line))
|
|
120
|
+
return line;
|
|
121
|
+
const numbered = /^(\d{1,9})([.)])(?=[ \t]|$)/.exec(line);
|
|
122
|
+
if (numbered)
|
|
123
|
+
return `${numbered[1]}\\${line.slice(numbered[1].length)}`;
|
|
124
|
+
if (/^(#{1,6}([ \t]|$)|>|[-+]([ \t]|$)|[-=]+[ \t]*$|~~~|---|___)/.test(line))
|
|
125
|
+
return `\\${line}`;
|
|
126
|
+
return line;
|
|
127
|
+
}).join("\n");
|
|
128
|
+
}
|
|
129
|
+
function alignOf(cell) {
|
|
130
|
+
const b = cell?.blocks[0];
|
|
131
|
+
return b?.type === "p" ? b.align : undefined;
|
|
132
|
+
}
|
|
133
|
+
function cellText(cell) {
|
|
134
|
+
const parts = [];
|
|
135
|
+
const walk = (blocks) => {
|
|
136
|
+
for (const b of blocks) {
|
|
137
|
+
if (b.type === "p" || b.type === "h")
|
|
138
|
+
parts.push(inline(b.content, true, true));
|
|
139
|
+
else if (b.type === "pre")
|
|
140
|
+
parts.push(codeSpan(b.text.replace(/\n/g, " ")));
|
|
141
|
+
else if (b.type === "quote")
|
|
142
|
+
walk(b.blocks);
|
|
143
|
+
else if (b.type === "ul" || b.type === "ol")
|
|
144
|
+
for (const item of b.items)
|
|
145
|
+
parts.push(inline(item.content, true, true));
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
walk(cell.blocks);
|
|
149
|
+
return parts.filter((p) => p !== "").join(" ");
|
|
150
|
+
}
|
|
151
|
+
function table(t) {
|
|
152
|
+
const grid = [];
|
|
153
|
+
t.rows.forEach((row, r) => {
|
|
154
|
+
const line = (grid[r] ??= []);
|
|
155
|
+
let c = 0;
|
|
156
|
+
for (const cell of row) {
|
|
157
|
+
while (line[c] !== undefined)
|
|
158
|
+
c++;
|
|
159
|
+
for (let dr = 0; dr < Math.max(1, cell.rowspan); dr++) {
|
|
160
|
+
const target = (grid[r + dr] ??= []);
|
|
161
|
+
for (let dc = 0; dc < Math.max(1, cell.colspan); dc++)
|
|
162
|
+
target[c + dc] = dr === 0 && dc === 0 ? cell : null;
|
|
163
|
+
}
|
|
164
|
+
c += Math.max(1, cell.colspan);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
const width = Math.max(1, ...grid.map((row) => row.length));
|
|
168
|
+
const line = (row) => `| ${Array.from({ length: width }, (_, c) => { const cell = row[c]; return cell ? cellText(cell) : ""; }).join(" | ")} |`;
|
|
169
|
+
const head = grid[0] ?? [];
|
|
170
|
+
const delimiter = Array.from({ length: width }, (_, c) => {
|
|
171
|
+
const a = alignOf(head[c] ?? undefined) ?? alignOf(grid.find((row) => row[c])?.[c] ?? undefined);
|
|
172
|
+
return a === "center" ? ":---:" : a === "right" ? "---:" : a === "left" ? ":---" : "---";
|
|
173
|
+
});
|
|
174
|
+
return [line(head), `| ${delimiter.join(" | ")} |`, ...grid.slice(1).map(line)].join("\n");
|
|
175
|
+
}
|
|
176
|
+
const indentLines = (text, by, first) => text.split("\n").map((l, i) => (i === 0 ? first + l : l === "" ? "" : by + l)).join("\n");
|
|
177
|
+
function list(l, flip) {
|
|
178
|
+
const notes = l.notes === true;
|
|
179
|
+
const start = l.type === "ol" ? Math.max(0, Math.min(l.start ?? 1, 999_999_999 - l.items.length)) : 0;
|
|
180
|
+
return l.items.map((item, i) => {
|
|
181
|
+
const mark = notes ? `[^${labels.get(item.id ?? "") ?? item.id}]:` : l.type === "ul" ? (flip ? "*" : "-") : `${start + i}${flip ? ")" : "."}`;
|
|
182
|
+
const task = notes || item.checked === undefined ? "" : item.checked ? "[x] " : "[ ] ";
|
|
183
|
+
const paragraphs = inlineSplit(item.content).map((p) => guardLines(p));
|
|
184
|
+
const body = [`${task}${paragraphs[0] ?? ""}`, ...paragraphs.slice(1)].join("\n\n");
|
|
185
|
+
let text = body;
|
|
186
|
+
let before;
|
|
187
|
+
let beforeFlip = false;
|
|
188
|
+
for (const child of item.children) {
|
|
189
|
+
const isList = child.type === "ul" || child.type === "ol";
|
|
190
|
+
const flipNext = isList && before !== undefined && before.type === child.type ? !beforeFlip : false;
|
|
191
|
+
const rendered = child.type === "ul" || child.type === "ol" ? list(child, flipNext) : render(child, false);
|
|
192
|
+
const gap = isList && (before === undefined || before.type === "ul" || before.type === "ol") ? "\n" : "\n\n";
|
|
193
|
+
text = text === "" && before === undefined ? rendered : `${text}${gap}${rendered}`;
|
|
194
|
+
before = child;
|
|
195
|
+
beforeFlip = flipNext;
|
|
196
|
+
}
|
|
197
|
+
return indentLines(text, " ".repeat(notes ? 4 : mark.length + 1), `${mark} `).replace(/^(\S+) (\n|$)/, "$1$2");
|
|
198
|
+
}).join(notes ? "\n\n" : "\n");
|
|
199
|
+
}
|
|
200
|
+
function inlineSplit(content) {
|
|
201
|
+
const parts = [[]];
|
|
202
|
+
for (const node of content) {
|
|
203
|
+
if (node.type === "br" && node.paragraph)
|
|
204
|
+
parts.push([]);
|
|
205
|
+
else
|
|
206
|
+
parts[parts.length - 1].push(node);
|
|
207
|
+
}
|
|
208
|
+
return parts.map((p) => inline(p, false, false));
|
|
209
|
+
}
|
|
210
|
+
function blocks(list_) {
|
|
211
|
+
const out = [];
|
|
212
|
+
let previous;
|
|
213
|
+
let flip = false;
|
|
214
|
+
for (const block of list_) {
|
|
215
|
+
flip = previous && (previous.type === "ul" || previous.type === "ol") && previous.type === block.type ? !flip : false;
|
|
216
|
+
const text = render(block, flip);
|
|
217
|
+
if (text !== "")
|
|
218
|
+
out.push(text);
|
|
219
|
+
previous = block;
|
|
220
|
+
}
|
|
221
|
+
return out.join("\n\n");
|
|
222
|
+
}
|
|
223
|
+
function render(block, flip) {
|
|
224
|
+
switch (block.type) {
|
|
225
|
+
case "p": return guardLines(inline(block.content, false, false));
|
|
226
|
+
case "h": return `${"#".repeat(block.level)} ${inline(block.content, true, false)}`.trimEnd();
|
|
227
|
+
case "ul":
|
|
228
|
+
case "ol": return list(block, flip);
|
|
229
|
+
case "dl": return block.entries.map((e) => guardLines(inline(e.content, false, false))).filter((t) => t !== "").join("\n\n");
|
|
230
|
+
case "table": return table(block);
|
|
231
|
+
case "quote": return blocks(block.blocks).split("\n").map((l) => (l === "" ? ">" : `> ${l}`)).join("\n") || ">";
|
|
232
|
+
case "pre": {
|
|
233
|
+
let longest = 0;
|
|
234
|
+
for (const run of block.text.match(/`+/g) ?? [])
|
|
235
|
+
longest = Math.max(longest, run.length);
|
|
236
|
+
const fence = "`".repeat(Math.max(3, longest + 1));
|
|
237
|
+
return block.text === "" ? `${fence}\n${fence}` : `${fence}\n${block.text}\n${fence}`;
|
|
238
|
+
}
|
|
239
|
+
case "hr": return "---";
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
export function exportMarkdown(doc, options = {}) {
|
|
243
|
+
inlineHtml = options.inlineHtml === true;
|
|
244
|
+
labels = new Map(noteIds(doc).map((id, i) => [id, String(i + 1)]));
|
|
245
|
+
try {
|
|
246
|
+
return blocks(doc.blocks);
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
inlineHtml = false;
|
|
250
|
+
labels = new Map();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { parseMarkdown } from "./parse.ts";
|
|
2
|
+
export { exportMarkdown, type MarkdownExportOptions } from "./export.ts";
|
|
3
|
+
export { looksLikeMarkdown } from "./detect.ts";
|
|
4
|
+
export { markdown, type MarkdownPasteOptions } from "./plugin.ts";
|
|
5
|
+
export type { MarkdownOptions } from "./inline.ts";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Inline, type ListItem } from "../model.ts";
|
|
2
|
+
export interface MarkdownOptions {
|
|
3
|
+
/** URL schemes a link may use besides relative and fragment addresses. Default: http, https, mailto, tel. */
|
|
4
|
+
readonly linkSchemes?: readonly string[];
|
|
5
|
+
/** Cleans an SVG data address, or returns undefined to refuse it. Without it, SVG images are refused. */
|
|
6
|
+
readonly sanitizeSvg?: ((dataUrl: string) => string | undefined) | undefined;
|
|
7
|
+
/** Largest `data:` image address, in characters. */
|
|
8
|
+
readonly maxDataImageLength?: number;
|
|
9
|
+
}
|
|
10
|
+
/** Link reference definitions found in the document: `[label]: address "title"`. Keys come from `refLabel`. */
|
|
11
|
+
export type Refs = ReadonlyMap<string, {
|
|
12
|
+
readonly dest: string;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* The footnotes of the text being read. `keys` has a label (in `refLabel` form) for each definition found before reading begins;
|
|
16
|
+
* `use` gives the id for one more reference to a label (a label used twice gets two ids, as each reference has its own note).
|
|
17
|
+
*/
|
|
18
|
+
export interface NoteState {
|
|
19
|
+
readonly keys: ReadonlyMap<string, string>;
|
|
20
|
+
readonly items: Map<string, ListItem>;
|
|
21
|
+
/** Each id handed out, with the label it stands for, in the order the references were read. */
|
|
22
|
+
readonly used: Map<string, string>;
|
|
23
|
+
use(key: string): string | undefined;
|
|
24
|
+
}
|
|
25
|
+
/** What a footnote label may be: letters, digits, underscore, dot and dash. */
|
|
26
|
+
export declare const NOTE_LABEL: RegExp;
|
|
27
|
+
export interface InlineContext extends MarkdownOptions {
|
|
28
|
+
readonly refs?: Refs;
|
|
29
|
+
readonly notes?: NoteState | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** The form of a reference label that is compared: case and runs of white space do not matter. */
|
|
32
|
+
export declare const refLabel: (label: string) => string;
|
|
33
|
+
export declare function parseInline(src: string, options?: InlineContext): Inline[];
|
|
34
|
+
/** Backslash escapes and entities in an address or title. */
|
|
35
|
+
export declare function unescape(raw: string): string;
|