typewright 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/SPEC.md +327 -0
- package/dist/chunk-CZQO7TTL.js +1533 -0
- package/dist/chunk-CZQO7TTL.js.map +1 -0
- package/dist/chunk-KNEKNZXK.js +286 -0
- package/dist/chunk-KNEKNZXK.js.map +1 -0
- package/dist/chunk-M6IVEMXO.js +1398 -0
- package/dist/chunk-M6IVEMXO.js.map +1 -0
- package/dist/chunk-NJ7PJKHN.js +652 -0
- package/dist/chunk-NJ7PJKHN.js.map +1 -0
- package/dist/commands-Z3ndUSza.d.cts +221 -0
- package/dist/commands-Z3ndUSza.d.ts +221 -0
- package/dist/core/index.cjs +2952 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +410 -0
- package/dist/core/index.d.ts +410 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/mdx/index.cjs +662 -0
- package/dist/mdx/index.cjs.map +1 -0
- package/dist/mdx/index.d.cts +252 -0
- package/dist/mdx/index.d.ts +252 -0
- package/dist/mdx/index.js +3 -0
- package/dist/mdx/index.js.map +1 -0
- package/dist/react/index.cjs +7439 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +140 -0
- package/dist/react/index.d.ts +140 -0
- package/dist/react/index.js +3644 -0
- package/dist/react/index.js.map +1 -0
- package/dist/streaming/index.cjs +1494 -0
- package/dist/streaming/index.cjs.map +1 -0
- package/dist/streaming/index.d.cts +64 -0
- package/dist/streaming/index.d.ts +64 -0
- package/dist/streaming/index.js +4 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/types-CX1GOx0Y.d.cts +319 -0
- package/dist/types-CX1GOx0Y.d.ts +319 -0
- package/package.json +128 -0
|
@@ -0,0 +1,1494 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/ast.ts
|
|
4
|
+
function walk(node, visit) {
|
|
5
|
+
const go = (n) => {
|
|
6
|
+
if (visit(n) === false) return;
|
|
7
|
+
const kids = childrenOf(n);
|
|
8
|
+
for (const k of kids) go(k);
|
|
9
|
+
};
|
|
10
|
+
go(node);
|
|
11
|
+
}
|
|
12
|
+
function childrenOf(node) {
|
|
13
|
+
switch (node.type) {
|
|
14
|
+
case "document":
|
|
15
|
+
case "blockquote":
|
|
16
|
+
return node.children;
|
|
17
|
+
case "heading":
|
|
18
|
+
case "paragraph":
|
|
19
|
+
case "emphasis":
|
|
20
|
+
case "strong":
|
|
21
|
+
case "strikethrough":
|
|
22
|
+
case "tableCell":
|
|
23
|
+
return node.children;
|
|
24
|
+
case "link":
|
|
25
|
+
return node.children;
|
|
26
|
+
case "list":
|
|
27
|
+
return node.items;
|
|
28
|
+
case "listItem":
|
|
29
|
+
return node.children;
|
|
30
|
+
case "table":
|
|
31
|
+
return [...node.header, ...node.rows.flat()];
|
|
32
|
+
case "footnoteDef":
|
|
33
|
+
return node.children;
|
|
34
|
+
case "defList":
|
|
35
|
+
return node.items;
|
|
36
|
+
case "defItem":
|
|
37
|
+
return [...node.term, ...node.definitions.flat()];
|
|
38
|
+
default:
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/core/parser.ts
|
|
44
|
+
var MAX_URL = 4096;
|
|
45
|
+
var MAX_TITLE = 1024;
|
|
46
|
+
var MAX_ALT = 4096;
|
|
47
|
+
var MAX_LANG = 256;
|
|
48
|
+
var MAX_VALUE = 1e6;
|
|
49
|
+
var MAX_FOOTNOTE_ID = 256;
|
|
50
|
+
var cap = (s, n) => s.length > n ? s.slice(0, n) : s;
|
|
51
|
+
var opts = { math: false, footnotes: false, defLists: false };
|
|
52
|
+
function splitLines(src) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
let from = 0;
|
|
55
|
+
for (let i = 0; i <= src.length; i++) {
|
|
56
|
+
if (i === src.length || src[i] === "\n") {
|
|
57
|
+
let end = i;
|
|
58
|
+
if (end > from && src[end - 1] === "\r") end--;
|
|
59
|
+
lines.push({ from, to: end, text: src.slice(from, end) });
|
|
60
|
+
from = i + 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return lines;
|
|
64
|
+
}
|
|
65
|
+
var leadingSpaces = (t) => {
|
|
66
|
+
let n = 0;
|
|
67
|
+
while (n < t.length && t[n] === " ") n++;
|
|
68
|
+
return n;
|
|
69
|
+
};
|
|
70
|
+
var isBlank = (line) => line.text.trim() === "";
|
|
71
|
+
var FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;
|
|
72
|
+
var ATX_RE = /^( {0,3})(#{1,6})(?=\s|$)/;
|
|
73
|
+
var ITEM_RE = /^( {0,3})([-+*]|\d{1,9}[.)])( +|$)/;
|
|
74
|
+
var HTML_TAG_RE = /^<\/?([A-Za-z][A-Za-z0-9-]*)(?:[\s/>]|$)/;
|
|
75
|
+
var ESM_RE = /^(?:import|export)\b/;
|
|
76
|
+
var isFence = (t) => {
|
|
77
|
+
const m = FENCE_RE.exec(t);
|
|
78
|
+
if (!m) return false;
|
|
79
|
+
return m[2][0] !== "`" || !m[3].includes("`");
|
|
80
|
+
};
|
|
81
|
+
var isAtx = (t) => ATX_RE.test(t);
|
|
82
|
+
var isThematic = (t) => {
|
|
83
|
+
const trimmed = t.trim();
|
|
84
|
+
if (trimmed.length < 3) return false;
|
|
85
|
+
const compact = trimmed.replace(/ /g, "");
|
|
86
|
+
return /^(?:-{3,}|_{3,}|\*{3,})$/.test(compact);
|
|
87
|
+
};
|
|
88
|
+
var isQuote = (t) => /^ {0,3}>/.test(t);
|
|
89
|
+
function detectItem(line) {
|
|
90
|
+
const m = ITEM_RE.exec(line.text);
|
|
91
|
+
if (!m) return null;
|
|
92
|
+
const marker = m[2];
|
|
93
|
+
return {
|
|
94
|
+
indent: m[1].length,
|
|
95
|
+
marker,
|
|
96
|
+
markerLen: marker.length,
|
|
97
|
+
spacesAfter: m[3] === "" ? 0 : m[3].length,
|
|
98
|
+
ordered: /\d/.test(marker[0])
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function htmlVariant(t) {
|
|
102
|
+
const s = t.replace(/^ {0,3}/, "");
|
|
103
|
+
if (ESM_RE.test(s)) return "mdxFlow";
|
|
104
|
+
const m = HTML_TAG_RE.exec(s);
|
|
105
|
+
if (!m) return null;
|
|
106
|
+
return /[A-Z]/.test(m[1][0]) ? "mdxFlow" : "html";
|
|
107
|
+
}
|
|
108
|
+
function isDelimRow(t) {
|
|
109
|
+
let s = t.trim();
|
|
110
|
+
if (!s.includes("-")) return false;
|
|
111
|
+
s = s.replace(/^\|/, "").replace(/\|$/, "");
|
|
112
|
+
const cells = s.split("|");
|
|
113
|
+
return cells.length >= 1 && cells.every((c) => /^ *:?-+:? *$/.test(c));
|
|
114
|
+
}
|
|
115
|
+
function isTableStartAt(lines, j) {
|
|
116
|
+
const line = lines[j];
|
|
117
|
+
const next = lines[j + 1];
|
|
118
|
+
if (!line || !next) return false;
|
|
119
|
+
if (isBlank(line)) return false;
|
|
120
|
+
return line.text.includes("|") && isDelimRow(next.text);
|
|
121
|
+
}
|
|
122
|
+
var MATH_FENCE_RE = /^ {0,3}\$\$[ \t]*$/;
|
|
123
|
+
var isMathOpen = (t) => opts.math && MATH_FENCE_RE.test(t);
|
|
124
|
+
var FOOTNOTE_DEF_RE = /^( {0,3})\[\^([^\]\s]+)\]:/;
|
|
125
|
+
var isFootnoteDef = (line) => opts.footnotes && FOOTNOTE_DEF_RE.test(line.text);
|
|
126
|
+
var DEF_RE = /^( {0,3}):[ \t]+/;
|
|
127
|
+
function isDefListStartAt(lines, j) {
|
|
128
|
+
if (!opts.defLists) return false;
|
|
129
|
+
const line = lines[j];
|
|
130
|
+
const next = lines[j + 1];
|
|
131
|
+
if (!line || !next) return false;
|
|
132
|
+
if (isBlank(line)) return false;
|
|
133
|
+
if (startsBlock(lines, j)) return false;
|
|
134
|
+
return DEF_RE.test(next.text);
|
|
135
|
+
}
|
|
136
|
+
function startsBlock(lines, j) {
|
|
137
|
+
const line = lines[j];
|
|
138
|
+
const t = line.text;
|
|
139
|
+
return isFence(t) || isAtx(t) || isThematic(t) || isQuote(t) || detectItem(line) !== null || htmlVariant(t) !== null || isTableStartAt(lines, j) || isMathOpen(t) || isFootnoteDef(line);
|
|
140
|
+
}
|
|
141
|
+
function startsOtherBlock(line) {
|
|
142
|
+
const t = line.text;
|
|
143
|
+
return isFence(t) || isAtx(t) || isThematic(t) || isQuote(t) || detectItem(line) !== null || htmlVariant(t) !== null || isMathOpen(t) || isFootnoteDef(line);
|
|
144
|
+
}
|
|
145
|
+
function buildHeading(line) {
|
|
146
|
+
const t = line.text;
|
|
147
|
+
const m = ATX_RE.exec(t);
|
|
148
|
+
const indent = m[1].length;
|
|
149
|
+
const hashes = m[2];
|
|
150
|
+
const level = hashes.length;
|
|
151
|
+
let ce = indent + hashes.length;
|
|
152
|
+
if (t[ce] === " ") ce++;
|
|
153
|
+
const contentFrom = line.from + ce;
|
|
154
|
+
let end = t.length;
|
|
155
|
+
while (end > ce && (t[end - 1] === " " || t[end - 1] === " ")) end--;
|
|
156
|
+
let e2 = end;
|
|
157
|
+
while (e2 > ce && t[e2 - 1] === "#") e2--;
|
|
158
|
+
if (e2 < end && (e2 === ce || t[e2 - 1] === " " || t[e2 - 1] === " ")) {
|
|
159
|
+
end = e2;
|
|
160
|
+
while (end > ce && (t[end - 1] === " " || t[end - 1] === " ")) end--;
|
|
161
|
+
}
|
|
162
|
+
const children = parseInlineSegs([
|
|
163
|
+
{ from: contentFrom, to: line.from + end, text: t.slice(ce, end) }
|
|
164
|
+
]);
|
|
165
|
+
return { type: "heading", from: line.from, to: line.to, level, contentFrom, children };
|
|
166
|
+
}
|
|
167
|
+
function buildFence(lines, i) {
|
|
168
|
+
const open = lines[i];
|
|
169
|
+
const m = FENCE_RE.exec(open.text);
|
|
170
|
+
const indent = m[1].length;
|
|
171
|
+
const fenceChar = m[2][0];
|
|
172
|
+
const fenceLen = m[2].length;
|
|
173
|
+
const lang = cap(m[3].trim(), MAX_LANG);
|
|
174
|
+
const inner = [];
|
|
175
|
+
let j = i + 1;
|
|
176
|
+
let closeTo = null;
|
|
177
|
+
while (j < lines.length) {
|
|
178
|
+
const cm = /^( {0,3})(`{3,}|~{3,}) *$/.exec(lines[j].text);
|
|
179
|
+
if (cm && cm[2][0] === fenceChar && cm[2].length >= fenceLen) {
|
|
180
|
+
closeTo = lines[j].to;
|
|
181
|
+
j++;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
inner.push(lines[j]);
|
|
185
|
+
j++;
|
|
186
|
+
}
|
|
187
|
+
const value = cap(
|
|
188
|
+
inner.map((l) => leadingSpaces(l.text) >= indent ? l.text.slice(indent) : l.text.trimStart()).join("\n"),
|
|
189
|
+
MAX_VALUE
|
|
190
|
+
);
|
|
191
|
+
const to = closeTo ?? (inner.length ? inner[inner.length - 1].to : open.to);
|
|
192
|
+
return { node: { type: "codeBlock", from: open.from, to, lang, value, fenced: true }, next: j };
|
|
193
|
+
}
|
|
194
|
+
function buildMathBlock(lines, i) {
|
|
195
|
+
const open = lines[i];
|
|
196
|
+
const inner = [];
|
|
197
|
+
let j = i + 1;
|
|
198
|
+
let closeTo = null;
|
|
199
|
+
while (j < lines.length) {
|
|
200
|
+
if (MATH_FENCE_RE.test(lines[j].text)) {
|
|
201
|
+
closeTo = lines[j].to;
|
|
202
|
+
j++;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
inner.push(lines[j]);
|
|
206
|
+
j++;
|
|
207
|
+
}
|
|
208
|
+
const value = cap(inner.map((l) => l.text).join("\n"), MAX_VALUE);
|
|
209
|
+
const to = closeTo ?? (inner.length ? inner[inner.length - 1].to : open.to);
|
|
210
|
+
const node = { type: "mathBlock", from: open.from, to, value };
|
|
211
|
+
return { node, next: j };
|
|
212
|
+
}
|
|
213
|
+
function buildIndentedCode(lines, i) {
|
|
214
|
+
const from = lines[i].from;
|
|
215
|
+
const inner = [lines[i]];
|
|
216
|
+
let j = i + 1;
|
|
217
|
+
let pending = [];
|
|
218
|
+
while (j < lines.length) {
|
|
219
|
+
const line = lines[j];
|
|
220
|
+
if (isBlank(line)) {
|
|
221
|
+
pending.push(line);
|
|
222
|
+
j++;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (leadingSpaces(line.text) >= 4) {
|
|
226
|
+
inner.push(...pending, line);
|
|
227
|
+
pending = [];
|
|
228
|
+
j++;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
const last = inner[inner.length - 1];
|
|
234
|
+
const value = cap(inner.map((l) => l.text.slice(4)).join("\n"), MAX_VALUE);
|
|
235
|
+
return {
|
|
236
|
+
node: { type: "codeBlock", from, to: last.to, lang: "", value, fenced: false },
|
|
237
|
+
next: j
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function buildThematic(line) {
|
|
241
|
+
return { type: "thematicBreak", from: line.from, to: line.to };
|
|
242
|
+
}
|
|
243
|
+
function stripQuote(line) {
|
|
244
|
+
const m = /^( {0,3})>( ?)/.exec(line.text);
|
|
245
|
+
const cut = m[0].length;
|
|
246
|
+
return { from: line.from + cut, to: line.to, text: line.text.slice(cut) };
|
|
247
|
+
}
|
|
248
|
+
function buildBlockquote(lines, i) {
|
|
249
|
+
const from = lines[i].from;
|
|
250
|
+
const inner = [];
|
|
251
|
+
let j = i;
|
|
252
|
+
while (j < lines.length && isQuote(lines[j].text)) {
|
|
253
|
+
inner.push(stripQuote(lines[j]));
|
|
254
|
+
j++;
|
|
255
|
+
}
|
|
256
|
+
const to = lines[j - 1].to;
|
|
257
|
+
const children = parseBlocks(inner);
|
|
258
|
+
return { node: { type: "blockquote", from, to, children }, next: j };
|
|
259
|
+
}
|
|
260
|
+
var TASK_RE = /^\[([ xX])\](\s|$)/;
|
|
261
|
+
function collectItem(lines, idx) {
|
|
262
|
+
const line0 = lines[idx];
|
|
263
|
+
const det = detectItem(line0);
|
|
264
|
+
const contentCol = det.indent + det.markerLen + 1;
|
|
265
|
+
const localStart = det.indent + det.markerLen + (det.spacesAfter > 0 ? 1 : 0);
|
|
266
|
+
let task = null;
|
|
267
|
+
let bodyStart = localStart;
|
|
268
|
+
const afterMarker = line0.text.slice(localStart);
|
|
269
|
+
const tm = TASK_RE.exec(afterMarker);
|
|
270
|
+
if (tm) {
|
|
271
|
+
task = tm[1] === " " ? "unchecked" : "checked";
|
|
272
|
+
bodyStart = localStart + tm[0].length;
|
|
273
|
+
}
|
|
274
|
+
const contentFrom = line0.from + bodyStart;
|
|
275
|
+
const body = [
|
|
276
|
+
{ from: contentFrom, to: line0.to, text: line0.text.slice(bodyStart) }
|
|
277
|
+
];
|
|
278
|
+
let lastTo = line0.to;
|
|
279
|
+
let loose = false;
|
|
280
|
+
let pendingBlank = false;
|
|
281
|
+
let j = idx + 1;
|
|
282
|
+
while (j < lines.length) {
|
|
283
|
+
const lj = lines[j];
|
|
284
|
+
if (isBlank(lj)) {
|
|
285
|
+
pendingBlank = true;
|
|
286
|
+
j++;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const ind = leadingSpaces(lj.text);
|
|
290
|
+
if (ind >= contentCol) {
|
|
291
|
+
if (pendingBlank) {
|
|
292
|
+
loose = true;
|
|
293
|
+
body.push({ from: lj.from, to: lj.from, text: "" });
|
|
294
|
+
}
|
|
295
|
+
body.push({
|
|
296
|
+
from: lj.from + contentCol,
|
|
297
|
+
to: lj.to,
|
|
298
|
+
text: lj.text.slice(contentCol)
|
|
299
|
+
});
|
|
300
|
+
lastTo = lj.to;
|
|
301
|
+
pendingBlank = false;
|
|
302
|
+
j++;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (detectItem(lj)) break;
|
|
306
|
+
if (pendingBlank) break;
|
|
307
|
+
if (startsOtherBlock(lj)) break;
|
|
308
|
+
body.push({ from: lj.from, to: lj.to, text: lj.text });
|
|
309
|
+
lastTo = lj.to;
|
|
310
|
+
j++;
|
|
311
|
+
}
|
|
312
|
+
const children = parseBlocks(body);
|
|
313
|
+
const item = {
|
|
314
|
+
type: "listItem",
|
|
315
|
+
from: line0.from,
|
|
316
|
+
to: lastTo,
|
|
317
|
+
task,
|
|
318
|
+
contentFrom,
|
|
319
|
+
children
|
|
320
|
+
};
|
|
321
|
+
return { item, next: j, blankBefore: pendingBlank, loose };
|
|
322
|
+
}
|
|
323
|
+
function buildList(lines, start) {
|
|
324
|
+
const first = detectItem(lines[start]);
|
|
325
|
+
const ordered = first.ordered;
|
|
326
|
+
const startNum = ordered ? parseInt(first.marker, 10) || 0 : 1;
|
|
327
|
+
const items = [];
|
|
328
|
+
let loose = false;
|
|
329
|
+
let i = start;
|
|
330
|
+
while (i < lines.length) {
|
|
331
|
+
const det = detectItem(lines[i]);
|
|
332
|
+
if (!det || det.ordered !== ordered) break;
|
|
333
|
+
const r = collectItem(lines, i);
|
|
334
|
+
if (r.blankBefore) loose = true;
|
|
335
|
+
if (r.loose) loose = true;
|
|
336
|
+
items.push(r.item);
|
|
337
|
+
i = r.next;
|
|
338
|
+
}
|
|
339
|
+
const node = {
|
|
340
|
+
type: "list",
|
|
341
|
+
from: items[0].from,
|
|
342
|
+
to: items[items.length - 1].to,
|
|
343
|
+
ordered,
|
|
344
|
+
start: startNum,
|
|
345
|
+
tight: !loose,
|
|
346
|
+
items
|
|
347
|
+
};
|
|
348
|
+
return { node, next: i };
|
|
349
|
+
}
|
|
350
|
+
function parseAligns(delim) {
|
|
351
|
+
return splitCellRanges(delim).map(([a, b]) => {
|
|
352
|
+
const cell = delim.slice(a, b).trim();
|
|
353
|
+
const left = cell.startsWith(":");
|
|
354
|
+
const right = cell.endsWith(":");
|
|
355
|
+
if (left && right) return "center";
|
|
356
|
+
if (left) return "left";
|
|
357
|
+
if (right) return "right";
|
|
358
|
+
return null;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
function splitCellRanges(t) {
|
|
362
|
+
const cells = [];
|
|
363
|
+
let s = 0;
|
|
364
|
+
for (let k = 0; k < t.length; k++) {
|
|
365
|
+
if (t[k] === "|" && t[k - 1] !== "\\") {
|
|
366
|
+
cells.push([s, k]);
|
|
367
|
+
s = k + 1;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
cells.push([s, t.length]);
|
|
371
|
+
if (cells.length > 1 && t.slice(cells[0][0], cells[0][1]).trim() === "") cells.shift();
|
|
372
|
+
if (cells.length > 1 && t.slice(cells[cells.length - 1][0], cells[cells.length - 1][1]).trim() === "")
|
|
373
|
+
cells.pop();
|
|
374
|
+
return cells;
|
|
375
|
+
}
|
|
376
|
+
function splitRow(line) {
|
|
377
|
+
const t = line.text;
|
|
378
|
+
return splitCellRanges(t).map(([a, b]) => {
|
|
379
|
+
let ca = a;
|
|
380
|
+
while (ca < b && t[ca] === " ") ca++;
|
|
381
|
+
let cb = b;
|
|
382
|
+
while (cb > ca && t[cb - 1] === " ") cb--;
|
|
383
|
+
const from = line.from + ca;
|
|
384
|
+
const to = line.from + cb;
|
|
385
|
+
const children = parseInlineSegs([{ from, to, text: t.slice(ca, cb) }]);
|
|
386
|
+
return { type: "tableCell", from, to, children };
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
function buildTable(lines, i) {
|
|
390
|
+
const header = splitRow(lines[i]);
|
|
391
|
+
const align = parseAligns(lines[i + 1].text);
|
|
392
|
+
const rows = [];
|
|
393
|
+
let j = i + 2;
|
|
394
|
+
let lastTo = lines[i + 1].to;
|
|
395
|
+
while (j < lines.length) {
|
|
396
|
+
const line = lines[j];
|
|
397
|
+
if (isBlank(line) || startsOtherBlock(line)) break;
|
|
398
|
+
rows.push(splitRow(line));
|
|
399
|
+
lastTo = line.to;
|
|
400
|
+
j++;
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
node: { type: "table", from: lines[i].from, to: lastTo, align, header, rows },
|
|
404
|
+
next: j
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function buildHtml(lines, i) {
|
|
408
|
+
const variant = htmlVariant(lines[i].text);
|
|
409
|
+
const from = lines[i].from;
|
|
410
|
+
const block = [];
|
|
411
|
+
let j = i;
|
|
412
|
+
while (j < lines.length && !isBlank(lines[j])) {
|
|
413
|
+
block.push(lines[j]);
|
|
414
|
+
j++;
|
|
415
|
+
}
|
|
416
|
+
const to = block[block.length - 1].to;
|
|
417
|
+
const value = cap(block.map((l) => l.text).join("\n"), MAX_VALUE);
|
|
418
|
+
return { node: { type: "htmlBlock", from, to, value, variant }, next: j };
|
|
419
|
+
}
|
|
420
|
+
function buildFootnoteDef(lines, i) {
|
|
421
|
+
const line0 = lines[i];
|
|
422
|
+
const m = FOOTNOTE_DEF_RE.exec(line0.text);
|
|
423
|
+
const indent = m[1].length;
|
|
424
|
+
const id = m[2];
|
|
425
|
+
const markerLen = m[0].length - indent;
|
|
426
|
+
const contentCol = indent + markerLen + 1;
|
|
427
|
+
let bodyStart = indent + markerLen;
|
|
428
|
+
if (line0.text[bodyStart] === " ") bodyStart++;
|
|
429
|
+
const contentFrom = line0.from + bodyStart;
|
|
430
|
+
const body = [
|
|
431
|
+
{ from: contentFrom, to: line0.to, text: line0.text.slice(bodyStart) }
|
|
432
|
+
];
|
|
433
|
+
let lastTo = line0.to;
|
|
434
|
+
let pendingBlank = false;
|
|
435
|
+
let j = i + 1;
|
|
436
|
+
while (j < lines.length) {
|
|
437
|
+
const lj = lines[j];
|
|
438
|
+
if (isBlank(lj)) {
|
|
439
|
+
pendingBlank = true;
|
|
440
|
+
j++;
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
const ind = leadingSpaces(lj.text);
|
|
444
|
+
if (ind >= contentCol) {
|
|
445
|
+
if (pendingBlank) body.push({ from: lj.from, to: lj.from, text: "" });
|
|
446
|
+
body.push({ from: lj.from + contentCol, to: lj.to, text: lj.text.slice(contentCol) });
|
|
447
|
+
lastTo = lj.to;
|
|
448
|
+
pendingBlank = false;
|
|
449
|
+
j++;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (pendingBlank) break;
|
|
453
|
+
if (isFootnoteDef(lj)) break;
|
|
454
|
+
if (startsOtherBlock(lj)) break;
|
|
455
|
+
if (isDefListStartAt(lines, j)) break;
|
|
456
|
+
body.push({ from: lj.from, to: lj.to, text: lj.text });
|
|
457
|
+
lastTo = lj.to;
|
|
458
|
+
j++;
|
|
459
|
+
}
|
|
460
|
+
const children = parseBlocks(body);
|
|
461
|
+
const node = { type: "footnoteDef", from: line0.from, to: lastTo, id, children };
|
|
462
|
+
return { node, next: j };
|
|
463
|
+
}
|
|
464
|
+
function collectDefinition(lines, idx) {
|
|
465
|
+
const line0 = lines[idx];
|
|
466
|
+
const m = DEF_RE.exec(line0.text);
|
|
467
|
+
const contentCol = m[0].length;
|
|
468
|
+
const body = [
|
|
469
|
+
{ from: line0.from + contentCol, to: line0.to, text: line0.text.slice(contentCol) }
|
|
470
|
+
];
|
|
471
|
+
let lastTo = line0.to;
|
|
472
|
+
let pendingBlank = false;
|
|
473
|
+
let j = idx + 1;
|
|
474
|
+
while (j < lines.length) {
|
|
475
|
+
const lj = lines[j];
|
|
476
|
+
if (isBlank(lj)) {
|
|
477
|
+
pendingBlank = true;
|
|
478
|
+
j++;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (DEF_RE.test(lj.text)) break;
|
|
482
|
+
const ind = leadingSpaces(lj.text);
|
|
483
|
+
if (ind >= contentCol) {
|
|
484
|
+
if (pendingBlank) body.push({ from: lj.from, to: lj.from, text: "" });
|
|
485
|
+
body.push({ from: lj.from + contentCol, to: lj.to, text: lj.text.slice(contentCol) });
|
|
486
|
+
lastTo = lj.to;
|
|
487
|
+
pendingBlank = false;
|
|
488
|
+
j++;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (pendingBlank) break;
|
|
492
|
+
if (startsOtherBlock(lj)) break;
|
|
493
|
+
if (isFootnoteDef(lj)) break;
|
|
494
|
+
if (isDefListStartAt(lines, j)) break;
|
|
495
|
+
body.push({ from: lj.from, to: lj.to, text: lj.text });
|
|
496
|
+
lastTo = lj.to;
|
|
497
|
+
j++;
|
|
498
|
+
}
|
|
499
|
+
return { blocks: parseBlocks(body), to: lastTo, next: j };
|
|
500
|
+
}
|
|
501
|
+
function collectDefItem(lines, idx) {
|
|
502
|
+
const termLine = lines[idx];
|
|
503
|
+
const term = parseInlineSegs([
|
|
504
|
+
{ from: termLine.from, to: termLine.to, text: termLine.text }
|
|
505
|
+
]);
|
|
506
|
+
const definitions = [];
|
|
507
|
+
let lastTo = termLine.to;
|
|
508
|
+
let j = idx + 1;
|
|
509
|
+
while (j < lines.length && DEF_RE.test(lines[j].text)) {
|
|
510
|
+
const d = collectDefinition(lines, j);
|
|
511
|
+
definitions.push(d.blocks);
|
|
512
|
+
lastTo = d.to;
|
|
513
|
+
j = d.next;
|
|
514
|
+
}
|
|
515
|
+
const item = { type: "defItem", from: termLine.from, to: lastTo, term, definitions };
|
|
516
|
+
return { item, next: j };
|
|
517
|
+
}
|
|
518
|
+
function buildDefList(lines, start) {
|
|
519
|
+
const items = [];
|
|
520
|
+
let i = start;
|
|
521
|
+
while (i < lines.length && isDefListStartAt(lines, i)) {
|
|
522
|
+
const r = collectDefItem(lines, i);
|
|
523
|
+
items.push(r.item);
|
|
524
|
+
i = r.next;
|
|
525
|
+
}
|
|
526
|
+
const node = {
|
|
527
|
+
type: "defList",
|
|
528
|
+
from: items[0].from,
|
|
529
|
+
to: items[items.length - 1].to,
|
|
530
|
+
items
|
|
531
|
+
};
|
|
532
|
+
return { node, next: i };
|
|
533
|
+
}
|
|
534
|
+
function buildParagraph(lines, i) {
|
|
535
|
+
const para = [lines[i]];
|
|
536
|
+
let j = i + 1;
|
|
537
|
+
while (j < lines.length) {
|
|
538
|
+
if (isBlank(lines[j])) break;
|
|
539
|
+
if (startsBlock(lines, j)) break;
|
|
540
|
+
para.push(lines[j]);
|
|
541
|
+
j++;
|
|
542
|
+
}
|
|
543
|
+
const from = para[0].from;
|
|
544
|
+
const to = para[para.length - 1].to;
|
|
545
|
+
const children = parseInlineSegs(para.map((l) => ({ from: l.from, to: l.to, text: l.text })));
|
|
546
|
+
return { node: { type: "paragraph", from, to, children }, next: j };
|
|
547
|
+
}
|
|
548
|
+
function parseBlocks(lines) {
|
|
549
|
+
const out = [];
|
|
550
|
+
let i = 0;
|
|
551
|
+
while (i < lines.length) {
|
|
552
|
+
const line = lines[i];
|
|
553
|
+
if (isBlank(line)) {
|
|
554
|
+
i++;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const t = line.text;
|
|
558
|
+
if (isFence(t)) {
|
|
559
|
+
const b = buildFence(lines, i);
|
|
560
|
+
out.push(b.node);
|
|
561
|
+
i = b.next;
|
|
562
|
+
} else if (isMathOpen(t)) {
|
|
563
|
+
const b = buildMathBlock(lines, i);
|
|
564
|
+
out.push(b.node);
|
|
565
|
+
i = b.next;
|
|
566
|
+
} else if (leadingSpaces(t) >= 4) {
|
|
567
|
+
const b = buildIndentedCode(lines, i);
|
|
568
|
+
out.push(b.node);
|
|
569
|
+
i = b.next;
|
|
570
|
+
} else if (isAtx(t)) {
|
|
571
|
+
out.push(buildHeading(line));
|
|
572
|
+
i++;
|
|
573
|
+
} else if (isThematic(t)) {
|
|
574
|
+
out.push(buildThematic(line));
|
|
575
|
+
i++;
|
|
576
|
+
} else if (isQuote(t)) {
|
|
577
|
+
const b = buildBlockquote(lines, i);
|
|
578
|
+
out.push(b.node);
|
|
579
|
+
i = b.next;
|
|
580
|
+
} else if (detectItem(line)) {
|
|
581
|
+
const b = buildList(lines, i);
|
|
582
|
+
out.push(b.node);
|
|
583
|
+
i = b.next;
|
|
584
|
+
} else if (isDefListStartAt(lines, i)) {
|
|
585
|
+
const b = buildDefList(lines, i);
|
|
586
|
+
out.push(b.node);
|
|
587
|
+
i = b.next;
|
|
588
|
+
} else if (isTableStartAt(lines, i)) {
|
|
589
|
+
const b = buildTable(lines, i);
|
|
590
|
+
out.push(b.node);
|
|
591
|
+
i = b.next;
|
|
592
|
+
} else if (htmlVariant(t)) {
|
|
593
|
+
const b = buildHtml(lines, i);
|
|
594
|
+
out.push(b.node);
|
|
595
|
+
i = b.next;
|
|
596
|
+
} else if (isFootnoteDef(line)) {
|
|
597
|
+
const b = buildFootnoteDef(lines, i);
|
|
598
|
+
out.push(b.node);
|
|
599
|
+
i = b.next;
|
|
600
|
+
} else {
|
|
601
|
+
const b = buildParagraph(lines, i);
|
|
602
|
+
out.push(b.node);
|
|
603
|
+
i = b.next;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return out;
|
|
607
|
+
}
|
|
608
|
+
var isWs = (ch) => ch === void 0 || ch === " " || ch === "\n" || ch === " " || ch === "\r";
|
|
609
|
+
var isAlnum = (ch) => ch !== void 0 && /[0-9A-Za-z]/.test(ch);
|
|
610
|
+
function parseInlineSegs(segs) {
|
|
611
|
+
if (segs.length === 0) return [];
|
|
612
|
+
let buf = "";
|
|
613
|
+
const map = [];
|
|
614
|
+
for (let s = 0; s < segs.length; s++) {
|
|
615
|
+
const seg = segs[s];
|
|
616
|
+
if (s > 0) {
|
|
617
|
+
map.push(segs[s - 1].to);
|
|
618
|
+
buf += "\n";
|
|
619
|
+
}
|
|
620
|
+
for (let k = 0; k < seg.text.length; k++) {
|
|
621
|
+
map.push(seg.from + k);
|
|
622
|
+
buf += seg.text[k];
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
map.push(segs[segs.length - 1].to);
|
|
626
|
+
const abs = (k) => k < map.length ? map[k] : segs[segs.length - 1].to;
|
|
627
|
+
const text = (from, to, value) => ({
|
|
628
|
+
type: "text",
|
|
629
|
+
from,
|
|
630
|
+
to,
|
|
631
|
+
value: cap(value, MAX_VALUE)
|
|
632
|
+
});
|
|
633
|
+
function tryCode(i, end) {
|
|
634
|
+
let n = 1;
|
|
635
|
+
while (i + n < end && buf[i + n] === "`") n++;
|
|
636
|
+
let k = i + n;
|
|
637
|
+
while (k < end) {
|
|
638
|
+
if (buf[k] === "`") {
|
|
639
|
+
let m = 1;
|
|
640
|
+
while (k + m < end && buf[k + m] === "`") m++;
|
|
641
|
+
if (m === n) {
|
|
642
|
+
let val = buf.slice(i + n, k);
|
|
643
|
+
if (val.length > 2 && val[0] === " " && val[val.length - 1] === " " && val.trim() !== "")
|
|
644
|
+
val = val.slice(1, -1);
|
|
645
|
+
const node = {
|
|
646
|
+
type: "inlineCode",
|
|
647
|
+
from: abs(i),
|
|
648
|
+
to: abs(k + n),
|
|
649
|
+
value: cap(val, MAX_VALUE),
|
|
650
|
+
ticks: n
|
|
651
|
+
};
|
|
652
|
+
return { node, endIdx: k + n };
|
|
653
|
+
}
|
|
654
|
+
k += m;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
k++;
|
|
658
|
+
}
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
function tryMath(i, end) {
|
|
662
|
+
let n = 1;
|
|
663
|
+
while (i + n < end && buf[i + n] === "$" && n < 2) n++;
|
|
664
|
+
const after = buf[i + n];
|
|
665
|
+
if (after === void 0 || after === " " || after === " " || after === "\n") return null;
|
|
666
|
+
let k = i + n;
|
|
667
|
+
while (k < end) {
|
|
668
|
+
const ch = buf[k];
|
|
669
|
+
if (ch === "\\") {
|
|
670
|
+
k += 2;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
if (ch === "\n") return null;
|
|
674
|
+
if (ch === "$") {
|
|
675
|
+
let m = 1;
|
|
676
|
+
while (k + m < end && buf[k + m] === "$" && m < 2) m++;
|
|
677
|
+
if (m === n && k > i + n) {
|
|
678
|
+
const prev = buf[k - 1];
|
|
679
|
+
if (prev !== " " && prev !== " " && prev !== "\n") {
|
|
680
|
+
const node = {
|
|
681
|
+
type: "math",
|
|
682
|
+
from: abs(i),
|
|
683
|
+
to: abs(k + n),
|
|
684
|
+
value: cap(buf.slice(i + n, k), MAX_VALUE),
|
|
685
|
+
display: n === 2
|
|
686
|
+
};
|
|
687
|
+
return { node, endIdx: k + n };
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
k += m;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
k++;
|
|
694
|
+
}
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
function tryFootnoteRef(i, end) {
|
|
698
|
+
const idStart = i + 2;
|
|
699
|
+
const stop = Math.min(end, idStart + MAX_FOOTNOTE_ID);
|
|
700
|
+
let k = idStart;
|
|
701
|
+
while (k < stop) {
|
|
702
|
+
const ch = buf[k];
|
|
703
|
+
if (ch === "]") break;
|
|
704
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "[" || ch === "^") return null;
|
|
705
|
+
k++;
|
|
706
|
+
}
|
|
707
|
+
if (k >= stop || buf[k] !== "]" || k === idStart) return null;
|
|
708
|
+
const node = {
|
|
709
|
+
type: "footnoteRef",
|
|
710
|
+
from: abs(i),
|
|
711
|
+
to: abs(k + 1),
|
|
712
|
+
id: buf.slice(idStart, k)
|
|
713
|
+
};
|
|
714
|
+
return { node, endIdx: k + 1 };
|
|
715
|
+
}
|
|
716
|
+
function tryAutolink(i, end) {
|
|
717
|
+
let k = i + 1;
|
|
718
|
+
while (k < end && buf[k] !== ">" && buf[k] !== "<" && buf[k] !== " " && buf[k] !== "\n") k++;
|
|
719
|
+
if (k >= end || buf[k] !== ">") return null;
|
|
720
|
+
const content = buf.slice(i + 1, k);
|
|
721
|
+
const isUri = /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/.test(content);
|
|
722
|
+
const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(content);
|
|
723
|
+
if (!isUri && !isEmail) return null;
|
|
724
|
+
const node = {
|
|
725
|
+
type: "autolink",
|
|
726
|
+
from: abs(i),
|
|
727
|
+
to: abs(k + 1),
|
|
728
|
+
url: cap(content, MAX_URL)
|
|
729
|
+
};
|
|
730
|
+
return { node, endIdx: k + 1 };
|
|
731
|
+
}
|
|
732
|
+
function matchLabel(open, end) {
|
|
733
|
+
const stop = Math.min(end, open + 4096);
|
|
734
|
+
let depth = 0;
|
|
735
|
+
for (let k = open; k < stop; k++) {
|
|
736
|
+
const ch = buf[k];
|
|
737
|
+
if (ch === "\\") {
|
|
738
|
+
k++;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (ch === "[") depth++;
|
|
742
|
+
else if (ch === "]") {
|
|
743
|
+
depth--;
|
|
744
|
+
if (depth === 0) return k;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return -1;
|
|
748
|
+
}
|
|
749
|
+
function tryLink(i, end, isImage) {
|
|
750
|
+
const br = i + (isImage ? 1 : 0);
|
|
751
|
+
if (buf[br] !== "[") return null;
|
|
752
|
+
const close = matchLabel(br, end);
|
|
753
|
+
if (close < 0) return null;
|
|
754
|
+
let k = close + 1;
|
|
755
|
+
if (buf[k] !== "(") return null;
|
|
756
|
+
k++;
|
|
757
|
+
while (k < end && (buf[k] === " " || buf[k] === "\n")) k++;
|
|
758
|
+
let url = "";
|
|
759
|
+
if (buf[k] === "<") {
|
|
760
|
+
k++;
|
|
761
|
+
const s = k;
|
|
762
|
+
while (k < end && buf[k] !== ">" && buf[k] !== "\n") k++;
|
|
763
|
+
if (buf[k] !== ">") return null;
|
|
764
|
+
url = buf.slice(s, k);
|
|
765
|
+
k++;
|
|
766
|
+
} else {
|
|
767
|
+
const s = k;
|
|
768
|
+
let depth = 0;
|
|
769
|
+
while (k < end) {
|
|
770
|
+
const ch = buf[k];
|
|
771
|
+
if (ch === " " || ch === "\n") break;
|
|
772
|
+
if (ch === "(") depth++;
|
|
773
|
+
else if (ch === ")") {
|
|
774
|
+
if (depth === 0) break;
|
|
775
|
+
depth--;
|
|
776
|
+
}
|
|
777
|
+
k++;
|
|
778
|
+
}
|
|
779
|
+
url = buf.slice(s, k);
|
|
780
|
+
}
|
|
781
|
+
let title;
|
|
782
|
+
while (k < end && (buf[k] === " " || buf[k] === "\n")) k++;
|
|
783
|
+
const q = buf[k];
|
|
784
|
+
if (q === '"' || q === "'" || q === "(") {
|
|
785
|
+
const closeCh = q === "(" ? ")" : q;
|
|
786
|
+
k++;
|
|
787
|
+
const s = k;
|
|
788
|
+
while (k < end && buf[k] !== closeCh) k++;
|
|
789
|
+
if (buf[k] !== closeCh) return null;
|
|
790
|
+
title = buf.slice(s, k);
|
|
791
|
+
k++;
|
|
792
|
+
while (k < end && (buf[k] === " " || buf[k] === "\n")) k++;
|
|
793
|
+
}
|
|
794
|
+
if (buf[k] !== ")") return null;
|
|
795
|
+
const endIdx = k + 1;
|
|
796
|
+
const from = abs(i);
|
|
797
|
+
const to = abs(endIdx);
|
|
798
|
+
if (isImage) {
|
|
799
|
+
const node2 = {
|
|
800
|
+
type: "image",
|
|
801
|
+
from,
|
|
802
|
+
to,
|
|
803
|
+
url: cap(url, MAX_URL),
|
|
804
|
+
alt: cap(buf.slice(br + 1, close), MAX_ALT)
|
|
805
|
+
};
|
|
806
|
+
if (title !== void 0) node2.title = cap(title, MAX_TITLE);
|
|
807
|
+
return { node: node2, endIdx };
|
|
808
|
+
}
|
|
809
|
+
const node = {
|
|
810
|
+
type: "link",
|
|
811
|
+
from,
|
|
812
|
+
to,
|
|
813
|
+
url: cap(url, MAX_URL),
|
|
814
|
+
children: parseInlineRange(br + 1, close)
|
|
815
|
+
};
|
|
816
|
+
if (title !== void 0) node.title = cap(title, MAX_TITLE);
|
|
817
|
+
return { node, endIdx };
|
|
818
|
+
}
|
|
819
|
+
function tryEmph(i, end) {
|
|
820
|
+
const c = buf[i];
|
|
821
|
+
let n = 1;
|
|
822
|
+
while (i + n < end && buf[i + n] === c) n++;
|
|
823
|
+
const isStrike = c === "~";
|
|
824
|
+
if (isStrike && n < 2) return null;
|
|
825
|
+
const strength = isStrike ? 2 : n >= 2 ? 2 : 1;
|
|
826
|
+
if (i + strength > end) return null;
|
|
827
|
+
const after = buf[i + strength];
|
|
828
|
+
const before = buf[i - 1];
|
|
829
|
+
if (isWs(after)) return null;
|
|
830
|
+
if (c === "_" && isAlnum(before)) return null;
|
|
831
|
+
let k = i + strength;
|
|
832
|
+
while (k < end) {
|
|
833
|
+
if (buf[k] === c) {
|
|
834
|
+
let m = 1;
|
|
835
|
+
while (k + m < end && buf[k + m] === c) m++;
|
|
836
|
+
if (m >= strength) {
|
|
837
|
+
const prev = buf[k - 1];
|
|
838
|
+
const aft = buf[k + strength];
|
|
839
|
+
if (!isWs(prev) && (c !== "_" || !isAlnum(aft))) {
|
|
840
|
+
const children = parseInlineRange(i + strength, k);
|
|
841
|
+
const from = abs(i);
|
|
842
|
+
const to = abs(k + strength);
|
|
843
|
+
let node;
|
|
844
|
+
if (isStrike) {
|
|
845
|
+
node = { type: "strikethrough", from, to, children };
|
|
846
|
+
} else if (strength === 2) {
|
|
847
|
+
node = { type: "strong", from, to, marker: c.repeat(2), children };
|
|
848
|
+
} else {
|
|
849
|
+
node = { type: "emphasis", from, to, marker: c, children };
|
|
850
|
+
}
|
|
851
|
+
return { node, endIdx: k + strength };
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
k += m;
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
k++;
|
|
858
|
+
}
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
function parseInlineRange(start, end) {
|
|
862
|
+
const out = [];
|
|
863
|
+
let i = start;
|
|
864
|
+
let textStart = start;
|
|
865
|
+
const flush = (upto) => {
|
|
866
|
+
if (upto > textStart) out.push(text(abs(textStart), abs(upto), buf.slice(textStart, upto)));
|
|
867
|
+
};
|
|
868
|
+
const commit = (hit) => {
|
|
869
|
+
flush(i);
|
|
870
|
+
out.push(hit.node);
|
|
871
|
+
i = hit.endIdx;
|
|
872
|
+
textStart = i;
|
|
873
|
+
};
|
|
874
|
+
while (i < end) {
|
|
875
|
+
const c = buf[i];
|
|
876
|
+
if (c === "\\") {
|
|
877
|
+
if (buf[i + 1] === "\n") {
|
|
878
|
+
flush(i);
|
|
879
|
+
out.push({ type: "break", from: abs(i), to: abs(i + 2), hard: true });
|
|
880
|
+
i += 2;
|
|
881
|
+
textStart = i;
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
i += 2;
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
if (c === "\n") {
|
|
888
|
+
let sp = 0;
|
|
889
|
+
let p = i - 1;
|
|
890
|
+
while (p >= textStart && buf[p] === " ") {
|
|
891
|
+
sp++;
|
|
892
|
+
p--;
|
|
893
|
+
}
|
|
894
|
+
const hard = sp >= 2;
|
|
895
|
+
flush(hard ? i - sp : i);
|
|
896
|
+
out.push({
|
|
897
|
+
type: "break",
|
|
898
|
+
from: abs(hard ? i - sp : i),
|
|
899
|
+
to: abs(i + 1),
|
|
900
|
+
hard
|
|
901
|
+
});
|
|
902
|
+
i++;
|
|
903
|
+
textStart = i;
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (c === "`") {
|
|
907
|
+
const r = tryCode(i, end);
|
|
908
|
+
if (r) {
|
|
909
|
+
commit(r);
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
i++;
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
if (c === "$" && opts.math) {
|
|
916
|
+
const r = tryMath(i, end);
|
|
917
|
+
if (r) {
|
|
918
|
+
commit(r);
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
921
|
+
i++;
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
if (c === "<") {
|
|
925
|
+
const r = tryAutolink(i, end);
|
|
926
|
+
if (r) {
|
|
927
|
+
commit(r);
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
i++;
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
if (c === "!" && buf[i + 1] === "[") {
|
|
934
|
+
const r = tryLink(i, end, true);
|
|
935
|
+
if (r) {
|
|
936
|
+
commit(r);
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
i++;
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
if (c === "[") {
|
|
943
|
+
if (opts.footnotes && buf[i + 1] === "^") {
|
|
944
|
+
const rf = tryFootnoteRef(i, end);
|
|
945
|
+
if (rf) {
|
|
946
|
+
commit(rf);
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
const r = tryLink(i, end, false);
|
|
951
|
+
if (r) {
|
|
952
|
+
commit(r);
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
i++;
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
if (c === "*" || c === "_" || c === "~") {
|
|
959
|
+
const r = tryEmph(i, end);
|
|
960
|
+
if (r) {
|
|
961
|
+
commit(r);
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
i++;
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
i++;
|
|
968
|
+
}
|
|
969
|
+
flush(end);
|
|
970
|
+
return out;
|
|
971
|
+
}
|
|
972
|
+
return parseInlineRange(0, buf.length);
|
|
973
|
+
}
|
|
974
|
+
function parse(src, options) {
|
|
975
|
+
opts = {
|
|
976
|
+
math: options?.math === true,
|
|
977
|
+
footnotes: options?.footnotes === true,
|
|
978
|
+
defLists: options?.defLists === true
|
|
979
|
+
};
|
|
980
|
+
const lines = splitLines(src);
|
|
981
|
+
const children = parseBlocks(lines);
|
|
982
|
+
return { type: "document", from: 0, to: src.length, children };
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/core/render.ts
|
|
986
|
+
var MAX_URL2 = 8192;
|
|
987
|
+
var SAFE_SCHEMES = /^(https?:|mailto:)/;
|
|
988
|
+
function newFnCtx() {
|
|
989
|
+
return { nums: /* @__PURE__ */ new Map() };
|
|
990
|
+
}
|
|
991
|
+
function fnNumber(ctx, slug) {
|
|
992
|
+
const existing = ctx.nums.get(slug);
|
|
993
|
+
if (existing !== void 0) return existing;
|
|
994
|
+
const n = ctx.nums.size + 1;
|
|
995
|
+
ctx.nums.set(slug, n);
|
|
996
|
+
return n;
|
|
997
|
+
}
|
|
998
|
+
function escapeHtml(value) {
|
|
999
|
+
let out = "";
|
|
1000
|
+
for (let i = 0; i < value.length; i++) {
|
|
1001
|
+
const ch = value.charCodeAt(i);
|
|
1002
|
+
switch (ch) {
|
|
1003
|
+
case 38:
|
|
1004
|
+
out += "&";
|
|
1005
|
+
break;
|
|
1006
|
+
case 60:
|
|
1007
|
+
out += "<";
|
|
1008
|
+
break;
|
|
1009
|
+
case 62:
|
|
1010
|
+
out += ">";
|
|
1011
|
+
break;
|
|
1012
|
+
case 34:
|
|
1013
|
+
out += """;
|
|
1014
|
+
break;
|
|
1015
|
+
default:
|
|
1016
|
+
out += value[i];
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return out;
|
|
1020
|
+
}
|
|
1021
|
+
function slugFootnoteId(id) {
|
|
1022
|
+
const slug = id.replace(/[^A-Za-z0-9_-]+/g, "-");
|
|
1023
|
+
return slug === "" ? "_" : slug;
|
|
1024
|
+
}
|
|
1025
|
+
function safeUrl(url) {
|
|
1026
|
+
if (typeof url !== "string" || url.length === 0) return "#";
|
|
1027
|
+
const trimmed = url.trim().slice(0, MAX_URL2);
|
|
1028
|
+
if (trimmed === "") return "#";
|
|
1029
|
+
const probe = trimmed.replace(/[\u0000-\u0020\u007f]/g, "").toLowerCase();
|
|
1030
|
+
if (probe === "") return "#";
|
|
1031
|
+
if (SAFE_SCHEMES.test(probe)) return trimmed;
|
|
1032
|
+
if (/^[a-z][a-z0-9+.-]*:/.test(probe)) return "#";
|
|
1033
|
+
return trimmed;
|
|
1034
|
+
}
|
|
1035
|
+
function attrUrl(url) {
|
|
1036
|
+
return escapeHtml(safeUrl(url));
|
|
1037
|
+
}
|
|
1038
|
+
function renderInline(nodes, options) {
|
|
1039
|
+
return inlineRun(nodes, options, newFnCtx());
|
|
1040
|
+
}
|
|
1041
|
+
function inlineRun(nodes, options, ctx) {
|
|
1042
|
+
let out = "";
|
|
1043
|
+
for (const node of nodes) out += renderInlineNode(node, options, ctx);
|
|
1044
|
+
return out;
|
|
1045
|
+
}
|
|
1046
|
+
function renderInlineNode(node, options, ctx) {
|
|
1047
|
+
switch (node.type) {
|
|
1048
|
+
case "text":
|
|
1049
|
+
return escapeHtml(node.value);
|
|
1050
|
+
case "strong":
|
|
1051
|
+
return `<strong>${inlineRun(node.children, options, ctx)}</strong>`;
|
|
1052
|
+
case "emphasis":
|
|
1053
|
+
return `<em>${inlineRun(node.children, options, ctx)}</em>`;
|
|
1054
|
+
case "strikethrough":
|
|
1055
|
+
return `<del>${inlineRun(node.children, options, ctx)}</del>`;
|
|
1056
|
+
case "inlineCode":
|
|
1057
|
+
return `<code>${escapeHtml(node.value)}</code>`;
|
|
1058
|
+
case "link": {
|
|
1059
|
+
const title = node.title ? ` title="${escapeHtml(node.title)}"` : "";
|
|
1060
|
+
return `<a href="${attrUrl(node.url)}"${title}>${inlineRun(node.children, options, ctx)}</a>`;
|
|
1061
|
+
}
|
|
1062
|
+
case "image": {
|
|
1063
|
+
const title = node.title ? ` title="${escapeHtml(node.title)}"` : "";
|
|
1064
|
+
return `<img src="${attrUrl(node.url)}" alt="${escapeHtml(node.alt)}"${title}>`;
|
|
1065
|
+
}
|
|
1066
|
+
case "autolink":
|
|
1067
|
+
return `<a href="${attrUrl(node.url)}">${escapeHtml(node.url)}</a>`;
|
|
1068
|
+
case "break":
|
|
1069
|
+
return node.hard ? "<br>\n" : "\n";
|
|
1070
|
+
case "math":
|
|
1071
|
+
return options?.math ? options.math(node.value, node.display) : `<span class="tw-math-src">${escapeHtml(node.value)}</span>`;
|
|
1072
|
+
case "footnoteRef": {
|
|
1073
|
+
const slug = slugFootnoteId(node.id);
|
|
1074
|
+
const n = fnNumber(ctx, slug);
|
|
1075
|
+
const safe = escapeHtml(slug);
|
|
1076
|
+
return `<sup class="tw-fnref" id="fnref-${safe}"><a href="#fn-${safe}">[${n}]</a></sup>`;
|
|
1077
|
+
}
|
|
1078
|
+
default: {
|
|
1079
|
+
const _never = node;
|
|
1080
|
+
return _never;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function renderBlockNode(node, options, ctx) {
|
|
1085
|
+
switch (node.type) {
|
|
1086
|
+
case "heading": {
|
|
1087
|
+
const l = node.level;
|
|
1088
|
+
return `<h${l}>${inlineRun(node.children, options, ctx)}</h${l}>`;
|
|
1089
|
+
}
|
|
1090
|
+
case "paragraph":
|
|
1091
|
+
return `<p>${inlineRun(node.children, options, ctx)}</p>`;
|
|
1092
|
+
case "blockquote":
|
|
1093
|
+
return `<blockquote>${renderBlocks(node.children, options, ctx)}</blockquote>`;
|
|
1094
|
+
case "list":
|
|
1095
|
+
return renderList(node.ordered, node.start, node.tight, node.items, options, ctx);
|
|
1096
|
+
case "codeBlock": {
|
|
1097
|
+
const cls = node.lang ? ` class="language-${escapeHtml(node.lang)}"` : "";
|
|
1098
|
+
const body = escapeHtml(node.value);
|
|
1099
|
+
return `<pre><code${cls}>${body}</code></pre>`;
|
|
1100
|
+
}
|
|
1101
|
+
case "thematicBreak":
|
|
1102
|
+
return "<hr>";
|
|
1103
|
+
case "table":
|
|
1104
|
+
return renderTable(node, options, ctx);
|
|
1105
|
+
case "htmlBlock":
|
|
1106
|
+
return `<pre>${escapeHtml(node.value)}</pre>`;
|
|
1107
|
+
case "mathBlock":
|
|
1108
|
+
return options?.math ? options.math(node.value, true) : `<div class="tw-math-src">${escapeHtml(node.value)}</div>`;
|
|
1109
|
+
case "footnoteDef":
|
|
1110
|
+
return `<div class="tw-footnote-def">${renderBlocks(node.children, options, ctx)}</div>`;
|
|
1111
|
+
case "defList": {
|
|
1112
|
+
let out = "";
|
|
1113
|
+
for (const item of node.items) {
|
|
1114
|
+
out += `<dt>${inlineRun(item.term, options, ctx)}</dt>`;
|
|
1115
|
+
for (const def of item.definitions) {
|
|
1116
|
+
out += `<dd>${renderBlocks(def, options, ctx)}</dd>`;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return `<dl>${out}</dl>`;
|
|
1120
|
+
}
|
|
1121
|
+
default: {
|
|
1122
|
+
const _never = node;
|
|
1123
|
+
return _never;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
function renderBlocks(blocks, options, ctx) {
|
|
1128
|
+
let out = "";
|
|
1129
|
+
for (const b of blocks) {
|
|
1130
|
+
if (b.type === "footnoteDef") continue;
|
|
1131
|
+
out += renderBlockNode(b, options, ctx);
|
|
1132
|
+
}
|
|
1133
|
+
return out;
|
|
1134
|
+
}
|
|
1135
|
+
function renderList(ordered, start, tight, items, options, ctx) {
|
|
1136
|
+
const tag = ordered ? "ol" : "ul";
|
|
1137
|
+
const startAttr = ordered && Number.isFinite(start) && start !== 1 ? ` start="${escapeHtml(String(Math.trunc(start)))}"` : "";
|
|
1138
|
+
let body = "";
|
|
1139
|
+
for (const item of items) body += renderListItem(item, tight, options, ctx);
|
|
1140
|
+
return `<${tag}${startAttr}>${body}</${tag}>`;
|
|
1141
|
+
}
|
|
1142
|
+
function renderListItem(item, tight, options, ctx) {
|
|
1143
|
+
let checkbox = "";
|
|
1144
|
+
if (item.task !== null) {
|
|
1145
|
+
const checked = item.task === "checked" ? " checked" : "";
|
|
1146
|
+
checkbox = `<input type="checkbox" disabled${checked}> `;
|
|
1147
|
+
}
|
|
1148
|
+
return `<li>${checkbox}${renderItemChildren(item.children, tight, options, ctx)}</li>`;
|
|
1149
|
+
}
|
|
1150
|
+
function renderItemChildren(children, tight, options, ctx) {
|
|
1151
|
+
if (tight) {
|
|
1152
|
+
let out = "";
|
|
1153
|
+
for (const child of children) {
|
|
1154
|
+
out += child.type === "paragraph" ? inlineRun(child.children, options, ctx) : renderBlockNode(child, options, ctx);
|
|
1155
|
+
}
|
|
1156
|
+
return out;
|
|
1157
|
+
}
|
|
1158
|
+
return renderBlocks(children, options, ctx);
|
|
1159
|
+
}
|
|
1160
|
+
function renderTable(node, options, ctx) {
|
|
1161
|
+
const align = node.align;
|
|
1162
|
+
const cellStyle = (col) => {
|
|
1163
|
+
const a = align[col];
|
|
1164
|
+
return a ? ` style="text-align:${a}"` : "";
|
|
1165
|
+
};
|
|
1166
|
+
const headCells = renderRow(node.header, "th", cellStyle, options, ctx);
|
|
1167
|
+
const head = `<thead><tr>${headCells}</tr></thead>`;
|
|
1168
|
+
let bodyRows = "";
|
|
1169
|
+
for (const row of node.rows) {
|
|
1170
|
+
bodyRows += `<tr>${renderRow(row, "td", cellStyle, options, ctx)}</tr>`;
|
|
1171
|
+
}
|
|
1172
|
+
const body = `<tbody>${bodyRows}</tbody>`;
|
|
1173
|
+
return `<table>${head}${body}</table>`;
|
|
1174
|
+
}
|
|
1175
|
+
function renderRow(cells, tag, cellStyle, options, ctx) {
|
|
1176
|
+
let out = "";
|
|
1177
|
+
for (let i = 0; i < cells.length; i++) {
|
|
1178
|
+
const cell = cells[i];
|
|
1179
|
+
out += `<${tag}${cellStyle(i)}>${inlineRun(cell.children, options, ctx)}</${tag}>`;
|
|
1180
|
+
}
|
|
1181
|
+
return out;
|
|
1182
|
+
}
|
|
1183
|
+
function collectFootnoteDefs(doc) {
|
|
1184
|
+
const defs = [];
|
|
1185
|
+
walk(doc, (n) => {
|
|
1186
|
+
if (n.type === "footnoteDef") {
|
|
1187
|
+
defs.push(n);
|
|
1188
|
+
return false;
|
|
1189
|
+
}
|
|
1190
|
+
return true;
|
|
1191
|
+
});
|
|
1192
|
+
return defs;
|
|
1193
|
+
}
|
|
1194
|
+
function renderToHtml(doc, options) {
|
|
1195
|
+
const ctx = newFnCtx();
|
|
1196
|
+
let out = renderBlocks(doc.children, options, ctx);
|
|
1197
|
+
const defs = collectFootnoteDefs(doc);
|
|
1198
|
+
if (defs.length > 0) {
|
|
1199
|
+
let items = "";
|
|
1200
|
+
for (const def of defs) {
|
|
1201
|
+
const safe = escapeHtml(slugFootnoteId(def.id));
|
|
1202
|
+
items += `<li id="fn-${safe}">` + renderBlocks(def.children, options, ctx) + ` <a href="#fnref-${safe}" class="tw-fn-back">\u21A9</a></li>`;
|
|
1203
|
+
}
|
|
1204
|
+
out += `<section class="tw-footnotes"><ol>${items}</ol></section>`;
|
|
1205
|
+
}
|
|
1206
|
+
return out;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// src/streaming/anticipate.ts
|
|
1210
|
+
function normalizeOpt(o) {
|
|
1211
|
+
const all = (v) => ({
|
|
1212
|
+
emphasis: v,
|
|
1213
|
+
strong: v,
|
|
1214
|
+
code: v,
|
|
1215
|
+
strikethrough: v,
|
|
1216
|
+
links: v,
|
|
1217
|
+
headings: v,
|
|
1218
|
+
fences: v,
|
|
1219
|
+
listItems: v,
|
|
1220
|
+
tables: v,
|
|
1221
|
+
jsx: v
|
|
1222
|
+
});
|
|
1223
|
+
if (o === true || o === void 0) return all(true);
|
|
1224
|
+
if (o === false) return all(false);
|
|
1225
|
+
return { ...all(true), ...o };
|
|
1226
|
+
}
|
|
1227
|
+
function esc(s) {
|
|
1228
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1229
|
+
}
|
|
1230
|
+
function inlineHtml(text) {
|
|
1231
|
+
const doc = parse(text);
|
|
1232
|
+
const first = doc.children[0];
|
|
1233
|
+
if (first && (first.type === "paragraph" || first.type === "heading")) {
|
|
1234
|
+
return renderInline(first.children);
|
|
1235
|
+
}
|
|
1236
|
+
return esc(text);
|
|
1237
|
+
}
|
|
1238
|
+
var CARET = '<span class="tw-caret" aria-hidden="true"></span>';
|
|
1239
|
+
function indexesOf(line, sub) {
|
|
1240
|
+
const out = [];
|
|
1241
|
+
let i = line.indexOf(sub);
|
|
1242
|
+
while (i >= 0) {
|
|
1243
|
+
out.push(i);
|
|
1244
|
+
i = line.indexOf(sub, i + sub.length);
|
|
1245
|
+
}
|
|
1246
|
+
return out;
|
|
1247
|
+
}
|
|
1248
|
+
function findOpenLink(line) {
|
|
1249
|
+
const bi = line.lastIndexOf("[");
|
|
1250
|
+
if (bi < 0) return null;
|
|
1251
|
+
const rest = line.slice(bi);
|
|
1252
|
+
if (!rest.includes("]")) return bi;
|
|
1253
|
+
if (/^\[[^\]]*\]\([^)]*$/.test(rest)) return bi;
|
|
1254
|
+
return null;
|
|
1255
|
+
}
|
|
1256
|
+
function pushEmphasis(line, ch, dbl, cands) {
|
|
1257
|
+
const masked = line.split(dbl).join("");
|
|
1258
|
+
const idx = [];
|
|
1259
|
+
for (let i = 0; i < masked.length; i++) if (masked[i] === ch) idx.push(i);
|
|
1260
|
+
if (idx.length % 2 === 1) {
|
|
1261
|
+
cands.push({ kind: "emphasis", tag: "em", cls: "tw-pending tw-pending-em", opener: idx[idx.length - 1], dl: 1 });
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
function findOpenRun(line, opt) {
|
|
1265
|
+
const cands = [];
|
|
1266
|
+
if (opt.strong) {
|
|
1267
|
+
const x = indexesOf(line, "**");
|
|
1268
|
+
if (x.length % 2 === 1) cands.push({ kind: "strong", tag: "strong", cls: "tw-pending tw-pending-strong", opener: x[x.length - 1], dl: 2 });
|
|
1269
|
+
}
|
|
1270
|
+
if (opt.strikethrough) {
|
|
1271
|
+
const x = indexesOf(line, "~~");
|
|
1272
|
+
if (x.length % 2 === 1) cands.push({ kind: "strikethrough", tag: "del", cls: "tw-pending tw-pending-del", opener: x[x.length - 1], dl: 2 });
|
|
1273
|
+
}
|
|
1274
|
+
if (opt.code) {
|
|
1275
|
+
const ticks = [];
|
|
1276
|
+
for (const m of line.matchAll(/`+/g)) {
|
|
1277
|
+
if (m[0].length >= 3) continue;
|
|
1278
|
+
for (let k = 0; k < m[0].length; k++) ticks.push(m.index + k);
|
|
1279
|
+
}
|
|
1280
|
+
if (ticks.length % 2 === 1) cands.push({ kind: "code", tag: "code", cls: "tw-pending tw-pending-code", opener: ticks[ticks.length - 1], dl: 1 });
|
|
1281
|
+
}
|
|
1282
|
+
if (opt.links) {
|
|
1283
|
+
const li = findOpenLink(line);
|
|
1284
|
+
if (li !== null) cands.push({ kind: "link", tag: "a", cls: "tw-pending tw-pending-link", opener: li, dl: 1 });
|
|
1285
|
+
}
|
|
1286
|
+
if (opt.emphasis) {
|
|
1287
|
+
pushEmphasis(line, "*", "**", cands);
|
|
1288
|
+
pushEmphasis(line, "_", "__", cands);
|
|
1289
|
+
}
|
|
1290
|
+
if (!cands.length) return null;
|
|
1291
|
+
cands.sort((a, b) => a.opener - b.opener);
|
|
1292
|
+
return cands[0];
|
|
1293
|
+
}
|
|
1294
|
+
function isFormingJsx(line, opt) {
|
|
1295
|
+
return opt.jsx && /<[A-Z][A-Za-z0-9]*\b[^>]*$/.test(line) && !/\/?>\s*$/.test(line);
|
|
1296
|
+
}
|
|
1297
|
+
var LIST_ITEM_RE = /^( {0,3})([-+*]|\d{1,9}[.)])[ \t]+(.*)$/;
|
|
1298
|
+
function isFormingListItem(line, opt) {
|
|
1299
|
+
return opt.listItems && LIST_ITEM_RE.test(line);
|
|
1300
|
+
}
|
|
1301
|
+
var TABLE_ROW_RE = /^ {0,3}\|/;
|
|
1302
|
+
function splitTableCells(row) {
|
|
1303
|
+
let s = row.trim();
|
|
1304
|
+
if (s.startsWith("|")) s = s.slice(1);
|
|
1305
|
+
if (s.endsWith("|")) s = s.slice(0, -1);
|
|
1306
|
+
return s.split("|").map((c) => c.trim());
|
|
1307
|
+
}
|
|
1308
|
+
function hasTrailing(line, opt) {
|
|
1309
|
+
if (!line.trim()) return false;
|
|
1310
|
+
return isFormingJsx(line, opt) || isFormingListItem(line, opt) || findOpenRun(line, opt) !== null;
|
|
1311
|
+
}
|
|
1312
|
+
function renderActiveLine(line, opt, pending, fallback) {
|
|
1313
|
+
if (isFormingJsx(line, opt)) {
|
|
1314
|
+
const name = /<([A-Z][A-Za-z0-9]*)/.exec(line)?.[1] ?? "Component";
|
|
1315
|
+
pending.push("jsx");
|
|
1316
|
+
const label = fallback ? esc(fallback) : `\u2039${esc(name)}\u203A rendering\u2026`;
|
|
1317
|
+
return `<div class="tw-skeleton" data-component="${esc(name)}"><span class="tw-skeleton-label">${label}</span><div class="tw-skeleton-bar"></div><div class="tw-skeleton-bar" style="width:70%"></div></div>`;
|
|
1318
|
+
}
|
|
1319
|
+
if (isFormingListItem(line, opt)) return renderActiveListItem(line, opt, pending);
|
|
1320
|
+
const heading = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
1321
|
+
const body = heading ? heading[2] ?? "" : line;
|
|
1322
|
+
const inner = renderInlineWithOpen(body, opt, pending);
|
|
1323
|
+
if (heading) return `<h${heading[1].length}>${inner}${CARET}</h${heading[1].length}>`;
|
|
1324
|
+
return `<p>${inner}${CARET}</p>`;
|
|
1325
|
+
}
|
|
1326
|
+
function renderActiveListItem(line, opt, pending) {
|
|
1327
|
+
const m = LIST_ITEM_RE.exec(line);
|
|
1328
|
+
const marker = m[2];
|
|
1329
|
+
const content = m[3] ?? "";
|
|
1330
|
+
pending.push("listItem");
|
|
1331
|
+
const inner = content ? renderInlineWithOpen(content, opt, pending) : "";
|
|
1332
|
+
const li = `<li class="tw-pending tw-pending-li">${inner}${CARET}</li>`;
|
|
1333
|
+
if (/\d/.test(marker)) {
|
|
1334
|
+
const n = parseInt(marker, 10);
|
|
1335
|
+
const startAttr = Number.isFinite(n) && n !== 1 ? ` start="${esc(String(n))}"` : "";
|
|
1336
|
+
return `<ol${startAttr} class="tw-pending tw-pending-list">${li}</ol>`;
|
|
1337
|
+
}
|
|
1338
|
+
return `<ul class="tw-pending tw-pending-list">${li}</ul>`;
|
|
1339
|
+
}
|
|
1340
|
+
function renderInlineWithOpen(text, opt, pending) {
|
|
1341
|
+
const open = findOpenRun(text, opt);
|
|
1342
|
+
if (!open) return inlineHtml(text);
|
|
1343
|
+
const prefix = text.slice(0, open.opener);
|
|
1344
|
+
pending.push(open.kind);
|
|
1345
|
+
if (open.kind === "link") {
|
|
1346
|
+
const after = text.slice(open.opener + 1);
|
|
1347
|
+
const close = after.indexOf("]");
|
|
1348
|
+
const label = close >= 0 ? after.slice(0, close) : after;
|
|
1349
|
+
return `${inlineHtml(prefix)}<a class="${open.cls}">${inlineHtml(label)}</a>`;
|
|
1350
|
+
}
|
|
1351
|
+
const content = text.slice(open.opener + open.dl);
|
|
1352
|
+
const inner = open.kind === "code" ? esc(content) : inlineHtml(content);
|
|
1353
|
+
return `${inlineHtml(prefix)}<${open.tag} class="${open.cls}">${inner}</${open.tag}>`;
|
|
1354
|
+
}
|
|
1355
|
+
function renderOpenFence(block, pending) {
|
|
1356
|
+
const nl = block.indexOf("\n");
|
|
1357
|
+
const info = block.slice(3, nl < 0 ? block.length : nl).trim();
|
|
1358
|
+
const lang = info.split(/\s+/)[0] ?? "";
|
|
1359
|
+
const code = nl < 0 ? "" : block.slice(nl + 1);
|
|
1360
|
+
pending.push("code");
|
|
1361
|
+
const langAttr = lang ? ` class="language-${esc(lang)}"` : "";
|
|
1362
|
+
return `<pre class="tw-codeblock"><code${langAttr}>${esc(code)}${CARET}</code></pre>`;
|
|
1363
|
+
}
|
|
1364
|
+
function renderActiveTableRow(row, pending) {
|
|
1365
|
+
const cells = splitTableCells(row);
|
|
1366
|
+
pending.push("tableRow");
|
|
1367
|
+
let tds = "";
|
|
1368
|
+
for (let i = 0; i < cells.length; i++) {
|
|
1369
|
+
const caret = i === cells.length - 1 ? CARET : "";
|
|
1370
|
+
tds += `<td>${cells[i] ? inlineHtml(cells[i]) : ""}${caret}</td>`;
|
|
1371
|
+
}
|
|
1372
|
+
return `<table class="tw-pending tw-pending-table"><tbody><tr>${tds}</tr></tbody></table>`;
|
|
1373
|
+
}
|
|
1374
|
+
function anticipate(partial, options = true, componentFallback) {
|
|
1375
|
+
const opt = normalizeOpt(options);
|
|
1376
|
+
const pending = [];
|
|
1377
|
+
if (!partial) return { html: "", pending };
|
|
1378
|
+
const fenceCount = (partial.match(/^```/gm) ?? []).length;
|
|
1379
|
+
if (opt.fences && fenceCount % 2 === 1) {
|
|
1380
|
+
const idx = partial.lastIndexOf("```");
|
|
1381
|
+
const committed2 = partial.slice(0, idx).replace(/\n+$/, "");
|
|
1382
|
+
const open = partial.slice(idx);
|
|
1383
|
+
const committedHtml2 = committed2.trim() ? renderToHtml(parse(committed2)) : "";
|
|
1384
|
+
return { html: committedHtml2 + renderOpenFence(open, pending), pending };
|
|
1385
|
+
}
|
|
1386
|
+
if (opt.tables) {
|
|
1387
|
+
const nl = partial.lastIndexOf("\n");
|
|
1388
|
+
if (nl >= 0) {
|
|
1389
|
+
const row = partial.slice(nl + 1);
|
|
1390
|
+
if (TABLE_ROW_RE.test(row)) {
|
|
1391
|
+
const committed2 = partial.slice(0, nl);
|
|
1392
|
+
const doc = parse(committed2);
|
|
1393
|
+
const last = doc.children[doc.children.length - 1];
|
|
1394
|
+
if (last && last.type === "table") {
|
|
1395
|
+
return { html: renderToHtml(doc) + renderActiveTableRow(row, pending), pending };
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
const lastNl = partial.lastIndexOf("\n");
|
|
1401
|
+
const active = lastNl >= 0 ? partial.slice(lastNl + 1) : partial;
|
|
1402
|
+
if (!hasTrailing(active, opt)) {
|
|
1403
|
+
return { html: renderToHtml(parse(partial)), pending };
|
|
1404
|
+
}
|
|
1405
|
+
const committed = lastNl >= 0 ? partial.slice(0, lastNl) : "";
|
|
1406
|
+
const committedHtml = committed.trim() ? renderToHtml(parse(committed)) : "";
|
|
1407
|
+
return { html: committedHtml + renderActiveLine(active, opt, pending, componentFallback), pending };
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
// src/streaming/index.ts
|
|
1411
|
+
var DEFAULT_CPS = 220;
|
|
1412
|
+
var TICK_MS = 1e3 / 30;
|
|
1413
|
+
function createStreamController(onUpdate, options = {}) {
|
|
1414
|
+
const smoothCfg = !options.smooth ? null : typeof options.smooth === "object" ? options.smooth : { charsPerSecond: DEFAULT_CPS };
|
|
1415
|
+
const cps = smoothCfg && smoothCfg.charsPerSecond > 0 ? smoothCfg.charsPerSecond : DEFAULT_CPS;
|
|
1416
|
+
const perTick = Math.max(1, Math.round(cps * TICK_MS / 1e3));
|
|
1417
|
+
let text = "";
|
|
1418
|
+
let revealed = 0;
|
|
1419
|
+
let complete = false;
|
|
1420
|
+
let timer = null;
|
|
1421
|
+
const emit = () => onUpdate(smoothCfg ? text.slice(0, revealed) : text, complete);
|
|
1422
|
+
const stopTimer = () => {
|
|
1423
|
+
if (timer !== null) {
|
|
1424
|
+
clearInterval(timer);
|
|
1425
|
+
timer = null;
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
const tick = () => {
|
|
1429
|
+
revealed = Math.min(text.length, revealed + perTick);
|
|
1430
|
+
emit();
|
|
1431
|
+
if (revealed >= text.length) stopTimer();
|
|
1432
|
+
};
|
|
1433
|
+
const ensureTimer = () => {
|
|
1434
|
+
if (timer !== null || complete || revealed >= text.length) return;
|
|
1435
|
+
timer = setInterval(tick, TICK_MS);
|
|
1436
|
+
};
|
|
1437
|
+
return {
|
|
1438
|
+
push(chunk) {
|
|
1439
|
+
if (complete) return;
|
|
1440
|
+
text += chunk;
|
|
1441
|
+
if (smoothCfg) ensureTimer();
|
|
1442
|
+
emit();
|
|
1443
|
+
},
|
|
1444
|
+
replace(full) {
|
|
1445
|
+
text = full;
|
|
1446
|
+
complete = false;
|
|
1447
|
+
if (smoothCfg) {
|
|
1448
|
+
if (revealed > text.length) revealed = text.length;
|
|
1449
|
+
ensureTimer();
|
|
1450
|
+
}
|
|
1451
|
+
emit();
|
|
1452
|
+
},
|
|
1453
|
+
end() {
|
|
1454
|
+
complete = true;
|
|
1455
|
+
revealed = text.length;
|
|
1456
|
+
stopTimer();
|
|
1457
|
+
emit();
|
|
1458
|
+
},
|
|
1459
|
+
reset() {
|
|
1460
|
+
text = "";
|
|
1461
|
+
revealed = 0;
|
|
1462
|
+
complete = false;
|
|
1463
|
+
stopTimer();
|
|
1464
|
+
emit();
|
|
1465
|
+
},
|
|
1466
|
+
get text() {
|
|
1467
|
+
return text;
|
|
1468
|
+
},
|
|
1469
|
+
get complete() {
|
|
1470
|
+
return complete;
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
async function pipeStream(source, controller) {
|
|
1475
|
+
if (Symbol.asyncIterator in source) {
|
|
1476
|
+
for await (const chunk of source) {
|
|
1477
|
+
controller.push(chunk);
|
|
1478
|
+
}
|
|
1479
|
+
} else {
|
|
1480
|
+
const reader = source.getReader();
|
|
1481
|
+
for (; ; ) {
|
|
1482
|
+
const { done, value } = await reader.read();
|
|
1483
|
+
if (done) break;
|
|
1484
|
+
if (value != null) controller.push(value);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
controller.end();
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
exports.anticipate = anticipate;
|
|
1491
|
+
exports.createStreamController = createStreamController;
|
|
1492
|
+
exports.pipeStream = pipeStream;
|
|
1493
|
+
//# sourceMappingURL=index.cjs.map
|
|
1494
|
+
//# sourceMappingURL=index.cjs.map
|