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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +168 -0
  3. package/SPEC.md +327 -0
  4. package/dist/chunk-CZQO7TTL.js +1533 -0
  5. package/dist/chunk-CZQO7TTL.js.map +1 -0
  6. package/dist/chunk-KNEKNZXK.js +286 -0
  7. package/dist/chunk-KNEKNZXK.js.map +1 -0
  8. package/dist/chunk-M6IVEMXO.js +1398 -0
  9. package/dist/chunk-M6IVEMXO.js.map +1 -0
  10. package/dist/chunk-NJ7PJKHN.js +652 -0
  11. package/dist/chunk-NJ7PJKHN.js.map +1 -0
  12. package/dist/commands-Z3ndUSza.d.cts +221 -0
  13. package/dist/commands-Z3ndUSza.d.ts +221 -0
  14. package/dist/core/index.cjs +2952 -0
  15. package/dist/core/index.cjs.map +1 -0
  16. package/dist/core/index.d.cts +410 -0
  17. package/dist/core/index.d.ts +410 -0
  18. package/dist/core/index.js +4 -0
  19. package/dist/core/index.js.map +1 -0
  20. package/dist/mdx/index.cjs +662 -0
  21. package/dist/mdx/index.cjs.map +1 -0
  22. package/dist/mdx/index.d.cts +252 -0
  23. package/dist/mdx/index.d.ts +252 -0
  24. package/dist/mdx/index.js +3 -0
  25. package/dist/mdx/index.js.map +1 -0
  26. package/dist/react/index.cjs +7439 -0
  27. package/dist/react/index.cjs.map +1 -0
  28. package/dist/react/index.d.cts +140 -0
  29. package/dist/react/index.d.ts +140 -0
  30. package/dist/react/index.js +3644 -0
  31. package/dist/react/index.js.map +1 -0
  32. package/dist/streaming/index.cjs +1494 -0
  33. package/dist/streaming/index.cjs.map +1 -0
  34. package/dist/streaming/index.d.cts +64 -0
  35. package/dist/streaming/index.d.ts +64 -0
  36. package/dist/streaming/index.js +4 -0
  37. package/dist/streaming/index.js.map +1 -0
  38. package/dist/types-CX1GOx0Y.d.cts +319 -0
  39. package/dist/types-CX1GOx0Y.d.ts +319 -0
  40. package/package.json +128 -0
@@ -0,0 +1,2952 @@
1
+ 'use strict';
2
+
3
+ // src/core/ast.ts
4
+ var INLINE_TYPES = /* @__PURE__ */ new Set([
5
+ "text",
6
+ "emphasis",
7
+ "strong",
8
+ "strikethrough",
9
+ "inlineCode",
10
+ "link",
11
+ "image",
12
+ "autolink",
13
+ "break",
14
+ "math",
15
+ "footnoteRef"
16
+ ]);
17
+ function isInline(node) {
18
+ return INLINE_TYPES.has(node.type);
19
+ }
20
+ function walk(node, visit) {
21
+ const go = (n) => {
22
+ if (visit(n) === false) return;
23
+ const kids = childrenOf(n);
24
+ for (const k of kids) go(k);
25
+ };
26
+ go(node);
27
+ }
28
+ function childrenOf(node) {
29
+ switch (node.type) {
30
+ case "document":
31
+ case "blockquote":
32
+ return node.children;
33
+ case "heading":
34
+ case "paragraph":
35
+ case "emphasis":
36
+ case "strong":
37
+ case "strikethrough":
38
+ case "tableCell":
39
+ return node.children;
40
+ case "link":
41
+ return node.children;
42
+ case "list":
43
+ return node.items;
44
+ case "listItem":
45
+ return node.children;
46
+ case "table":
47
+ return [...node.header, ...node.rows.flat()];
48
+ case "footnoteDef":
49
+ return node.children;
50
+ case "defList":
51
+ return node.items;
52
+ case "defItem":
53
+ return [...node.term, ...node.definitions.flat()];
54
+ default:
55
+ return [];
56
+ }
57
+ }
58
+
59
+ // src/core/parser.ts
60
+ var MAX_URL = 4096;
61
+ var MAX_TITLE = 1024;
62
+ var MAX_ALT = 4096;
63
+ var MAX_LANG = 256;
64
+ var MAX_VALUE = 1e6;
65
+ var MAX_FOOTNOTE_ID = 256;
66
+ var cap = (s, n) => s.length > n ? s.slice(0, n) : s;
67
+ var opts = { math: false, footnotes: false, defLists: false };
68
+ function splitLines(src) {
69
+ const lines = [];
70
+ let from = 0;
71
+ for (let i = 0; i <= src.length; i++) {
72
+ if (i === src.length || src[i] === "\n") {
73
+ let end = i;
74
+ if (end > from && src[end - 1] === "\r") end--;
75
+ lines.push({ from, to: end, text: src.slice(from, end) });
76
+ from = i + 1;
77
+ }
78
+ }
79
+ return lines;
80
+ }
81
+ var leadingSpaces = (t) => {
82
+ let n = 0;
83
+ while (n < t.length && t[n] === " ") n++;
84
+ return n;
85
+ };
86
+ var isBlank = (line) => line.text.trim() === "";
87
+ var FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;
88
+ var ATX_RE = /^( {0,3})(#{1,6})(?=\s|$)/;
89
+ var ITEM_RE = /^( {0,3})([-+*]|\d{1,9}[.)])( +|$)/;
90
+ var HTML_TAG_RE = /^<\/?([A-Za-z][A-Za-z0-9-]*)(?:[\s/>]|$)/;
91
+ var ESM_RE = /^(?:import|export)\b/;
92
+ var isFence = (t) => {
93
+ const m = FENCE_RE.exec(t);
94
+ if (!m) return false;
95
+ return m[2][0] !== "`" || !m[3].includes("`");
96
+ };
97
+ var isAtx = (t) => ATX_RE.test(t);
98
+ var isThematic = (t) => {
99
+ const trimmed = t.trim();
100
+ if (trimmed.length < 3) return false;
101
+ const compact = trimmed.replace(/ /g, "");
102
+ return /^(?:-{3,}|_{3,}|\*{3,})$/.test(compact);
103
+ };
104
+ var isQuote = (t) => /^ {0,3}>/.test(t);
105
+ function detectItem(line) {
106
+ const m = ITEM_RE.exec(line.text);
107
+ if (!m) return null;
108
+ const marker = m[2];
109
+ return {
110
+ indent: m[1].length,
111
+ marker,
112
+ markerLen: marker.length,
113
+ spacesAfter: m[3] === "" ? 0 : m[3].length,
114
+ ordered: /\d/.test(marker[0])
115
+ };
116
+ }
117
+ function htmlVariant(t) {
118
+ const s = t.replace(/^ {0,3}/, "");
119
+ if (ESM_RE.test(s)) return "mdxFlow";
120
+ const m = HTML_TAG_RE.exec(s);
121
+ if (!m) return null;
122
+ return /[A-Z]/.test(m[1][0]) ? "mdxFlow" : "html";
123
+ }
124
+ function isDelimRow(t) {
125
+ let s = t.trim();
126
+ if (!s.includes("-")) return false;
127
+ s = s.replace(/^\|/, "").replace(/\|$/, "");
128
+ const cells = s.split("|");
129
+ return cells.length >= 1 && cells.every((c) => /^ *:?-+:? *$/.test(c));
130
+ }
131
+ function isTableStartAt(lines, j) {
132
+ const line = lines[j];
133
+ const next = lines[j + 1];
134
+ if (!line || !next) return false;
135
+ if (isBlank(line)) return false;
136
+ return line.text.includes("|") && isDelimRow(next.text);
137
+ }
138
+ var MATH_FENCE_RE = /^ {0,3}\$\$[ \t]*$/;
139
+ var isMathOpen = (t) => opts.math && MATH_FENCE_RE.test(t);
140
+ var FOOTNOTE_DEF_RE = /^( {0,3})\[\^([^\]\s]+)\]:/;
141
+ var isFootnoteDef = (line) => opts.footnotes && FOOTNOTE_DEF_RE.test(line.text);
142
+ var DEF_RE = /^( {0,3}):[ \t]+/;
143
+ function isDefListStartAt(lines, j) {
144
+ if (!opts.defLists) return false;
145
+ const line = lines[j];
146
+ const next = lines[j + 1];
147
+ if (!line || !next) return false;
148
+ if (isBlank(line)) return false;
149
+ if (startsBlock(lines, j)) return false;
150
+ return DEF_RE.test(next.text);
151
+ }
152
+ function startsBlock(lines, j) {
153
+ const line = lines[j];
154
+ const t = line.text;
155
+ return isFence(t) || isAtx(t) || isThematic(t) || isQuote(t) || detectItem(line) !== null || htmlVariant(t) !== null || isTableStartAt(lines, j) || isMathOpen(t) || isFootnoteDef(line);
156
+ }
157
+ function startsOtherBlock(line) {
158
+ const t = line.text;
159
+ return isFence(t) || isAtx(t) || isThematic(t) || isQuote(t) || detectItem(line) !== null || htmlVariant(t) !== null || isMathOpen(t) || isFootnoteDef(line);
160
+ }
161
+ function buildHeading(line) {
162
+ const t = line.text;
163
+ const m = ATX_RE.exec(t);
164
+ const indent = m[1].length;
165
+ const hashes = m[2];
166
+ const level = hashes.length;
167
+ let ce = indent + hashes.length;
168
+ if (t[ce] === " ") ce++;
169
+ const contentFrom = line.from + ce;
170
+ let end = t.length;
171
+ while (end > ce && (t[end - 1] === " " || t[end - 1] === " ")) end--;
172
+ let e2 = end;
173
+ while (e2 > ce && t[e2 - 1] === "#") e2--;
174
+ if (e2 < end && (e2 === ce || t[e2 - 1] === " " || t[e2 - 1] === " ")) {
175
+ end = e2;
176
+ while (end > ce && (t[end - 1] === " " || t[end - 1] === " ")) end--;
177
+ }
178
+ const children = parseInlineSegs([
179
+ { from: contentFrom, to: line.from + end, text: t.slice(ce, end) }
180
+ ]);
181
+ return { type: "heading", from: line.from, to: line.to, level, contentFrom, children };
182
+ }
183
+ function buildFence(lines, i) {
184
+ const open = lines[i];
185
+ const m = FENCE_RE.exec(open.text);
186
+ const indent = m[1].length;
187
+ const fenceChar = m[2][0];
188
+ const fenceLen = m[2].length;
189
+ const lang = cap(m[3].trim(), MAX_LANG);
190
+ const inner = [];
191
+ let j = i + 1;
192
+ let closeTo = null;
193
+ while (j < lines.length) {
194
+ const cm = /^( {0,3})(`{3,}|~{3,}) *$/.exec(lines[j].text);
195
+ if (cm && cm[2][0] === fenceChar && cm[2].length >= fenceLen) {
196
+ closeTo = lines[j].to;
197
+ j++;
198
+ break;
199
+ }
200
+ inner.push(lines[j]);
201
+ j++;
202
+ }
203
+ const value = cap(
204
+ inner.map((l) => leadingSpaces(l.text) >= indent ? l.text.slice(indent) : l.text.trimStart()).join("\n"),
205
+ MAX_VALUE
206
+ );
207
+ const to = closeTo ?? (inner.length ? inner[inner.length - 1].to : open.to);
208
+ return { node: { type: "codeBlock", from: open.from, to, lang, value, fenced: true }, next: j };
209
+ }
210
+ function buildMathBlock(lines, i) {
211
+ const open = lines[i];
212
+ const inner = [];
213
+ let j = i + 1;
214
+ let closeTo = null;
215
+ while (j < lines.length) {
216
+ if (MATH_FENCE_RE.test(lines[j].text)) {
217
+ closeTo = lines[j].to;
218
+ j++;
219
+ break;
220
+ }
221
+ inner.push(lines[j]);
222
+ j++;
223
+ }
224
+ const value = cap(inner.map((l) => l.text).join("\n"), MAX_VALUE);
225
+ const to = closeTo ?? (inner.length ? inner[inner.length - 1].to : open.to);
226
+ const node = { type: "mathBlock", from: open.from, to, value };
227
+ return { node, next: j };
228
+ }
229
+ function buildIndentedCode(lines, i) {
230
+ const from = lines[i].from;
231
+ const inner = [lines[i]];
232
+ let j = i + 1;
233
+ let pending = [];
234
+ while (j < lines.length) {
235
+ const line = lines[j];
236
+ if (isBlank(line)) {
237
+ pending.push(line);
238
+ j++;
239
+ continue;
240
+ }
241
+ if (leadingSpaces(line.text) >= 4) {
242
+ inner.push(...pending, line);
243
+ pending = [];
244
+ j++;
245
+ continue;
246
+ }
247
+ break;
248
+ }
249
+ const last = inner[inner.length - 1];
250
+ const value = cap(inner.map((l) => l.text.slice(4)).join("\n"), MAX_VALUE);
251
+ return {
252
+ node: { type: "codeBlock", from, to: last.to, lang: "", value, fenced: false },
253
+ next: j
254
+ };
255
+ }
256
+ function buildThematic(line) {
257
+ return { type: "thematicBreak", from: line.from, to: line.to };
258
+ }
259
+ function stripQuote(line) {
260
+ const m = /^( {0,3})>( ?)/.exec(line.text);
261
+ const cut = m[0].length;
262
+ return { from: line.from + cut, to: line.to, text: line.text.slice(cut) };
263
+ }
264
+ function buildBlockquote(lines, i) {
265
+ const from = lines[i].from;
266
+ const inner = [];
267
+ let j = i;
268
+ while (j < lines.length && isQuote(lines[j].text)) {
269
+ inner.push(stripQuote(lines[j]));
270
+ j++;
271
+ }
272
+ const to = lines[j - 1].to;
273
+ const children = parseBlocks(inner);
274
+ return { node: { type: "blockquote", from, to, children }, next: j };
275
+ }
276
+ var TASK_RE = /^\[([ xX])\](\s|$)/;
277
+ function collectItem(lines, idx) {
278
+ const line0 = lines[idx];
279
+ const det = detectItem(line0);
280
+ const contentCol = det.indent + det.markerLen + 1;
281
+ const localStart = det.indent + det.markerLen + (det.spacesAfter > 0 ? 1 : 0);
282
+ let task = null;
283
+ let bodyStart = localStart;
284
+ const afterMarker = line0.text.slice(localStart);
285
+ const tm = TASK_RE.exec(afterMarker);
286
+ if (tm) {
287
+ task = tm[1] === " " ? "unchecked" : "checked";
288
+ bodyStart = localStart + tm[0].length;
289
+ }
290
+ const contentFrom = line0.from + bodyStart;
291
+ const body = [
292
+ { from: contentFrom, to: line0.to, text: line0.text.slice(bodyStart) }
293
+ ];
294
+ let lastTo = line0.to;
295
+ let loose = false;
296
+ let pendingBlank = false;
297
+ let j = idx + 1;
298
+ while (j < lines.length) {
299
+ const lj = lines[j];
300
+ if (isBlank(lj)) {
301
+ pendingBlank = true;
302
+ j++;
303
+ continue;
304
+ }
305
+ const ind = leadingSpaces(lj.text);
306
+ if (ind >= contentCol) {
307
+ if (pendingBlank) {
308
+ loose = true;
309
+ body.push({ from: lj.from, to: lj.from, text: "" });
310
+ }
311
+ body.push({
312
+ from: lj.from + contentCol,
313
+ to: lj.to,
314
+ text: lj.text.slice(contentCol)
315
+ });
316
+ lastTo = lj.to;
317
+ pendingBlank = false;
318
+ j++;
319
+ continue;
320
+ }
321
+ if (detectItem(lj)) break;
322
+ if (pendingBlank) break;
323
+ if (startsOtherBlock(lj)) break;
324
+ body.push({ from: lj.from, to: lj.to, text: lj.text });
325
+ lastTo = lj.to;
326
+ j++;
327
+ }
328
+ const children = parseBlocks(body);
329
+ const item = {
330
+ type: "listItem",
331
+ from: line0.from,
332
+ to: lastTo,
333
+ task,
334
+ contentFrom,
335
+ children
336
+ };
337
+ return { item, next: j, blankBefore: pendingBlank, loose };
338
+ }
339
+ function buildList(lines, start) {
340
+ const first = detectItem(lines[start]);
341
+ const ordered = first.ordered;
342
+ const startNum = ordered ? parseInt(first.marker, 10) || 0 : 1;
343
+ const items = [];
344
+ let loose = false;
345
+ let i = start;
346
+ while (i < lines.length) {
347
+ const det = detectItem(lines[i]);
348
+ if (!det || det.ordered !== ordered) break;
349
+ const r = collectItem(lines, i);
350
+ if (r.blankBefore) loose = true;
351
+ if (r.loose) loose = true;
352
+ items.push(r.item);
353
+ i = r.next;
354
+ }
355
+ const node = {
356
+ type: "list",
357
+ from: items[0].from,
358
+ to: items[items.length - 1].to,
359
+ ordered,
360
+ start: startNum,
361
+ tight: !loose,
362
+ items
363
+ };
364
+ return { node, next: i };
365
+ }
366
+ function parseAligns(delim) {
367
+ return splitCellRanges(delim).map(([a, b]) => {
368
+ const cell = delim.slice(a, b).trim();
369
+ const left = cell.startsWith(":");
370
+ const right = cell.endsWith(":");
371
+ if (left && right) return "center";
372
+ if (left) return "left";
373
+ if (right) return "right";
374
+ return null;
375
+ });
376
+ }
377
+ function splitCellRanges(t) {
378
+ const cells = [];
379
+ let s = 0;
380
+ for (let k = 0; k < t.length; k++) {
381
+ if (t[k] === "|" && t[k - 1] !== "\\") {
382
+ cells.push([s, k]);
383
+ s = k + 1;
384
+ }
385
+ }
386
+ cells.push([s, t.length]);
387
+ if (cells.length > 1 && t.slice(cells[0][0], cells[0][1]).trim() === "") cells.shift();
388
+ if (cells.length > 1 && t.slice(cells[cells.length - 1][0], cells[cells.length - 1][1]).trim() === "")
389
+ cells.pop();
390
+ return cells;
391
+ }
392
+ function splitRow(line) {
393
+ const t = line.text;
394
+ return splitCellRanges(t).map(([a, b]) => {
395
+ let ca = a;
396
+ while (ca < b && t[ca] === " ") ca++;
397
+ let cb = b;
398
+ while (cb > ca && t[cb - 1] === " ") cb--;
399
+ const from = line.from + ca;
400
+ const to = line.from + cb;
401
+ const children = parseInlineSegs([{ from, to, text: t.slice(ca, cb) }]);
402
+ return { type: "tableCell", from, to, children };
403
+ });
404
+ }
405
+ function buildTable(lines, i) {
406
+ const header = splitRow(lines[i]);
407
+ const align = parseAligns(lines[i + 1].text);
408
+ const rows = [];
409
+ let j = i + 2;
410
+ let lastTo = lines[i + 1].to;
411
+ while (j < lines.length) {
412
+ const line = lines[j];
413
+ if (isBlank(line) || startsOtherBlock(line)) break;
414
+ rows.push(splitRow(line));
415
+ lastTo = line.to;
416
+ j++;
417
+ }
418
+ return {
419
+ node: { type: "table", from: lines[i].from, to: lastTo, align, header, rows },
420
+ next: j
421
+ };
422
+ }
423
+ function buildHtml(lines, i) {
424
+ const variant = htmlVariant(lines[i].text);
425
+ const from = lines[i].from;
426
+ const block = [];
427
+ let j = i;
428
+ while (j < lines.length && !isBlank(lines[j])) {
429
+ block.push(lines[j]);
430
+ j++;
431
+ }
432
+ const to = block[block.length - 1].to;
433
+ const value = cap(block.map((l) => l.text).join("\n"), MAX_VALUE);
434
+ return { node: { type: "htmlBlock", from, to, value, variant }, next: j };
435
+ }
436
+ function buildFootnoteDef(lines, i) {
437
+ const line0 = lines[i];
438
+ const m = FOOTNOTE_DEF_RE.exec(line0.text);
439
+ const indent = m[1].length;
440
+ const id = m[2];
441
+ const markerLen = m[0].length - indent;
442
+ const contentCol = indent + markerLen + 1;
443
+ let bodyStart = indent + markerLen;
444
+ if (line0.text[bodyStart] === " ") bodyStart++;
445
+ const contentFrom = line0.from + bodyStart;
446
+ const body = [
447
+ { from: contentFrom, to: line0.to, text: line0.text.slice(bodyStart) }
448
+ ];
449
+ let lastTo = line0.to;
450
+ let pendingBlank = false;
451
+ let j = i + 1;
452
+ while (j < lines.length) {
453
+ const lj = lines[j];
454
+ if (isBlank(lj)) {
455
+ pendingBlank = true;
456
+ j++;
457
+ continue;
458
+ }
459
+ const ind = leadingSpaces(lj.text);
460
+ if (ind >= contentCol) {
461
+ if (pendingBlank) body.push({ from: lj.from, to: lj.from, text: "" });
462
+ body.push({ from: lj.from + contentCol, to: lj.to, text: lj.text.slice(contentCol) });
463
+ lastTo = lj.to;
464
+ pendingBlank = false;
465
+ j++;
466
+ continue;
467
+ }
468
+ if (pendingBlank) break;
469
+ if (isFootnoteDef(lj)) break;
470
+ if (startsOtherBlock(lj)) break;
471
+ if (isDefListStartAt(lines, j)) break;
472
+ body.push({ from: lj.from, to: lj.to, text: lj.text });
473
+ lastTo = lj.to;
474
+ j++;
475
+ }
476
+ const children = parseBlocks(body);
477
+ const node = { type: "footnoteDef", from: line0.from, to: lastTo, id, children };
478
+ return { node, next: j };
479
+ }
480
+ function collectDefinition(lines, idx) {
481
+ const line0 = lines[idx];
482
+ const m = DEF_RE.exec(line0.text);
483
+ const contentCol = m[0].length;
484
+ const body = [
485
+ { from: line0.from + contentCol, to: line0.to, text: line0.text.slice(contentCol) }
486
+ ];
487
+ let lastTo = line0.to;
488
+ let pendingBlank = false;
489
+ let j = idx + 1;
490
+ while (j < lines.length) {
491
+ const lj = lines[j];
492
+ if (isBlank(lj)) {
493
+ pendingBlank = true;
494
+ j++;
495
+ continue;
496
+ }
497
+ if (DEF_RE.test(lj.text)) break;
498
+ const ind = leadingSpaces(lj.text);
499
+ if (ind >= contentCol) {
500
+ if (pendingBlank) body.push({ from: lj.from, to: lj.from, text: "" });
501
+ body.push({ from: lj.from + contentCol, to: lj.to, text: lj.text.slice(contentCol) });
502
+ lastTo = lj.to;
503
+ pendingBlank = false;
504
+ j++;
505
+ continue;
506
+ }
507
+ if (pendingBlank) break;
508
+ if (startsOtherBlock(lj)) break;
509
+ if (isFootnoteDef(lj)) break;
510
+ if (isDefListStartAt(lines, j)) break;
511
+ body.push({ from: lj.from, to: lj.to, text: lj.text });
512
+ lastTo = lj.to;
513
+ j++;
514
+ }
515
+ return { blocks: parseBlocks(body), to: lastTo, next: j };
516
+ }
517
+ function collectDefItem(lines, idx) {
518
+ const termLine = lines[idx];
519
+ const term = parseInlineSegs([
520
+ { from: termLine.from, to: termLine.to, text: termLine.text }
521
+ ]);
522
+ const definitions = [];
523
+ let lastTo = termLine.to;
524
+ let j = idx + 1;
525
+ while (j < lines.length && DEF_RE.test(lines[j].text)) {
526
+ const d = collectDefinition(lines, j);
527
+ definitions.push(d.blocks);
528
+ lastTo = d.to;
529
+ j = d.next;
530
+ }
531
+ const item = { type: "defItem", from: termLine.from, to: lastTo, term, definitions };
532
+ return { item, next: j };
533
+ }
534
+ function buildDefList(lines, start) {
535
+ const items = [];
536
+ let i = start;
537
+ while (i < lines.length && isDefListStartAt(lines, i)) {
538
+ const r = collectDefItem(lines, i);
539
+ items.push(r.item);
540
+ i = r.next;
541
+ }
542
+ const node = {
543
+ type: "defList",
544
+ from: items[0].from,
545
+ to: items[items.length - 1].to,
546
+ items
547
+ };
548
+ return { node, next: i };
549
+ }
550
+ function buildParagraph(lines, i) {
551
+ const para = [lines[i]];
552
+ let j = i + 1;
553
+ while (j < lines.length) {
554
+ if (isBlank(lines[j])) break;
555
+ if (startsBlock(lines, j)) break;
556
+ para.push(lines[j]);
557
+ j++;
558
+ }
559
+ const from = para[0].from;
560
+ const to = para[para.length - 1].to;
561
+ const children = parseInlineSegs(para.map((l) => ({ from: l.from, to: l.to, text: l.text })));
562
+ return { node: { type: "paragraph", from, to, children }, next: j };
563
+ }
564
+ function parseBlocks(lines) {
565
+ const out = [];
566
+ let i = 0;
567
+ while (i < lines.length) {
568
+ const line = lines[i];
569
+ if (isBlank(line)) {
570
+ i++;
571
+ continue;
572
+ }
573
+ const t = line.text;
574
+ if (isFence(t)) {
575
+ const b = buildFence(lines, i);
576
+ out.push(b.node);
577
+ i = b.next;
578
+ } else if (isMathOpen(t)) {
579
+ const b = buildMathBlock(lines, i);
580
+ out.push(b.node);
581
+ i = b.next;
582
+ } else if (leadingSpaces(t) >= 4) {
583
+ const b = buildIndentedCode(lines, i);
584
+ out.push(b.node);
585
+ i = b.next;
586
+ } else if (isAtx(t)) {
587
+ out.push(buildHeading(line));
588
+ i++;
589
+ } else if (isThematic(t)) {
590
+ out.push(buildThematic(line));
591
+ i++;
592
+ } else if (isQuote(t)) {
593
+ const b = buildBlockquote(lines, i);
594
+ out.push(b.node);
595
+ i = b.next;
596
+ } else if (detectItem(line)) {
597
+ const b = buildList(lines, i);
598
+ out.push(b.node);
599
+ i = b.next;
600
+ } else if (isDefListStartAt(lines, i)) {
601
+ const b = buildDefList(lines, i);
602
+ out.push(b.node);
603
+ i = b.next;
604
+ } else if (isTableStartAt(lines, i)) {
605
+ const b = buildTable(lines, i);
606
+ out.push(b.node);
607
+ i = b.next;
608
+ } else if (htmlVariant(t)) {
609
+ const b = buildHtml(lines, i);
610
+ out.push(b.node);
611
+ i = b.next;
612
+ } else if (isFootnoteDef(line)) {
613
+ const b = buildFootnoteDef(lines, i);
614
+ out.push(b.node);
615
+ i = b.next;
616
+ } else {
617
+ const b = buildParagraph(lines, i);
618
+ out.push(b.node);
619
+ i = b.next;
620
+ }
621
+ }
622
+ return out;
623
+ }
624
+ var isWs = (ch) => ch === void 0 || ch === " " || ch === "\n" || ch === " " || ch === "\r";
625
+ var isAlnum = (ch) => ch !== void 0 && /[0-9A-Za-z]/.test(ch);
626
+ function parseInlineSegs(segs) {
627
+ if (segs.length === 0) return [];
628
+ let buf = "";
629
+ const map = [];
630
+ for (let s = 0; s < segs.length; s++) {
631
+ const seg = segs[s];
632
+ if (s > 0) {
633
+ map.push(segs[s - 1].to);
634
+ buf += "\n";
635
+ }
636
+ for (let k = 0; k < seg.text.length; k++) {
637
+ map.push(seg.from + k);
638
+ buf += seg.text[k];
639
+ }
640
+ }
641
+ map.push(segs[segs.length - 1].to);
642
+ const abs = (k) => k < map.length ? map[k] : segs[segs.length - 1].to;
643
+ const text = (from, to, value) => ({
644
+ type: "text",
645
+ from,
646
+ to,
647
+ value: cap(value, MAX_VALUE)
648
+ });
649
+ function tryCode(i, end) {
650
+ let n = 1;
651
+ while (i + n < end && buf[i + n] === "`") n++;
652
+ let k = i + n;
653
+ while (k < end) {
654
+ if (buf[k] === "`") {
655
+ let m = 1;
656
+ while (k + m < end && buf[k + m] === "`") m++;
657
+ if (m === n) {
658
+ let val = buf.slice(i + n, k);
659
+ if (val.length > 2 && val[0] === " " && val[val.length - 1] === " " && val.trim() !== "")
660
+ val = val.slice(1, -1);
661
+ const node = {
662
+ type: "inlineCode",
663
+ from: abs(i),
664
+ to: abs(k + n),
665
+ value: cap(val, MAX_VALUE),
666
+ ticks: n
667
+ };
668
+ return { node, endIdx: k + n };
669
+ }
670
+ k += m;
671
+ continue;
672
+ }
673
+ k++;
674
+ }
675
+ return null;
676
+ }
677
+ function tryMath(i, end) {
678
+ let n = 1;
679
+ while (i + n < end && buf[i + n] === "$" && n < 2) n++;
680
+ const after = buf[i + n];
681
+ if (after === void 0 || after === " " || after === " " || after === "\n") return null;
682
+ let k = i + n;
683
+ while (k < end) {
684
+ const ch = buf[k];
685
+ if (ch === "\\") {
686
+ k += 2;
687
+ continue;
688
+ }
689
+ if (ch === "\n") return null;
690
+ if (ch === "$") {
691
+ let m = 1;
692
+ while (k + m < end && buf[k + m] === "$" && m < 2) m++;
693
+ if (m === n && k > i + n) {
694
+ const prev = buf[k - 1];
695
+ if (prev !== " " && prev !== " " && prev !== "\n") {
696
+ const node = {
697
+ type: "math",
698
+ from: abs(i),
699
+ to: abs(k + n),
700
+ value: cap(buf.slice(i + n, k), MAX_VALUE),
701
+ display: n === 2
702
+ };
703
+ return { node, endIdx: k + n };
704
+ }
705
+ }
706
+ k += m;
707
+ continue;
708
+ }
709
+ k++;
710
+ }
711
+ return null;
712
+ }
713
+ function tryFootnoteRef(i, end) {
714
+ const idStart = i + 2;
715
+ const stop = Math.min(end, idStart + MAX_FOOTNOTE_ID);
716
+ let k = idStart;
717
+ while (k < stop) {
718
+ const ch = buf[k];
719
+ if (ch === "]") break;
720
+ if (ch === " " || ch === " " || ch === "\n" || ch === "[" || ch === "^") return null;
721
+ k++;
722
+ }
723
+ if (k >= stop || buf[k] !== "]" || k === idStart) return null;
724
+ const node = {
725
+ type: "footnoteRef",
726
+ from: abs(i),
727
+ to: abs(k + 1),
728
+ id: buf.slice(idStart, k)
729
+ };
730
+ return { node, endIdx: k + 1 };
731
+ }
732
+ function tryAutolink(i, end) {
733
+ let k = i + 1;
734
+ while (k < end && buf[k] !== ">" && buf[k] !== "<" && buf[k] !== " " && buf[k] !== "\n") k++;
735
+ if (k >= end || buf[k] !== ">") return null;
736
+ const content = buf.slice(i + 1, k);
737
+ const isUri = /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/.test(content);
738
+ const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(content);
739
+ if (!isUri && !isEmail) return null;
740
+ const node = {
741
+ type: "autolink",
742
+ from: abs(i),
743
+ to: abs(k + 1),
744
+ url: cap(content, MAX_URL)
745
+ };
746
+ return { node, endIdx: k + 1 };
747
+ }
748
+ function matchLabel(open, end) {
749
+ const stop = Math.min(end, open + 4096);
750
+ let depth = 0;
751
+ for (let k = open; k < stop; k++) {
752
+ const ch = buf[k];
753
+ if (ch === "\\") {
754
+ k++;
755
+ continue;
756
+ }
757
+ if (ch === "[") depth++;
758
+ else if (ch === "]") {
759
+ depth--;
760
+ if (depth === 0) return k;
761
+ }
762
+ }
763
+ return -1;
764
+ }
765
+ function tryLink(i, end, isImage) {
766
+ const br = i + (isImage ? 1 : 0);
767
+ if (buf[br] !== "[") return null;
768
+ const close = matchLabel(br, end);
769
+ if (close < 0) return null;
770
+ let k = close + 1;
771
+ if (buf[k] !== "(") return null;
772
+ k++;
773
+ while (k < end && (buf[k] === " " || buf[k] === "\n")) k++;
774
+ let url = "";
775
+ if (buf[k] === "<") {
776
+ k++;
777
+ const s = k;
778
+ while (k < end && buf[k] !== ">" && buf[k] !== "\n") k++;
779
+ if (buf[k] !== ">") return null;
780
+ url = buf.slice(s, k);
781
+ k++;
782
+ } else {
783
+ const s = k;
784
+ let depth = 0;
785
+ while (k < end) {
786
+ const ch = buf[k];
787
+ if (ch === " " || ch === "\n") break;
788
+ if (ch === "(") depth++;
789
+ else if (ch === ")") {
790
+ if (depth === 0) break;
791
+ depth--;
792
+ }
793
+ k++;
794
+ }
795
+ url = buf.slice(s, k);
796
+ }
797
+ let title;
798
+ while (k < end && (buf[k] === " " || buf[k] === "\n")) k++;
799
+ const q = buf[k];
800
+ if (q === '"' || q === "'" || q === "(") {
801
+ const closeCh = q === "(" ? ")" : q;
802
+ k++;
803
+ const s = k;
804
+ while (k < end && buf[k] !== closeCh) k++;
805
+ if (buf[k] !== closeCh) return null;
806
+ title = buf.slice(s, k);
807
+ k++;
808
+ while (k < end && (buf[k] === " " || buf[k] === "\n")) k++;
809
+ }
810
+ if (buf[k] !== ")") return null;
811
+ const endIdx = k + 1;
812
+ const from = abs(i);
813
+ const to = abs(endIdx);
814
+ if (isImage) {
815
+ const node2 = {
816
+ type: "image",
817
+ from,
818
+ to,
819
+ url: cap(url, MAX_URL),
820
+ alt: cap(buf.slice(br + 1, close), MAX_ALT)
821
+ };
822
+ if (title !== void 0) node2.title = cap(title, MAX_TITLE);
823
+ return { node: node2, endIdx };
824
+ }
825
+ const node = {
826
+ type: "link",
827
+ from,
828
+ to,
829
+ url: cap(url, MAX_URL),
830
+ children: parseInlineRange(br + 1, close)
831
+ };
832
+ if (title !== void 0) node.title = cap(title, MAX_TITLE);
833
+ return { node, endIdx };
834
+ }
835
+ function tryEmph(i, end) {
836
+ const c = buf[i];
837
+ let n = 1;
838
+ while (i + n < end && buf[i + n] === c) n++;
839
+ const isStrike = c === "~";
840
+ if (isStrike && n < 2) return null;
841
+ const strength = isStrike ? 2 : n >= 2 ? 2 : 1;
842
+ if (i + strength > end) return null;
843
+ const after = buf[i + strength];
844
+ const before = buf[i - 1];
845
+ if (isWs(after)) return null;
846
+ if (c === "_" && isAlnum(before)) return null;
847
+ let k = i + strength;
848
+ while (k < end) {
849
+ if (buf[k] === c) {
850
+ let m = 1;
851
+ while (k + m < end && buf[k + m] === c) m++;
852
+ if (m >= strength) {
853
+ const prev = buf[k - 1];
854
+ const aft = buf[k + strength];
855
+ if (!isWs(prev) && (c !== "_" || !isAlnum(aft))) {
856
+ const children = parseInlineRange(i + strength, k);
857
+ const from = abs(i);
858
+ const to = abs(k + strength);
859
+ let node;
860
+ if (isStrike) {
861
+ node = { type: "strikethrough", from, to, children };
862
+ } else if (strength === 2) {
863
+ node = { type: "strong", from, to, marker: c.repeat(2), children };
864
+ } else {
865
+ node = { type: "emphasis", from, to, marker: c, children };
866
+ }
867
+ return { node, endIdx: k + strength };
868
+ }
869
+ }
870
+ k += m;
871
+ continue;
872
+ }
873
+ k++;
874
+ }
875
+ return null;
876
+ }
877
+ function parseInlineRange(start, end) {
878
+ const out = [];
879
+ let i = start;
880
+ let textStart = start;
881
+ const flush = (upto) => {
882
+ if (upto > textStart) out.push(text(abs(textStart), abs(upto), buf.slice(textStart, upto)));
883
+ };
884
+ const commit2 = (hit) => {
885
+ flush(i);
886
+ out.push(hit.node);
887
+ i = hit.endIdx;
888
+ textStart = i;
889
+ };
890
+ while (i < end) {
891
+ const c = buf[i];
892
+ if (c === "\\") {
893
+ if (buf[i + 1] === "\n") {
894
+ flush(i);
895
+ out.push({ type: "break", from: abs(i), to: abs(i + 2), hard: true });
896
+ i += 2;
897
+ textStart = i;
898
+ continue;
899
+ }
900
+ i += 2;
901
+ continue;
902
+ }
903
+ if (c === "\n") {
904
+ let sp = 0;
905
+ let p = i - 1;
906
+ while (p >= textStart && buf[p] === " ") {
907
+ sp++;
908
+ p--;
909
+ }
910
+ const hard = sp >= 2;
911
+ flush(hard ? i - sp : i);
912
+ out.push({
913
+ type: "break",
914
+ from: abs(hard ? i - sp : i),
915
+ to: abs(i + 1),
916
+ hard
917
+ });
918
+ i++;
919
+ textStart = i;
920
+ continue;
921
+ }
922
+ if (c === "`") {
923
+ const r = tryCode(i, end);
924
+ if (r) {
925
+ commit2(r);
926
+ continue;
927
+ }
928
+ i++;
929
+ continue;
930
+ }
931
+ if (c === "$" && opts.math) {
932
+ const r = tryMath(i, end);
933
+ if (r) {
934
+ commit2(r);
935
+ continue;
936
+ }
937
+ i++;
938
+ continue;
939
+ }
940
+ if (c === "<") {
941
+ const r = tryAutolink(i, end);
942
+ if (r) {
943
+ commit2(r);
944
+ continue;
945
+ }
946
+ i++;
947
+ continue;
948
+ }
949
+ if (c === "!" && buf[i + 1] === "[") {
950
+ const r = tryLink(i, end, true);
951
+ if (r) {
952
+ commit2(r);
953
+ continue;
954
+ }
955
+ i++;
956
+ continue;
957
+ }
958
+ if (c === "[") {
959
+ if (opts.footnotes && buf[i + 1] === "^") {
960
+ const rf = tryFootnoteRef(i, end);
961
+ if (rf) {
962
+ commit2(rf);
963
+ continue;
964
+ }
965
+ }
966
+ const r = tryLink(i, end, false);
967
+ if (r) {
968
+ commit2(r);
969
+ continue;
970
+ }
971
+ i++;
972
+ continue;
973
+ }
974
+ if (c === "*" || c === "_" || c === "~") {
975
+ const r = tryEmph(i, end);
976
+ if (r) {
977
+ commit2(r);
978
+ continue;
979
+ }
980
+ i++;
981
+ continue;
982
+ }
983
+ i++;
984
+ }
985
+ flush(end);
986
+ return out;
987
+ }
988
+ return parseInlineRange(0, buf.length);
989
+ }
990
+ function parse(src, options) {
991
+ opts = {
992
+ math: options?.math === true,
993
+ footnotes: options?.footnotes === true,
994
+ defLists: options?.defLists === true
995
+ };
996
+ const lines = splitLines(src);
997
+ const children = parseBlocks(lines);
998
+ return { type: "document", from: 0, to: src.length, children };
999
+ }
1000
+ function headSafety(block) {
1001
+ switch (block.type) {
1002
+ case "heading":
1003
+ case "thematicBreak":
1004
+ case "mathBlock":
1005
+ return "always";
1006
+ case "codeBlock":
1007
+ return block.fenced ? "always" : "never";
1008
+ case "paragraph":
1009
+ case "table":
1010
+ case "htmlBlock":
1011
+ case "blockquote":
1012
+ return "needs-blank";
1013
+ default:
1014
+ return "never";
1015
+ }
1016
+ }
1017
+ function prevLineIsBlank(src, lineStart2) {
1018
+ if (lineStart2 <= 0) return false;
1019
+ const end = lineStart2 - 1;
1020
+ let start = end - 1;
1021
+ while (start >= 0 && src[start] !== "\n") start--;
1022
+ start += 1;
1023
+ return src.slice(start, end).trim() === "";
1024
+ }
1025
+ function blankLineFullyAfter(src, lineStart2, minStart) {
1026
+ if (lineStart2 <= 0) return false;
1027
+ const end = lineStart2 - 1;
1028
+ let start = end - 1;
1029
+ while (start >= 0 && src[start] !== "\n") start--;
1030
+ start += 1;
1031
+ if (start < minStart) return false;
1032
+ return src.slice(start, end).trim() === "";
1033
+ }
1034
+ function shiftBlock(block, delta) {
1035
+ const clone = structuredClone(block);
1036
+ walk(clone, (n) => {
1037
+ const p = n;
1038
+ p.from += delta;
1039
+ p.to += delta;
1040
+ if (typeof p.contentFrom === "number") p.contentFrom += delta;
1041
+ });
1042
+ return clone;
1043
+ }
1044
+ function lineStartingAt(lines, off) {
1045
+ let lo = 0;
1046
+ let hi = lines.length - 1;
1047
+ while (lo <= hi) {
1048
+ const mid = lo + hi >> 1;
1049
+ const f = lines[mid].from;
1050
+ if (f === off) return mid;
1051
+ if (f < off) lo = mid + 1;
1052
+ else hi = mid - 1;
1053
+ }
1054
+ return -1;
1055
+ }
1056
+ function lastBlockAtOrBefore(blocks, off) {
1057
+ let lo = 0;
1058
+ let hi = blocks.length - 1;
1059
+ let ans = -1;
1060
+ while (lo <= hi) {
1061
+ const mid = lo + hi >> 1;
1062
+ if (blocks[mid].from <= off) {
1063
+ ans = mid;
1064
+ lo = mid + 1;
1065
+ } else {
1066
+ hi = mid - 1;
1067
+ }
1068
+ }
1069
+ return ans;
1070
+ }
1071
+ var MAX_JOIN_ATTEMPTS = 4;
1072
+ function tryReuseSuffix(blocks, prevSrc, to, delta, nextLines, cLine) {
1073
+ const dEnd = lastBlockAtOrBefore(blocks, to);
1074
+ let attempts = 0;
1075
+ for (let j = dEnd + 1; j < blocks.length; j++) {
1076
+ const joinOff = blocks[j].from;
1077
+ if (joinOff < to) continue;
1078
+ if (!blankLineFullyAfter(prevSrc, joinOff, to)) continue;
1079
+ const nextJoinOff = joinOff + delta;
1080
+ const joinLine = lineStartingAt(nextLines, nextJoinOff);
1081
+ if (joinLine <= cLine) continue;
1082
+ if (++attempts > MAX_JOIN_ATTEMPTS) break;
1083
+ const guard = parseBlocks(nextLines.slice(cLine, joinLine + 1));
1084
+ const dirty = [];
1085
+ let proven = false;
1086
+ for (const b of guard) {
1087
+ if (b.from === nextJoinOff) {
1088
+ proven = true;
1089
+ break;
1090
+ }
1091
+ if (b.from > nextJoinOff) break;
1092
+ dirty.push(b);
1093
+ }
1094
+ if (!proven) continue;
1095
+ const rest = blocks.slice(j);
1096
+ const shifted = rest.map((b) => shiftBlock(b, delta));
1097
+ return { dirty, shifted, endLine: joinLine, count: rest.length };
1098
+ }
1099
+ return null;
1100
+ }
1101
+ function runIncremental(prev, prevSrc, change, nextSrc, options) {
1102
+ const nextLines = splitLines(nextSrc);
1103
+ const total = nextLines.length;
1104
+ const full = () => ({
1105
+ doc: parse(nextSrc, options),
1106
+ stats: {
1107
+ fellBack: true,
1108
+ reusedBlocks: 0,
1109
+ reparsedFromLine: 0,
1110
+ reparsedToLine: total,
1111
+ reusedSuffixBlocks: 0,
1112
+ totalLines: total
1113
+ }
1114
+ });
1115
+ const from = Math.max(0, Math.min(change.from, change.to));
1116
+ const to = Math.min(prevSrc.length, Math.max(change.from, change.to));
1117
+ if (to < from) return full();
1118
+ if (nextSrc.length !== prevSrc.length + change.insert.length - (to - from)) return full();
1119
+ if (prevSrc.slice(0, from) !== nextSrc.slice(0, from)) return full();
1120
+ const delta = change.insert.length - (to - from);
1121
+ const blocks = prev.children;
1122
+ const hi = Math.min(blocks.length - 1, lastBlockAtOrBefore(blocks, from));
1123
+ for (let k = hi; k >= 1; k--) {
1124
+ const cut = blocks[k].from;
1125
+ if (cut > from) continue;
1126
+ const safety = headSafety(blocks[k - 1]);
1127
+ if (safety === "never") continue;
1128
+ if (safety === "needs-blank" && !prevLineIsBlank(prevSrc, cut)) continue;
1129
+ const c = lineStartingAt(nextLines, cut);
1130
+ if (c < 0) continue;
1131
+ opts = {
1132
+ math: options?.math === true,
1133
+ footnotes: options?.footnotes === true,
1134
+ defLists: options?.defLists === true
1135
+ };
1136
+ const suffix = tryReuseSuffix(blocks, prevSrc, to, delta, nextLines, c);
1137
+ if (suffix) {
1138
+ const children2 = blocks.slice(0, k).concat(suffix.dirty, suffix.shifted);
1139
+ return {
1140
+ doc: { type: "document", from: 0, to: nextSrc.length, children: children2 },
1141
+ stats: {
1142
+ fellBack: false,
1143
+ reusedBlocks: k,
1144
+ reparsedFromLine: c,
1145
+ reparsedToLine: suffix.endLine,
1146
+ reusedSuffixBlocks: suffix.count,
1147
+ totalLines: total
1148
+ }
1149
+ };
1150
+ }
1151
+ const tail = parseBlocks(nextLines.slice(c));
1152
+ const children = blocks.slice(0, k).concat(tail);
1153
+ return {
1154
+ doc: { type: "document", from: 0, to: nextSrc.length, children },
1155
+ stats: {
1156
+ fellBack: false,
1157
+ reusedBlocks: k,
1158
+ reparsedFromLine: c,
1159
+ reparsedToLine: total,
1160
+ reusedSuffixBlocks: 0,
1161
+ totalLines: total
1162
+ }
1163
+ };
1164
+ }
1165
+ return full();
1166
+ }
1167
+ function parseIncremental(prev, prevSrc, change, nextSrc, options) {
1168
+ return runIncremental(prev, prevSrc, change, nextSrc, options).doc;
1169
+ }
1170
+
1171
+ // src/core/render.ts
1172
+ var MAX_URL2 = 8192;
1173
+ var SAFE_SCHEMES = /^(https?:|mailto:)/;
1174
+ function newFnCtx() {
1175
+ return { nums: /* @__PURE__ */ new Map() };
1176
+ }
1177
+ function fnNumber(ctx, slug) {
1178
+ const existing = ctx.nums.get(slug);
1179
+ if (existing !== void 0) return existing;
1180
+ const n = ctx.nums.size + 1;
1181
+ ctx.nums.set(slug, n);
1182
+ return n;
1183
+ }
1184
+ function escapeHtml(value) {
1185
+ let out = "";
1186
+ for (let i = 0; i < value.length; i++) {
1187
+ const ch = value.charCodeAt(i);
1188
+ switch (ch) {
1189
+ case 38:
1190
+ out += "&amp;";
1191
+ break;
1192
+ case 60:
1193
+ out += "&lt;";
1194
+ break;
1195
+ case 62:
1196
+ out += "&gt;";
1197
+ break;
1198
+ case 34:
1199
+ out += "&quot;";
1200
+ break;
1201
+ default:
1202
+ out += value[i];
1203
+ }
1204
+ }
1205
+ return out;
1206
+ }
1207
+ function slugFootnoteId(id) {
1208
+ const slug = id.replace(/[^A-Za-z0-9_-]+/g, "-");
1209
+ return slug === "" ? "_" : slug;
1210
+ }
1211
+ function safeUrl(url) {
1212
+ if (typeof url !== "string" || url.length === 0) return "#";
1213
+ const trimmed = url.trim().slice(0, MAX_URL2);
1214
+ if (trimmed === "") return "#";
1215
+ const probe = trimmed.replace(/[\u0000-\u0020\u007f]/g, "").toLowerCase();
1216
+ if (probe === "") return "#";
1217
+ if (SAFE_SCHEMES.test(probe)) return trimmed;
1218
+ if (/^[a-z][a-z0-9+.-]*:/.test(probe)) return "#";
1219
+ return trimmed;
1220
+ }
1221
+ function attrUrl(url) {
1222
+ return escapeHtml(safeUrl(url));
1223
+ }
1224
+ function renderInline(nodes, options) {
1225
+ return inlineRun(nodes, options, newFnCtx());
1226
+ }
1227
+ function inlineRun(nodes, options, ctx) {
1228
+ let out = "";
1229
+ for (const node of nodes) out += renderInlineNode(node, options, ctx);
1230
+ return out;
1231
+ }
1232
+ function renderInlineNode(node, options, ctx) {
1233
+ switch (node.type) {
1234
+ case "text":
1235
+ return escapeHtml(node.value);
1236
+ case "strong":
1237
+ return `<strong>${inlineRun(node.children, options, ctx)}</strong>`;
1238
+ case "emphasis":
1239
+ return `<em>${inlineRun(node.children, options, ctx)}</em>`;
1240
+ case "strikethrough":
1241
+ return `<del>${inlineRun(node.children, options, ctx)}</del>`;
1242
+ case "inlineCode":
1243
+ return `<code>${escapeHtml(node.value)}</code>`;
1244
+ case "link": {
1245
+ const title = node.title ? ` title="${escapeHtml(node.title)}"` : "";
1246
+ return `<a href="${attrUrl(node.url)}"${title}>${inlineRun(node.children, options, ctx)}</a>`;
1247
+ }
1248
+ case "image": {
1249
+ const title = node.title ? ` title="${escapeHtml(node.title)}"` : "";
1250
+ return `<img src="${attrUrl(node.url)}" alt="${escapeHtml(node.alt)}"${title}>`;
1251
+ }
1252
+ case "autolink":
1253
+ return `<a href="${attrUrl(node.url)}">${escapeHtml(node.url)}</a>`;
1254
+ case "break":
1255
+ return node.hard ? "<br>\n" : "\n";
1256
+ case "math":
1257
+ return options?.math ? options.math(node.value, node.display) : `<span class="tw-math-src">${escapeHtml(node.value)}</span>`;
1258
+ case "footnoteRef": {
1259
+ const slug = slugFootnoteId(node.id);
1260
+ const n = fnNumber(ctx, slug);
1261
+ const safe = escapeHtml(slug);
1262
+ return `<sup class="tw-fnref" id="fnref-${safe}"><a href="#fn-${safe}">[${n}]</a></sup>`;
1263
+ }
1264
+ default: {
1265
+ const _never = node;
1266
+ return _never;
1267
+ }
1268
+ }
1269
+ }
1270
+ function renderNode(node, options) {
1271
+ return renderBlockNode(node, options, newFnCtx());
1272
+ }
1273
+ function renderBlockNode(node, options, ctx) {
1274
+ switch (node.type) {
1275
+ case "heading": {
1276
+ const l = node.level;
1277
+ return `<h${l}>${inlineRun(node.children, options, ctx)}</h${l}>`;
1278
+ }
1279
+ case "paragraph":
1280
+ return `<p>${inlineRun(node.children, options, ctx)}</p>`;
1281
+ case "blockquote":
1282
+ return `<blockquote>${renderBlocks(node.children, options, ctx)}</blockquote>`;
1283
+ case "list":
1284
+ return renderList(node.ordered, node.start, node.tight, node.items, options, ctx);
1285
+ case "codeBlock": {
1286
+ const cls = node.lang ? ` class="language-${escapeHtml(node.lang)}"` : "";
1287
+ const body = options?.highlight ? options.highlight(node.lang, node.value) : escapeHtml(node.value);
1288
+ return `<pre><code${cls}>${body}</code></pre>`;
1289
+ }
1290
+ case "thematicBreak":
1291
+ return "<hr>";
1292
+ case "table":
1293
+ return renderTable(node, options, ctx);
1294
+ case "htmlBlock":
1295
+ return `<pre>${escapeHtml(node.value)}</pre>`;
1296
+ case "mathBlock":
1297
+ return options?.math ? options.math(node.value, true) : `<div class="tw-math-src">${escapeHtml(node.value)}</div>`;
1298
+ case "footnoteDef":
1299
+ return `<div class="tw-footnote-def">${renderBlocks(node.children, options, ctx)}</div>`;
1300
+ case "defList": {
1301
+ let out = "";
1302
+ for (const item of node.items) {
1303
+ out += `<dt>${inlineRun(item.term, options, ctx)}</dt>`;
1304
+ for (const def of item.definitions) {
1305
+ out += `<dd>${renderBlocks(def, options, ctx)}</dd>`;
1306
+ }
1307
+ }
1308
+ return `<dl>${out}</dl>`;
1309
+ }
1310
+ default: {
1311
+ const _never = node;
1312
+ return _never;
1313
+ }
1314
+ }
1315
+ }
1316
+ function renderBlocks(blocks, options, ctx) {
1317
+ let out = "";
1318
+ for (const b of blocks) {
1319
+ if (b.type === "footnoteDef") continue;
1320
+ out += renderBlockNode(b, options, ctx);
1321
+ }
1322
+ return out;
1323
+ }
1324
+ function renderList(ordered, start, tight, items, options, ctx) {
1325
+ const tag = ordered ? "ol" : "ul";
1326
+ const startAttr = ordered && Number.isFinite(start) && start !== 1 ? ` start="${escapeHtml(String(Math.trunc(start)))}"` : "";
1327
+ let body = "";
1328
+ for (const item of items) body += renderListItem(item, tight, options, ctx);
1329
+ return `<${tag}${startAttr}>${body}</${tag}>`;
1330
+ }
1331
+ function renderListItem(item, tight, options, ctx) {
1332
+ let checkbox = "";
1333
+ if (item.task !== null) {
1334
+ const checked = item.task === "checked" ? " checked" : "";
1335
+ checkbox = `<input type="checkbox" disabled${checked}> `;
1336
+ }
1337
+ return `<li>${checkbox}${renderItemChildren(item.children, tight, options, ctx)}</li>`;
1338
+ }
1339
+ function renderItemChildren(children, tight, options, ctx) {
1340
+ if (tight) {
1341
+ let out = "";
1342
+ for (const child of children) {
1343
+ out += child.type === "paragraph" ? inlineRun(child.children, options, ctx) : renderBlockNode(child, options, ctx);
1344
+ }
1345
+ return out;
1346
+ }
1347
+ return renderBlocks(children, options, ctx);
1348
+ }
1349
+ function renderTable(node, options, ctx) {
1350
+ const align = node.align;
1351
+ const cellStyle = (col) => {
1352
+ const a = align[col];
1353
+ return a ? ` style="text-align:${a}"` : "";
1354
+ };
1355
+ const headCells = renderRow(node.header, "th", cellStyle, options, ctx);
1356
+ const head = `<thead><tr>${headCells}</tr></thead>`;
1357
+ let bodyRows = "";
1358
+ for (const row of node.rows) {
1359
+ bodyRows += `<tr>${renderRow(row, "td", cellStyle, options, ctx)}</tr>`;
1360
+ }
1361
+ const body = `<tbody>${bodyRows}</tbody>`;
1362
+ return `<table>${head}${body}</table>`;
1363
+ }
1364
+ function renderRow(cells, tag, cellStyle, options, ctx) {
1365
+ let out = "";
1366
+ for (let i = 0; i < cells.length; i++) {
1367
+ const cell = cells[i];
1368
+ out += `<${tag}${cellStyle(i)}>${inlineRun(cell.children, options, ctx)}</${tag}>`;
1369
+ }
1370
+ return out;
1371
+ }
1372
+ function collectFootnoteDefs(doc) {
1373
+ const defs = [];
1374
+ walk(doc, (n) => {
1375
+ if (n.type === "footnoteDef") {
1376
+ defs.push(n);
1377
+ return false;
1378
+ }
1379
+ return true;
1380
+ });
1381
+ return defs;
1382
+ }
1383
+ function renderToHtml(doc, options) {
1384
+ const ctx = newFnCtx();
1385
+ let out = renderBlocks(doc.children, options, ctx);
1386
+ const defs = collectFootnoteDefs(doc);
1387
+ if (defs.length > 0) {
1388
+ let items = "";
1389
+ for (const def of defs) {
1390
+ const safe = escapeHtml(slugFootnoteId(def.id));
1391
+ items += `<li id="fn-${safe}">` + renderBlocks(def.children, options, ctx) + ` <a href="#fnref-${safe}" class="tw-fn-back">\u21A9</a></li>`;
1392
+ }
1393
+ out += `<section class="tw-footnotes"><ol>${items}</ol></section>`;
1394
+ }
1395
+ return out;
1396
+ }
1397
+
1398
+ // src/core/unified.ts
1399
+ function collectMarkers(doc, source) {
1400
+ const markers = [];
1401
+ const push = (from, to, kind) => {
1402
+ if (to > from) markers.push({ from, to, kind });
1403
+ };
1404
+ walk(doc, (node) => {
1405
+ switch (node.type) {
1406
+ case "heading": {
1407
+ push(node.from, node.contentFrom, "heading");
1408
+ break;
1409
+ }
1410
+ case "emphasis":
1411
+ case "strong":
1412
+ case "strikethrough": {
1413
+ const kind = node.type === "strikethrough" ? "strike" : node.type;
1414
+ const kids = node.children;
1415
+ const first = kids[0];
1416
+ const last = kids[kids.length - 1];
1417
+ const innerFrom = first ? first.from : node.to;
1418
+ const innerTo = last ? last.to : node.from;
1419
+ push(node.from, innerFrom, kind);
1420
+ push(innerTo, node.to, kind);
1421
+ break;
1422
+ }
1423
+ case "inlineCode": {
1424
+ const ticks = Math.max(1, node.ticks);
1425
+ const openTo = Math.min(node.from + ticks, node.to);
1426
+ const closeFrom = Math.max(node.to - ticks, openTo);
1427
+ push(node.from, openTo, "code");
1428
+ push(closeFrom, node.to, "code");
1429
+ break;
1430
+ }
1431
+ case "link": {
1432
+ const kids = node.children;
1433
+ const last = kids[kids.length - 1];
1434
+ const textEnd = last ? last.to : node.from + 1;
1435
+ const midTo = Math.min(textEnd + 2, node.to);
1436
+ push(node.from, Math.min(node.from + 1, node.to), "link");
1437
+ push(textEnd, midTo, "link");
1438
+ push(midTo, node.to - 1, "link");
1439
+ push(Math.max(node.to - 1, node.from), node.to, "link");
1440
+ break;
1441
+ }
1442
+ case "image": {
1443
+ const bracketOpen = node.from + 1;
1444
+ const altStart = bracketOpen + 1;
1445
+ const altEnd = Math.min(altStart + node.alt.length, node.to);
1446
+ const midTo = Math.min(altEnd + 2, node.to);
1447
+ push(node.from, Math.min(node.from + 1, node.to), "image");
1448
+ push(bracketOpen, Math.min(bracketOpen + 1, node.to), "image");
1449
+ push(altEnd, midTo, "image");
1450
+ push(midTo, node.to - 1, "image");
1451
+ push(Math.max(node.to - 1, node.from), node.to, "image");
1452
+ break;
1453
+ }
1454
+ case "listItem": {
1455
+ push(node.from, node.contentFrom, "listMarker");
1456
+ break;
1457
+ }
1458
+ case "math": {
1459
+ const delim = node.display ? 2 : 1;
1460
+ const openTo = Math.min(node.from + delim, node.to);
1461
+ const closeFrom = Math.max(node.to - delim, openTo);
1462
+ push(node.from, openTo, "math");
1463
+ push(closeFrom, node.to, "math");
1464
+ break;
1465
+ }
1466
+ case "footnoteRef": {
1467
+ const openTo = Math.min(node.from + 2, node.to);
1468
+ const closeFrom = Math.max(node.to - 1, openTo);
1469
+ push(node.from, openTo, "footnoteRef");
1470
+ push(closeFrom, node.to, "footnoteRef");
1471
+ break;
1472
+ }
1473
+ case "codeBlock": {
1474
+ if (!source || !node.fenced) break;
1475
+ let openTo = source.indexOf("\n", node.from);
1476
+ if (openTo === -1 || openTo > node.to) openTo = node.to;
1477
+ if (openTo > node.from && source[openTo - 1] === "\r") openTo--;
1478
+ push(node.from, openTo, "fence");
1479
+ const closeStart = source.lastIndexOf("\n", node.to - 1) + 1;
1480
+ if (closeStart > node.from) {
1481
+ const closeText = source.slice(closeStart, node.to);
1482
+ const cm = /^( {0,3})(`{3,}|~{3,}) *$/.exec(closeText);
1483
+ const om = /^ {0,3}([`~])/.exec(source.slice(node.from, openTo));
1484
+ if (cm && om && cm[2][0] === om[1]) push(closeStart, node.to, "fence");
1485
+ }
1486
+ break;
1487
+ }
1488
+ case "blockquote": {
1489
+ if (!source) break;
1490
+ const lineStart2 = source.lastIndexOf("\n", node.from - 1) + 1;
1491
+ if (lineStart2 !== node.from) break;
1492
+ let p = node.from;
1493
+ while (p < node.to) {
1494
+ let end = source.indexOf("\n", p);
1495
+ if (end === -1 || end > node.to) end = node.to;
1496
+ const qm = /^(?: {0,3}> ?)+/.exec(source.slice(p, end));
1497
+ if (qm) push(p, p + qm[0].length, "blockquote");
1498
+ p = end + 1;
1499
+ }
1500
+ break;
1501
+ }
1502
+ }
1503
+ return true;
1504
+ });
1505
+ return markers;
1506
+ }
1507
+ function hiddenMarkers(doc, sel, source) {
1508
+ const lo = Math.min(sel.from, sel.to) - 1;
1509
+ const hi = Math.max(sel.from, sel.to) + 1;
1510
+ const intersects = (m) => m.from < hi && lo < m.to;
1511
+ return collectMarkers(doc, source).filter((m) => !intersects(m));
1512
+ }
1513
+ function activeBlockIndex(doc, offset) {
1514
+ const blocks = doc.children;
1515
+ for (let i = 0; i < blocks.length; i++) {
1516
+ const b = blocks[i];
1517
+ if (b && offset >= b.from && offset <= b.to) return i;
1518
+ }
1519
+ return -1;
1520
+ }
1521
+
1522
+ // src/core/fold.ts
1523
+ function headingFoldRanges(doc) {
1524
+ const blocks = doc.children;
1525
+ const ranges = [];
1526
+ for (let i = 0; i < blocks.length; i++) {
1527
+ const h = blocks[i];
1528
+ if (!h || h.type !== "heading") continue;
1529
+ let to = doc.to;
1530
+ for (let j = i + 1; j < blocks.length; j++) {
1531
+ const n = blocks[j];
1532
+ if (n && n.type === "heading" && n.level <= h.level) {
1533
+ to = n.from;
1534
+ break;
1535
+ }
1536
+ }
1537
+ const from = h.to;
1538
+ if (to > from) {
1539
+ ranges.push({ headingFrom: h.from, level: h.level, from, to });
1540
+ }
1541
+ }
1542
+ return ranges;
1543
+ }
1544
+
1545
+ // src/core/commands.ts
1546
+ var COMMANDS = [
1547
+ { id: "bold", label: "Bold", kbd: "\u2318B", group: "inline" },
1548
+ { id: "italic", label: "Italic", kbd: "\u2318I", group: "inline" },
1549
+ { id: "strikethrough", label: "Strikethrough", group: "inline" },
1550
+ { id: "inlineCode", label: "Inline Code", kbd: "\u2318E", group: "inline" },
1551
+ { id: "link", label: "Link", kbd: "\u2318K", group: "inline" },
1552
+ { id: "heading1", label: "Heading 1", group: "block" },
1553
+ { id: "heading2", label: "Heading 2", group: "block" },
1554
+ { id: "heading3", label: "Heading 3", group: "block" },
1555
+ { id: "heading4", label: "Heading 4", group: "block" },
1556
+ { id: "heading5", label: "Heading 5", group: "block" },
1557
+ { id: "heading6", label: "Heading 6", group: "block" },
1558
+ { id: "bulletList", label: "Bullet List", group: "block" },
1559
+ { id: "orderedList", label: "Ordered List", group: "block" },
1560
+ { id: "taskList", label: "Task List", group: "block" },
1561
+ { id: "quote", label: "Quote", group: "block" },
1562
+ { id: "horizontalRule", label: "Horizontal Rule", group: "insert" },
1563
+ { id: "codeBlock", label: "Code Block", group: "insert" },
1564
+ { id: "table", label: "Table", group: "insert" }
1565
+ ];
1566
+ function norm(sel) {
1567
+ return { from: Math.min(sel.from, sel.to), to: Math.max(sel.from, sel.to) };
1568
+ }
1569
+ function toggleWrap(text, sel, marker) {
1570
+ const len = marker.length;
1571
+ const { from, to } = norm(sel);
1572
+ if (from >= len && text.slice(from - len, from) === marker && text.slice(to, to + len) === marker) {
1573
+ const t2 = text.slice(0, from - len) + text.slice(from, to) + text.slice(to + len);
1574
+ return { text: t2, selection: { from: from - len, to: to - len } };
1575
+ }
1576
+ const selected = text.slice(from, to);
1577
+ if (selected.length >= 2 * len && selected.startsWith(marker) && selected.endsWith(marker)) {
1578
+ const inner = selected.slice(len, selected.length - len);
1579
+ const t2 = text.slice(0, from) + inner + text.slice(to);
1580
+ return { text: t2, selection: { from, to: from + inner.length } };
1581
+ }
1582
+ const t = text.slice(0, from) + marker + selected + marker + text.slice(to);
1583
+ if (from === to) return { text: t, selection: { from: from + len, to: from + len } };
1584
+ return { text: t, selection: { from: from + len, to: to + len } };
1585
+ }
1586
+ function insertLink(text, sel) {
1587
+ const { from, to } = norm(sel);
1588
+ const label = text.slice(from, to) || "text";
1589
+ const url = "https://";
1590
+ const inserted = `[${label}](${url})`;
1591
+ const t = text.slice(0, from) + inserted + text.slice(to);
1592
+ const urlStart = from + label.length + 3;
1593
+ return { text: t, selection: { from: urlStart, to: urlStart + url.length } };
1594
+ }
1595
+ var HEADING_RE = /^ {0,3}#{1,6}\s+/;
1596
+ var LIST_RE = /^ {0,3}(?:[-*+]\s(?:\[[ xX]\]\s)?|\d+[.)]\s)/;
1597
+ var QUOTE_RE = /^ {0,3}>\s?/;
1598
+ function applyLinePrefix(text, sel, p) {
1599
+ const { from, to } = norm(sel);
1600
+ const start = text.lastIndexOf("\n", from - 1) + 1;
1601
+ const searchFrom = to > from ? to - 1 : to;
1602
+ let end = text.indexOf("\n", searchFrom);
1603
+ if (end === -1) end = text.length;
1604
+ const lines = text.slice(start, end).split("\n");
1605
+ const allHave = lines.every((l) => l.trim() === "" || p.has(l));
1606
+ const out = lines.map((l, i) => l.trim() === "" ? l : allHave ? p.strip(l) : p.add(p.strip(l), i)).join("\n");
1607
+ const t = text.slice(0, start) + out + text.slice(end);
1608
+ return { text: t, selection: { from: start, to: start + out.length } };
1609
+ }
1610
+ function stripAllPrefixes(line) {
1611
+ return line.replace(HEADING_RE, "").replace(LIST_RE, "").replace(QUOTE_RE, "");
1612
+ }
1613
+ function insertBlock(text, sel, block, selectInner) {
1614
+ const { from, to } = norm(sel);
1615
+ const before = text.slice(0, from);
1616
+ const after = text.slice(to);
1617
+ const lead = before === "" || before.endsWith("\n\n") ? "" : before.endsWith("\n") ? "\n" : "\n\n";
1618
+ const trail = after === "" || after.startsWith("\n\n") ? "" : after.startsWith("\n") ? "\n" : "\n\n";
1619
+ const insertedAt = from + lead.length;
1620
+ const t = before + lead + block + trail + after;
1621
+ const selection = selectInner ? { from: insertedAt + selectInner[0], to: insertedAt + selectInner[1] } : { from: insertedAt, to: insertedAt + block.length };
1622
+ return { text: t, selection };
1623
+ }
1624
+ function applyCommand(text, sel, cmd) {
1625
+ switch (cmd) {
1626
+ case "bold":
1627
+ return toggleWrap(text, sel, "**");
1628
+ case "italic":
1629
+ return toggleWrap(text, sel, "*");
1630
+ case "strikethrough":
1631
+ return toggleWrap(text, sel, "~~");
1632
+ case "inlineCode":
1633
+ return toggleWrap(text, sel, "`");
1634
+ case "link":
1635
+ return insertLink(text, sel);
1636
+ case "heading1":
1637
+ case "heading2":
1638
+ case "heading3":
1639
+ case "heading4":
1640
+ case "heading5":
1641
+ case "heading6": {
1642
+ const level = cmd === "heading1" ? 1 : cmd === "heading2" ? 2 : cmd === "heading3" ? 3 : cmd === "heading4" ? 4 : cmd === "heading5" ? 5 : 6;
1643
+ const marker = "#".repeat(level) + " ";
1644
+ return applyLinePrefix(text, sel, {
1645
+ has: (l) => new RegExp(`^ {0,3}#{${level}}\\s`).test(l),
1646
+ strip: (l) => l.replace(HEADING_RE, ""),
1647
+ add: (l) => marker + l
1648
+ });
1649
+ }
1650
+ case "bulletList":
1651
+ return applyLinePrefix(text, sel, {
1652
+ has: (l) => /^ {0,3}[-*+]\s/.test(l),
1653
+ strip: stripAllPrefixes,
1654
+ add: (l) => "- " + l
1655
+ });
1656
+ case "orderedList":
1657
+ return applyLinePrefix(text, sel, {
1658
+ has: (l) => /^ {0,3}\d+[.)]\s/.test(l),
1659
+ strip: stripAllPrefixes,
1660
+ add: (l, i) => `${i + 1}. ` + l
1661
+ });
1662
+ case "taskList":
1663
+ return applyLinePrefix(text, sel, {
1664
+ has: (l) => /^ {0,3}[-*+]\s\[[ xX]\]\s/.test(l),
1665
+ strip: stripAllPrefixes,
1666
+ add: (l) => "- [ ] " + l
1667
+ });
1668
+ case "quote":
1669
+ return applyLinePrefix(text, sel, {
1670
+ has: (l) => QUOTE_RE.test(l),
1671
+ strip: (l) => l.replace(QUOTE_RE, ""),
1672
+ add: (l) => "> " + l
1673
+ });
1674
+ case "horizontalRule":
1675
+ return insertBlock(text, sel, "---");
1676
+ case "codeBlock": {
1677
+ const selected = text.slice(norm(sel).from, norm(sel).to);
1678
+ const block = "```\n" + selected + "\n```";
1679
+ return insertBlock(text, sel, block, selected ? void 0 : [4, 4]);
1680
+ }
1681
+ case "table": {
1682
+ const block = "| Column | Column |\n| --- | --- |\n| Cell | Cell |";
1683
+ return insertBlock(text, sel, block, [2, 8]);
1684
+ }
1685
+ default: {
1686
+ return { text, selection: norm(sel) };
1687
+ }
1688
+ }
1689
+ }
1690
+
1691
+ // src/core/text.ts
1692
+ var TextDoc = class _TextDoc {
1693
+ text;
1694
+ _lineStarts = null;
1695
+ constructor(text = "") {
1696
+ this.text = text;
1697
+ }
1698
+ get length() {
1699
+ return this.text.length;
1700
+ }
1701
+ /** Apply one change, returning a new document. */
1702
+ apply(change) {
1703
+ const { from, to, insert } = normalize(change, this.text.length);
1704
+ return new _TextDoc(this.text.slice(0, from) + insert + this.text.slice(to));
1705
+ }
1706
+ /**
1707
+ * Apply several changes, returning a new document. Changes are given in
1708
+ * document coordinates of THIS doc; they are applied right-to-left so earlier
1709
+ * offsets stay valid.
1710
+ */
1711
+ applyAll(changes) {
1712
+ const sorted = [...changes].sort((a, b) => b.from - a.from);
1713
+ let text = this.text;
1714
+ for (const c of sorted) {
1715
+ const { from, to, insert } = normalize(c, text.length);
1716
+ text = text.slice(0, from) + insert + text.slice(to);
1717
+ }
1718
+ return new _TextDoc(text);
1719
+ }
1720
+ lineStarts() {
1721
+ if (this._lineStarts) return this._lineStarts;
1722
+ const starts = [0];
1723
+ for (let i = 0; i < this.text.length; i++) {
1724
+ if (this.text.charCodeAt(i) === 10) starts.push(i + 1);
1725
+ }
1726
+ this._lineStarts = starts;
1727
+ return starts;
1728
+ }
1729
+ /** Total number of lines (a trailing newline yields an extra empty line). */
1730
+ get lines() {
1731
+ return this.lineStarts().length;
1732
+ }
1733
+ /** The line (1-based) containing `offset`. */
1734
+ lineAt(offset) {
1735
+ const off = clamp(offset, 0, this.text.length);
1736
+ const starts = this.lineStarts();
1737
+ let lo = 0;
1738
+ let hi = starts.length - 1;
1739
+ while (lo < hi) {
1740
+ const mid = lo + hi + 1 >> 1;
1741
+ if (starts[mid] <= off) lo = mid;
1742
+ else hi = mid - 1;
1743
+ }
1744
+ const from = starts[lo];
1745
+ const nextStart = starts[lo + 1];
1746
+ const to = nextStart === void 0 ? this.text.length : nextStart - 1;
1747
+ return { number: lo + 1, from, to, text: this.text.slice(from, to) };
1748
+ }
1749
+ /** Line/column (both 1-based) for an offset. */
1750
+ positionAt(offset) {
1751
+ const line = this.lineAt(offset);
1752
+ return { line: line.number, column: clamp(offset, 0, this.text.length) - line.from + 1 };
1753
+ }
1754
+ /** Offset for a 1-based line/column. */
1755
+ offsetAt(line, column) {
1756
+ const starts = this.lineStarts();
1757
+ const idx = clamp(line, 1, starts.length) - 1;
1758
+ const from = starts[idx];
1759
+ const nextStart = starts[idx + 1];
1760
+ const to = nextStart === void 0 ? this.text.length : nextStart - 1;
1761
+ return clamp(from + (column - 1), from, to);
1762
+ }
1763
+ /**
1764
+ * Map an offset from this document's coordinates to the coordinates AFTER the
1765
+ * given change(s). `assoc` controls which side of an insertion at the offset
1766
+ * the mapped position sticks to: -1 = before (default), 1 = after.
1767
+ */
1768
+ mapOffset(offset, changes, assoc = -1) {
1769
+ const list = Array.isArray(changes) ? changes : [changes];
1770
+ const ordered = [...list].sort((a, b) => a.from - b.from);
1771
+ let result = offset;
1772
+ for (const c of ordered) {
1773
+ const { from, to, insert } = normalize(c, Number.MAX_SAFE_INTEGER);
1774
+ const delta = insert.length - (to - from);
1775
+ if (result < from) continue;
1776
+ if (result > to) {
1777
+ result += delta;
1778
+ } else if (result === from && assoc < 0) ; else if (result === to && assoc > 0) {
1779
+ result += delta;
1780
+ } else {
1781
+ result = assoc < 0 ? from : from + insert.length;
1782
+ }
1783
+ }
1784
+ return result;
1785
+ }
1786
+ toString() {
1787
+ return this.text;
1788
+ }
1789
+ };
1790
+ function normalize(change, len) {
1791
+ let from = clamp(Math.min(change.from, change.to), 0, len);
1792
+ let to = clamp(Math.max(change.from, change.to), 0, len);
1793
+ return { from, to, insert: change.insert };
1794
+ }
1795
+ function clamp(n, lo, hi) {
1796
+ return n < lo ? lo : n > hi ? hi : n;
1797
+ }
1798
+
1799
+ // src/core/highlight.ts
1800
+ function escapeHtml2(value) {
1801
+ let out = "";
1802
+ for (let i = 0; i < value.length; i++) {
1803
+ const ch = value.charCodeAt(i);
1804
+ switch (ch) {
1805
+ case 38:
1806
+ out += "&amp;";
1807
+ break;
1808
+ case 60:
1809
+ out += "&lt;";
1810
+ break;
1811
+ case 62:
1812
+ out += "&gt;";
1813
+ break;
1814
+ case 34:
1815
+ out += "&quot;";
1816
+ break;
1817
+ default:
1818
+ out += value.charAt(i);
1819
+ }
1820
+ }
1821
+ return out;
1822
+ }
1823
+ function span(kind, text) {
1824
+ return `<span class="tw-tok-${kind}">${escapeHtml2(text)}</span>`;
1825
+ }
1826
+ function isDigit(ch) {
1827
+ return ch >= 48 && ch <= 57;
1828
+ }
1829
+ function isIdentStart(ch) {
1830
+ return ch >= 65 && ch <= 90 || // A-Z
1831
+ ch >= 97 && ch <= 122 || // a-z
1832
+ ch === 95 || // _
1833
+ ch === 36;
1834
+ }
1835
+ function isIdentPart(ch) {
1836
+ return isIdentStart(ch) || isDigit(ch);
1837
+ }
1838
+ function isNumberPart(ch) {
1839
+ return isDigit(ch) || ch === 46 || // .
1840
+ ch === 95 || // _ separator
1841
+ ch === 120 || // x
1842
+ ch === 88 || // X
1843
+ ch === 111 || // o
1844
+ ch === 79 || // O
1845
+ ch === 98 || // b
1846
+ ch === 66 || // B
1847
+ ch >= 97 && ch <= 102 || // a-f (hex + exponent 'e')
1848
+ ch >= 65 && ch <= 70;
1849
+ }
1850
+ function isPunct(ch) {
1851
+ switch (ch) {
1852
+ case 123:
1853
+ // {
1854
+ case 125:
1855
+ // }
1856
+ case 40:
1857
+ // (
1858
+ case 41:
1859
+ // )
1860
+ case 91:
1861
+ // [
1862
+ case 93:
1863
+ // ]
1864
+ case 60:
1865
+ // <
1866
+ case 62:
1867
+ // >
1868
+ case 61:
1869
+ // =
1870
+ case 43:
1871
+ // +
1872
+ case 45:
1873
+ // -
1874
+ case 42:
1875
+ // *
1876
+ case 47:
1877
+ // /
1878
+ case 37:
1879
+ // %
1880
+ case 33:
1881
+ // !
1882
+ case 63:
1883
+ // ?
1884
+ case 58:
1885
+ // :
1886
+ case 59:
1887
+ // ;
1888
+ case 44:
1889
+ // ,
1890
+ case 46:
1891
+ // .
1892
+ case 38:
1893
+ // &
1894
+ case 124:
1895
+ // |
1896
+ case 94:
1897
+ // ^
1898
+ case 126:
1899
+ // ~
1900
+ case 64:
1901
+ return true;
1902
+ default:
1903
+ return false;
1904
+ }
1905
+ }
1906
+ function prevNonSpace(code, i) {
1907
+ let k = i - 1;
1908
+ while (k >= 0) {
1909
+ const c = code.charCodeAt(k);
1910
+ if (c === 32 || c === 9 || c === 10 || c === 13) {
1911
+ k--;
1912
+ continue;
1913
+ }
1914
+ return c;
1915
+ }
1916
+ return 0;
1917
+ }
1918
+ function consumeString(code, start, quote) {
1919
+ const n = code.length;
1920
+ let j = start + 1;
1921
+ while (j < n) {
1922
+ const c = code.charCodeAt(j);
1923
+ if (c === 92) {
1924
+ j += 2;
1925
+ continue;
1926
+ }
1927
+ if (c === quote) {
1928
+ return j + 1;
1929
+ }
1930
+ if (quote !== 96 && c === 10) {
1931
+ return j;
1932
+ }
1933
+ j++;
1934
+ }
1935
+ return n;
1936
+ }
1937
+ var JS_KEYWORDS = /* @__PURE__ */ new Set([
1938
+ "abstract",
1939
+ "any",
1940
+ "as",
1941
+ "asserts",
1942
+ "async",
1943
+ "await",
1944
+ "break",
1945
+ "case",
1946
+ "catch",
1947
+ "class",
1948
+ "const",
1949
+ "continue",
1950
+ "debugger",
1951
+ "declare",
1952
+ "default",
1953
+ "delete",
1954
+ "do",
1955
+ "else",
1956
+ "enum",
1957
+ "export",
1958
+ "extends",
1959
+ "false",
1960
+ "finally",
1961
+ "for",
1962
+ "from",
1963
+ "function",
1964
+ "get",
1965
+ "if",
1966
+ "implements",
1967
+ "import",
1968
+ "in",
1969
+ "infer",
1970
+ "instanceof",
1971
+ "interface",
1972
+ "is",
1973
+ "keyof",
1974
+ "let",
1975
+ "namespace",
1976
+ "new",
1977
+ "null",
1978
+ "of",
1979
+ "override",
1980
+ "package",
1981
+ "private",
1982
+ "protected",
1983
+ "public",
1984
+ "readonly",
1985
+ "return",
1986
+ "satisfies",
1987
+ "set",
1988
+ "static",
1989
+ "super",
1990
+ "switch",
1991
+ "this",
1992
+ "throw",
1993
+ "true",
1994
+ "try",
1995
+ "type",
1996
+ "typeof",
1997
+ "undefined",
1998
+ "var",
1999
+ "void",
2000
+ "while",
2001
+ "with",
2002
+ "yield"
2003
+ ]);
2004
+ var TS_TYPES = /* @__PURE__ */ new Set([
2005
+ "string",
2006
+ "number",
2007
+ "boolean",
2008
+ "object",
2009
+ "symbol",
2010
+ "bigint",
2011
+ "unknown",
2012
+ "never",
2013
+ "Array",
2014
+ "Record",
2015
+ "Promise",
2016
+ "Partial",
2017
+ "Required",
2018
+ "Readonly",
2019
+ "Pick",
2020
+ "Omit",
2021
+ "Map",
2022
+ "Set",
2023
+ "WeakMap",
2024
+ "WeakSet",
2025
+ "Date",
2026
+ "RegExp",
2027
+ "Error"
2028
+ ]);
2029
+ var PY_KEYWORDS = /* @__PURE__ */ new Set([
2030
+ "and",
2031
+ "as",
2032
+ "assert",
2033
+ "async",
2034
+ "await",
2035
+ "break",
2036
+ "case",
2037
+ "class",
2038
+ "continue",
2039
+ "def",
2040
+ "del",
2041
+ "elif",
2042
+ "else",
2043
+ "except",
2044
+ "False",
2045
+ "finally",
2046
+ "for",
2047
+ "from",
2048
+ "global",
2049
+ "if",
2050
+ "import",
2051
+ "in",
2052
+ "is",
2053
+ "lambda",
2054
+ "match",
2055
+ "None",
2056
+ "nonlocal",
2057
+ "not",
2058
+ "or",
2059
+ "pass",
2060
+ "raise",
2061
+ "return",
2062
+ "True",
2063
+ "try",
2064
+ "while",
2065
+ "with",
2066
+ "yield"
2067
+ ]);
2068
+ var BASH_KEYWORDS = /* @__PURE__ */ new Set([
2069
+ "if",
2070
+ "then",
2071
+ "else",
2072
+ "elif",
2073
+ "fi",
2074
+ "for",
2075
+ "while",
2076
+ "until",
2077
+ "do",
2078
+ "done",
2079
+ "case",
2080
+ "esac",
2081
+ "in",
2082
+ "function",
2083
+ "select",
2084
+ "time",
2085
+ "return",
2086
+ "break",
2087
+ "continue",
2088
+ "echo",
2089
+ "cd",
2090
+ "export",
2091
+ "local",
2092
+ "read",
2093
+ "printf",
2094
+ "source",
2095
+ "exit",
2096
+ "set",
2097
+ "unset",
2098
+ "test",
2099
+ "eval",
2100
+ "exec",
2101
+ "trap",
2102
+ "shift"
2103
+ ]);
2104
+ var SQL_KEYWORDS = /* @__PURE__ */ new Set([
2105
+ "add",
2106
+ "all",
2107
+ "alter",
2108
+ "and",
2109
+ "as",
2110
+ "asc",
2111
+ "auto_increment",
2112
+ "begin",
2113
+ "between",
2114
+ "by",
2115
+ "case",
2116
+ "column",
2117
+ "commit",
2118
+ "constraint",
2119
+ "create",
2120
+ "cross",
2121
+ "default",
2122
+ "delete",
2123
+ "desc",
2124
+ "distinct",
2125
+ "drop",
2126
+ "else",
2127
+ "end",
2128
+ "exists",
2129
+ "foreign",
2130
+ "from",
2131
+ "full",
2132
+ "group",
2133
+ "having",
2134
+ "if",
2135
+ "in",
2136
+ "index",
2137
+ "inner",
2138
+ "insert",
2139
+ "into",
2140
+ "is",
2141
+ "join",
2142
+ "key",
2143
+ "left",
2144
+ "like",
2145
+ "limit",
2146
+ "not",
2147
+ "null",
2148
+ "offset",
2149
+ "on",
2150
+ "or",
2151
+ "order",
2152
+ "outer",
2153
+ "primary",
2154
+ "references",
2155
+ "right",
2156
+ "rollback",
2157
+ "select",
2158
+ "set",
2159
+ "table",
2160
+ "then",
2161
+ "union",
2162
+ "unique",
2163
+ "update",
2164
+ "values",
2165
+ "view",
2166
+ "when",
2167
+ "where",
2168
+ "with",
2169
+ // common types / functions
2170
+ "int",
2171
+ "integer",
2172
+ "bigint",
2173
+ "smallint",
2174
+ "decimal",
2175
+ "numeric",
2176
+ "float",
2177
+ "real",
2178
+ "double",
2179
+ "char",
2180
+ "varchar",
2181
+ "text",
2182
+ "boolean",
2183
+ "bool",
2184
+ "date",
2185
+ "datetime",
2186
+ "timestamp",
2187
+ "count",
2188
+ "sum",
2189
+ "avg",
2190
+ "min",
2191
+ "max",
2192
+ "coalesce"
2193
+ ]);
2194
+ function highlightCLike(code) {
2195
+ const n = code.length;
2196
+ let out = "";
2197
+ let i = 0;
2198
+ let prevWord = "";
2199
+ while (i < n) {
2200
+ const ch = code.charCodeAt(i);
2201
+ if (ch === 47 && code.charCodeAt(i + 1) === 47) {
2202
+ let j = i + 2;
2203
+ while (j < n && code.charCodeAt(j) !== 10) j++;
2204
+ out += span("comment", code.slice(i, j));
2205
+ i = j;
2206
+ continue;
2207
+ }
2208
+ if (ch === 47 && code.charCodeAt(i + 1) === 42) {
2209
+ let j = i + 2;
2210
+ while (j < n && !(code.charCodeAt(j) === 42 && code.charCodeAt(j + 1) === 47)) {
2211
+ j++;
2212
+ }
2213
+ j = Math.min(n, j + 2);
2214
+ out += span("comment", code.slice(i, j));
2215
+ i = j;
2216
+ continue;
2217
+ }
2218
+ if (ch === 34 || ch === 39 || ch === 96) {
2219
+ const end = consumeString(code, i, ch);
2220
+ out += span("string", code.slice(i, end));
2221
+ i = end;
2222
+ continue;
2223
+ }
2224
+ if (isDigit(ch) || ch === 46 && isDigit(code.charCodeAt(i + 1))) {
2225
+ let j = i + 1;
2226
+ while (j < n && isNumberPart(code.charCodeAt(j))) j++;
2227
+ out += span("number", code.slice(i, j));
2228
+ i = j;
2229
+ prevWord = "";
2230
+ continue;
2231
+ }
2232
+ if (isIdentStart(ch)) {
2233
+ let j = i + 1;
2234
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2235
+ const word = code.slice(i, j);
2236
+ if (JS_KEYWORDS.has(word)) {
2237
+ out += span("keyword", word);
2238
+ } else if (TS_TYPES.has(word)) {
2239
+ out += span("type", word);
2240
+ } else {
2241
+ let k = j;
2242
+ while (k < n) {
2243
+ const c = code.charCodeAt(k);
2244
+ if (c === 32 || c === 9) {
2245
+ k++;
2246
+ continue;
2247
+ }
2248
+ break;
2249
+ }
2250
+ const isCall = code.charCodeAt(k) === 40;
2251
+ if (prevNonSpace(code, i) === 46) {
2252
+ out += span("prop", word);
2253
+ } else if (prevWord === "class" || prevWord === "interface") {
2254
+ out += span("type", word);
2255
+ } else if (isCall || prevWord === "function") {
2256
+ out += span("fn", word);
2257
+ } else {
2258
+ out += escapeHtml2(word);
2259
+ }
2260
+ }
2261
+ prevWord = word;
2262
+ i = j;
2263
+ continue;
2264
+ }
2265
+ if (isPunct(ch)) {
2266
+ out += span("punct", code.charAt(i));
2267
+ i++;
2268
+ continue;
2269
+ }
2270
+ out += escapeHtml2(code.charAt(i));
2271
+ i++;
2272
+ }
2273
+ return out;
2274
+ }
2275
+ function highlightJson(code) {
2276
+ const n = code.length;
2277
+ let out = "";
2278
+ let i = 0;
2279
+ while (i < n) {
2280
+ const ch = code.charCodeAt(i);
2281
+ if (ch === 34) {
2282
+ const end = consumeString(code, i, 34);
2283
+ const text = code.slice(i, end);
2284
+ let k = end;
2285
+ while (k < n) {
2286
+ const c = code.charCodeAt(k);
2287
+ if (c === 32 || c === 9 || c === 10 || c === 13) {
2288
+ k++;
2289
+ continue;
2290
+ }
2291
+ break;
2292
+ }
2293
+ out += span(code.charCodeAt(k) === 58 ? "prop" : "string", text);
2294
+ i = end;
2295
+ continue;
2296
+ }
2297
+ if (isDigit(ch) || ch === 45 && isDigit(code.charCodeAt(i + 1))) {
2298
+ let j = i + 1;
2299
+ while (j < n && isNumberPart(code.charCodeAt(j))) j++;
2300
+ out += span("number", code.slice(i, j));
2301
+ i = j;
2302
+ continue;
2303
+ }
2304
+ if (isIdentStart(ch)) {
2305
+ let j = i + 1;
2306
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2307
+ const word = code.slice(i, j);
2308
+ if (word === "true" || word === "false" || word === "null") {
2309
+ out += span("keyword", word);
2310
+ } else {
2311
+ out += escapeHtml2(word);
2312
+ }
2313
+ i = j;
2314
+ continue;
2315
+ }
2316
+ if (isPunct(ch)) {
2317
+ out += span("punct", code.charAt(i));
2318
+ i++;
2319
+ continue;
2320
+ }
2321
+ out += escapeHtml2(code.charAt(i));
2322
+ i++;
2323
+ }
2324
+ return out;
2325
+ }
2326
+ function highlightCss(code) {
2327
+ const n = code.length;
2328
+ let out = "";
2329
+ let i = 0;
2330
+ let depth = 0;
2331
+ let beforeColon = true;
2332
+ while (i < n) {
2333
+ const ch = code.charCodeAt(i);
2334
+ if (ch === 47 && code.charCodeAt(i + 1) === 42) {
2335
+ let j = i + 2;
2336
+ while (j < n && !(code.charCodeAt(j) === 42 && code.charCodeAt(j + 1) === 47)) {
2337
+ j++;
2338
+ }
2339
+ j = Math.min(n, j + 2);
2340
+ out += span("comment", code.slice(i, j));
2341
+ i = j;
2342
+ continue;
2343
+ }
2344
+ if (ch === 34 || ch === 39) {
2345
+ const end = consumeString(code, i, ch);
2346
+ out += span("string", code.slice(i, end));
2347
+ i = end;
2348
+ continue;
2349
+ }
2350
+ if (ch === 64) {
2351
+ let j = i + 1;
2352
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2353
+ out += span("keyword", code.slice(i, j));
2354
+ i = j;
2355
+ continue;
2356
+ }
2357
+ if (ch === 123) {
2358
+ out += span("punct", "{");
2359
+ depth++;
2360
+ beforeColon = true;
2361
+ i++;
2362
+ continue;
2363
+ }
2364
+ if (ch === 125) {
2365
+ out += span("punct", "}");
2366
+ if (depth > 0) depth--;
2367
+ beforeColon = true;
2368
+ i++;
2369
+ continue;
2370
+ }
2371
+ if (ch === 59) {
2372
+ out += span("punct", ";");
2373
+ beforeColon = true;
2374
+ i++;
2375
+ continue;
2376
+ }
2377
+ if (ch === 58) {
2378
+ out += span("punct", ":");
2379
+ beforeColon = false;
2380
+ i++;
2381
+ continue;
2382
+ }
2383
+ if (isDigit(ch) || ch === 46 && isDigit(code.charCodeAt(i + 1))) {
2384
+ let j = i + 1;
2385
+ while (j < n && isNumberPart(code.charCodeAt(j))) j++;
2386
+ out += span("number", code.slice(i, j));
2387
+ i = j;
2388
+ continue;
2389
+ }
2390
+ if (isIdentStart(ch) || ch === 45) {
2391
+ let j = i + 1;
2392
+ while (j < n && (isIdentPart(code.charCodeAt(j)) || code.charCodeAt(j) === 45)) {
2393
+ j++;
2394
+ }
2395
+ const word = code.slice(i, j);
2396
+ if (depth > 0 && beforeColon) {
2397
+ out += span("prop", word);
2398
+ } else {
2399
+ out += escapeHtml2(word);
2400
+ }
2401
+ i = j;
2402
+ continue;
2403
+ }
2404
+ if (isPunct(ch)) {
2405
+ out += span("punct", code.charAt(i));
2406
+ i++;
2407
+ continue;
2408
+ }
2409
+ out += escapeHtml2(code.charAt(i));
2410
+ i++;
2411
+ }
2412
+ return out;
2413
+ }
2414
+ function isTagChar(ch) {
2415
+ return isIdentPart(ch) || ch === 45 || ch === 58;
2416
+ }
2417
+ function highlightHtml(code) {
2418
+ const n = code.length;
2419
+ let out = "";
2420
+ let i = 0;
2421
+ while (i < n) {
2422
+ const ch = code.charCodeAt(i);
2423
+ if (ch === 60 && code.startsWith("<!--", i)) {
2424
+ const idx = code.indexOf("-->", i + 4);
2425
+ const j = idx === -1 ? n : idx + 3;
2426
+ out += span("comment", code.slice(i, j));
2427
+ i = j;
2428
+ continue;
2429
+ }
2430
+ if (ch === 60) {
2431
+ let j = i + 1;
2432
+ if (code.charCodeAt(j) === 47) j++;
2433
+ out += span("punct", code.slice(i, j));
2434
+ const nameStart = j;
2435
+ while (j < n && isTagChar(code.charCodeAt(j))) j++;
2436
+ if (j > nameStart) out += span("keyword", code.slice(nameStart, j));
2437
+ while (j < n && code.charCodeAt(j) !== 62) {
2438
+ const c = code.charCodeAt(j);
2439
+ if (c === 34 || c === 39) {
2440
+ const end = consumeString(code, j, c);
2441
+ out += span("string", code.slice(j, end));
2442
+ j = end;
2443
+ continue;
2444
+ }
2445
+ if (c === 61) {
2446
+ out += span("punct", "=");
2447
+ j++;
2448
+ continue;
2449
+ }
2450
+ if (isTagChar(c)) {
2451
+ const a = j;
2452
+ while (j < n && isTagChar(code.charCodeAt(j))) j++;
2453
+ out += span("prop", code.slice(a, j));
2454
+ continue;
2455
+ }
2456
+ out += escapeHtml2(code.charAt(j));
2457
+ j++;
2458
+ }
2459
+ if (j < n && code.charCodeAt(j) === 62) {
2460
+ out += span("punct", ">");
2461
+ j++;
2462
+ }
2463
+ i = j;
2464
+ continue;
2465
+ }
2466
+ if (ch === 38) {
2467
+ let j = i + 1;
2468
+ while (j < n && j - i < 12 && code.charCodeAt(j) !== 59 && (isIdentPart(code.charCodeAt(j)) || code.charCodeAt(j) === 35)) {
2469
+ j++;
2470
+ }
2471
+ if (j < n && code.charCodeAt(j) === 59) {
2472
+ out += span("type", code.slice(i, j + 1));
2473
+ i = j + 1;
2474
+ continue;
2475
+ }
2476
+ }
2477
+ out += escapeHtml2(code.charAt(i));
2478
+ i++;
2479
+ }
2480
+ return out;
2481
+ }
2482
+ function highlightMarkdownLine(line) {
2483
+ const n = line.length;
2484
+ let out = "";
2485
+ let i = 0;
2486
+ while (i < n && (line.charCodeAt(i) === 32 || line.charCodeAt(i) === 9)) {
2487
+ out += line.charAt(i);
2488
+ i++;
2489
+ }
2490
+ if (line.charCodeAt(i) === 35) {
2491
+ let h = i;
2492
+ while (h < n && line.charCodeAt(h) === 35) h++;
2493
+ if (h - i <= 6 && (h >= n || line.charCodeAt(h) === 32)) {
2494
+ out += span("keyword", line.slice(i, h));
2495
+ i = h;
2496
+ }
2497
+ } else if (line.charCodeAt(i) === 62) {
2498
+ out += span("keyword", ">");
2499
+ i++;
2500
+ } else {
2501
+ const c = line.charCodeAt(i);
2502
+ if ((c === 45 || c === 43 || c === 42) && line.charCodeAt(i + 1) === 32) {
2503
+ out += span("punct", line.charAt(i));
2504
+ i++;
2505
+ }
2506
+ }
2507
+ while (i < n) {
2508
+ const ch = line.charCodeAt(i);
2509
+ if (ch === 96) {
2510
+ let j = i + 1;
2511
+ while (j < n && line.charCodeAt(j) !== 96) j++;
2512
+ j = Math.min(n, j + 1);
2513
+ out += span("string", line.slice(i, j));
2514
+ i = j;
2515
+ continue;
2516
+ }
2517
+ if (ch === 42 || ch === 95 || ch === 126) {
2518
+ let j = i;
2519
+ while (j < n && line.charCodeAt(j) === ch) j++;
2520
+ out += span("punct", line.slice(i, j));
2521
+ i = j;
2522
+ continue;
2523
+ }
2524
+ if (ch === 91 || ch === 93 || ch === 40 || ch === 41) {
2525
+ out += span("punct", line.charAt(i));
2526
+ i++;
2527
+ continue;
2528
+ }
2529
+ out += escapeHtml2(line.charAt(i));
2530
+ i++;
2531
+ }
2532
+ return out;
2533
+ }
2534
+ function highlightMarkdown(code) {
2535
+ const lines = code.split("\n");
2536
+ let out = "";
2537
+ for (let i = 0; i < lines.length; i++) {
2538
+ if (i > 0) out += "\n";
2539
+ out += highlightMarkdownLine(lines[i] ?? "");
2540
+ }
2541
+ return out;
2542
+ }
2543
+ function highlightPython(code) {
2544
+ const n = code.length;
2545
+ let out = "";
2546
+ let i = 0;
2547
+ let prevWord = "";
2548
+ while (i < n) {
2549
+ const ch = code.charCodeAt(i);
2550
+ if (ch === 35) {
2551
+ let j = i + 1;
2552
+ while (j < n && code.charCodeAt(j) !== 10) j++;
2553
+ out += span("comment", code.slice(i, j));
2554
+ i = j;
2555
+ continue;
2556
+ }
2557
+ if ((ch === 34 || ch === 39) && code.charCodeAt(i + 1) === ch && code.charCodeAt(i + 2) === ch) {
2558
+ const marker = code.slice(i, i + 3);
2559
+ const idx = code.indexOf(marker, i + 3);
2560
+ const j = idx === -1 ? n : idx + 3;
2561
+ out += span("string", code.slice(i, j));
2562
+ i = j;
2563
+ prevWord = "";
2564
+ continue;
2565
+ }
2566
+ if (ch === 34 || ch === 39) {
2567
+ const end = consumeString(code, i, ch);
2568
+ out += span("string", code.slice(i, end));
2569
+ i = end;
2570
+ prevWord = "";
2571
+ continue;
2572
+ }
2573
+ if (ch === 64 && isIdentStart(code.charCodeAt(i + 1))) {
2574
+ let j = i + 1;
2575
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2576
+ out += span("keyword", code.slice(i, j));
2577
+ i = j;
2578
+ continue;
2579
+ }
2580
+ if (isDigit(ch) || ch === 46 && isDigit(code.charCodeAt(i + 1))) {
2581
+ let j = i + 1;
2582
+ while (j < n && isNumberPart(code.charCodeAt(j))) j++;
2583
+ out += span("number", code.slice(i, j));
2584
+ i = j;
2585
+ prevWord = "";
2586
+ continue;
2587
+ }
2588
+ if (isIdentStart(ch)) {
2589
+ let j = i + 1;
2590
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2591
+ const word = code.slice(i, j);
2592
+ if (PY_KEYWORDS.has(word)) {
2593
+ out += span("keyword", word);
2594
+ } else if (prevWord === "def") {
2595
+ out += span("fn", word);
2596
+ } else if (prevWord === "class") {
2597
+ out += span("type", word);
2598
+ } else {
2599
+ let k = j;
2600
+ while (k < n && (code.charCodeAt(k) === 32 || code.charCodeAt(k) === 9))
2601
+ k++;
2602
+ out += code.charCodeAt(k) === 40 ? span("fn", word) : escapeHtml2(word);
2603
+ }
2604
+ prevWord = word;
2605
+ i = j;
2606
+ continue;
2607
+ }
2608
+ if (isPunct(ch)) {
2609
+ out += span("punct", code.charAt(i));
2610
+ i++;
2611
+ continue;
2612
+ }
2613
+ out += escapeHtml2(code.charAt(i));
2614
+ i++;
2615
+ }
2616
+ return out;
2617
+ }
2618
+ function highlightBash(code) {
2619
+ const n = code.length;
2620
+ let out = "";
2621
+ let i = 0;
2622
+ while (i < n) {
2623
+ const ch = code.charCodeAt(i);
2624
+ if (ch === 35) {
2625
+ const prev = i > 0 ? code.charCodeAt(i - 1) : 10;
2626
+ if (i === 0 || prev === 32 || prev === 9 || prev === 10 || prev === 13 || prev === 59) {
2627
+ let j = i + 1;
2628
+ while (j < n && code.charCodeAt(j) !== 10) j++;
2629
+ out += span("comment", code.slice(i, j));
2630
+ i = j;
2631
+ continue;
2632
+ }
2633
+ }
2634
+ if (ch === 34 || ch === 39) {
2635
+ const end = consumeString(code, i, ch);
2636
+ out += span("string", code.slice(i, end));
2637
+ i = end;
2638
+ continue;
2639
+ }
2640
+ if (ch === 36) {
2641
+ let j = i + 1;
2642
+ if (code.charCodeAt(j) === 123) {
2643
+ while (j < n && code.charCodeAt(j) !== 125) j++;
2644
+ j = Math.min(n, j + 1);
2645
+ } else {
2646
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2647
+ }
2648
+ out += span("prop", code.slice(i, j));
2649
+ i = j;
2650
+ continue;
2651
+ }
2652
+ if (isDigit(ch)) {
2653
+ let j = i + 1;
2654
+ while (j < n && isNumberPart(code.charCodeAt(j))) j++;
2655
+ out += span("number", code.slice(i, j));
2656
+ i = j;
2657
+ continue;
2658
+ }
2659
+ if (isIdentStart(ch)) {
2660
+ let j = i + 1;
2661
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2662
+ const word = code.slice(i, j);
2663
+ out += BASH_KEYWORDS.has(word) ? span("keyword", word) : escapeHtml2(word);
2664
+ i = j;
2665
+ continue;
2666
+ }
2667
+ if (isPunct(ch)) {
2668
+ out += span("punct", code.charAt(i));
2669
+ i++;
2670
+ continue;
2671
+ }
2672
+ out += escapeHtml2(code.charAt(i));
2673
+ i++;
2674
+ }
2675
+ return out;
2676
+ }
2677
+ function highlightSql(code) {
2678
+ const n = code.length;
2679
+ let out = "";
2680
+ let i = 0;
2681
+ while (i < n) {
2682
+ const ch = code.charCodeAt(i);
2683
+ if (ch === 45 && code.charCodeAt(i + 1) === 45) {
2684
+ let j = i + 2;
2685
+ while (j < n && code.charCodeAt(j) !== 10) j++;
2686
+ out += span("comment", code.slice(i, j));
2687
+ i = j;
2688
+ continue;
2689
+ }
2690
+ if (ch === 47 && code.charCodeAt(i + 1) === 42) {
2691
+ let j = i + 2;
2692
+ while (j < n && !(code.charCodeAt(j) === 42 && code.charCodeAt(j + 1) === 47)) {
2693
+ j++;
2694
+ }
2695
+ j = Math.min(n, j + 2);
2696
+ out += span("comment", code.slice(i, j));
2697
+ i = j;
2698
+ continue;
2699
+ }
2700
+ if (ch === 39 || ch === 34) {
2701
+ const end = consumeString(code, i, ch);
2702
+ out += span("string", code.slice(i, end));
2703
+ i = end;
2704
+ continue;
2705
+ }
2706
+ if (isDigit(ch) || ch === 46 && isDigit(code.charCodeAt(i + 1))) {
2707
+ let j = i + 1;
2708
+ while (j < n && isNumberPart(code.charCodeAt(j))) j++;
2709
+ out += span("number", code.slice(i, j));
2710
+ i = j;
2711
+ continue;
2712
+ }
2713
+ if (isIdentStart(ch)) {
2714
+ let j = i + 1;
2715
+ while (j < n && isIdentPart(code.charCodeAt(j))) j++;
2716
+ const word = code.slice(i, j);
2717
+ out += SQL_KEYWORDS.has(word.toLowerCase()) ? span("keyword", word) : escapeHtml2(word);
2718
+ i = j;
2719
+ continue;
2720
+ }
2721
+ if (isPunct(ch)) {
2722
+ out += span("punct", code.charAt(i));
2723
+ i++;
2724
+ continue;
2725
+ }
2726
+ out += escapeHtml2(code.charAt(i));
2727
+ i++;
2728
+ }
2729
+ return out;
2730
+ }
2731
+ var ALIASES = {
2732
+ js: "javascript",
2733
+ jsx: "javascript",
2734
+ javascript: "javascript",
2735
+ mjs: "javascript",
2736
+ cjs: "javascript",
2737
+ ts: "typescript",
2738
+ tsx: "typescript",
2739
+ typescript: "typescript",
2740
+ json: "json",
2741
+ jsonc: "json",
2742
+ json5: "json",
2743
+ css: "css",
2744
+ html: "html",
2745
+ htm: "html",
2746
+ xml: "html",
2747
+ md: "markdown",
2748
+ markdown: "markdown",
2749
+ py: "python",
2750
+ python: "python",
2751
+ sh: "bash",
2752
+ bash: "bash",
2753
+ shell: "bash",
2754
+ zsh: "bash",
2755
+ sql: "sql"
2756
+ };
2757
+ var TOKENIZERS = {
2758
+ javascript: highlightCLike,
2759
+ typescript: highlightCLike,
2760
+ json: highlightJson,
2761
+ css: highlightCss,
2762
+ html: highlightHtml,
2763
+ markdown: highlightMarkdown,
2764
+ python: highlightPython,
2765
+ bash: highlightBash,
2766
+ sql: highlightSql
2767
+ };
2768
+ function highlightToHtml(lang, code) {
2769
+ const info = typeof lang === "string" ? lang : "";
2770
+ const first = info.trim().split(/\s+/)[0] ?? "";
2771
+ const key = first.toLowerCase();
2772
+ const resolved = ALIASES[key] ?? key;
2773
+ const tokenizer = TOKENIZERS[resolved];
2774
+ if (!tokenizer) return escapeHtml2(code);
2775
+ return tokenizer(code);
2776
+ }
2777
+
2778
+ // src/core/comments.ts
2779
+ var MAPPER = new TextDoc();
2780
+ var ASSOC_FROM = 1;
2781
+ var ASSOC_TO = 1;
2782
+ function mapAnchor(anchor, change) {
2783
+ const from = MAPPER.mapOffset(anchor.from, change, ASSOC_FROM);
2784
+ const to = MAPPER.mapOffset(anchor.to, change, ASSOC_TO);
2785
+ const collapsed = from === to;
2786
+ const deletedWholeRange = change.to > change.from && change.from <= anchor.from && change.to >= anchor.to;
2787
+ if (collapsed && deletedWholeRange) return null;
2788
+ return { from: Math.min(from, to), to: Math.max(from, to) };
2789
+ }
2790
+
2791
+ // src/core/table.ts
2792
+ var clampInt = (n, lo, hi) => n < lo ? lo : n > hi ? hi : n;
2793
+ function delimCell(align) {
2794
+ switch (align) {
2795
+ case "left":
2796
+ return ":---";
2797
+ case "center":
2798
+ return ":---:";
2799
+ case "right":
2800
+ return "---:";
2801
+ default:
2802
+ return "---";
2803
+ }
2804
+ }
2805
+ var cellText = (text, cell) => text.slice(cell.from, cell.to);
2806
+ var emptyRow = (ncols) => Array.from({ length: ncols }, () => "");
2807
+ function tableToGrid(text, table) {
2808
+ const ncols = Math.max(1, table.header.length);
2809
+ const header = table.header.map((c) => cellText(text, c));
2810
+ while (header.length < ncols) header.push("");
2811
+ const align = [];
2812
+ for (let i = 0; i < ncols; i++) align.push(table.align[i] ?? null);
2813
+ const rows = table.rows.map((row) => {
2814
+ const cells = row.map((c) => cellText(text, c));
2815
+ while (cells.length < ncols) cells.push("");
2816
+ cells.length = ncols;
2817
+ return cells;
2818
+ });
2819
+ return { header, align, rows };
2820
+ }
2821
+ function cellsForLine(grid, lineIdx) {
2822
+ const ncols = grid.header.length;
2823
+ if (lineIdx === 0) return grid.header;
2824
+ if (lineIdx === 1) {
2825
+ const a = [];
2826
+ for (let i = 0; i < ncols; i++) a.push(delimCell(grid.align[i] ?? null));
2827
+ return a;
2828
+ }
2829
+ return grid.rows[lineIdx - 2] ?? grid.header;
2830
+ }
2831
+ function gridLines(grid) {
2832
+ const ncols = grid.header.length;
2833
+ const rowLine = (cells) => {
2834
+ const out = cells.slice(0, ncols);
2835
+ while (out.length < ncols) out.push("");
2836
+ return `| ${out.join(" | ")} |`;
2837
+ };
2838
+ const delim = [];
2839
+ for (let i = 0; i < ncols; i++) delim.push(delimCell(grid.align[i] ?? null));
2840
+ const lines = [rowLine(grid.header), `| ${delim.join(" | ")} |`];
2841
+ for (const r of grid.rows) lines.push(rowLine(r));
2842
+ return lines;
2843
+ }
2844
+ function lineStart(lines, lineIdx) {
2845
+ let off = 0;
2846
+ for (const l of lines.slice(0, lineIdx)) off += l.length + 1;
2847
+ return off;
2848
+ }
2849
+ function colOffsetInLine(cells, col) {
2850
+ let off = 2;
2851
+ for (const c of cells.slice(0, col)) off += c.length + 3;
2852
+ return off;
2853
+ }
2854
+ function commit(text, table, grid, lineIdx, col) {
2855
+ const lines = gridLines(grid);
2856
+ const block = lines.join("\n");
2857
+ const inBlock = lineStart(lines, lineIdx) + colOffsetInLine(cellsForLine(grid, lineIdx), col);
2858
+ const newText = text.slice(0, table.from) + block + text.slice(table.to);
2859
+ return { text: newText, selection: table.from + inBlock };
2860
+ }
2861
+ function cellSourceRange(table, row, col) {
2862
+ const cell = row === 0 ? table.header[col] : table.rows[row - 1]?.[col];
2863
+ if (!cell) return null;
2864
+ return { from: cell.from, to: cell.to };
2865
+ }
2866
+ function addRow(text, table, atRow) {
2867
+ const grid = tableToGrid(text, table);
2868
+ const bodyIdx = clampInt(atRow - 1, 0, grid.rows.length);
2869
+ grid.rows.splice(bodyIdx, 0, emptyRow(grid.header.length));
2870
+ return commit(text, table, grid, 2 + bodyIdx, 0);
2871
+ }
2872
+ function addColumn(text, table, atCol) {
2873
+ const grid = tableToGrid(text, table);
2874
+ const idx = clampInt(atCol, 0, grid.header.length);
2875
+ grid.header.splice(idx, 0, "");
2876
+ grid.align.splice(idx, 0, null);
2877
+ for (const r of grid.rows) r.splice(idx, 0, "");
2878
+ return commit(text, table, grid, 0, idx);
2879
+ }
2880
+ function removeRow(text, table, atRow) {
2881
+ const grid = tableToGrid(text, table);
2882
+ const bodyIdx = atRow - 1;
2883
+ if (bodyIdx >= 0 && bodyIdx < grid.rows.length) grid.rows.splice(bodyIdx, 1);
2884
+ const lineIdx = grid.rows.length === 0 ? 0 : 2 + clampInt(bodyIdx, 0, grid.rows.length - 1);
2885
+ return commit(text, table, grid, lineIdx, 0);
2886
+ }
2887
+ function removeColumn(text, table, atCol) {
2888
+ const grid = tableToGrid(text, table);
2889
+ if (grid.header.length > 1 && atCol >= 0 && atCol < grid.header.length) {
2890
+ grid.header.splice(atCol, 1);
2891
+ grid.align.splice(atCol, 1);
2892
+ for (const r of grid.rows) r.splice(atCol, 1);
2893
+ }
2894
+ const caretCol = clampInt(atCol, 0, grid.header.length - 1);
2895
+ return commit(text, table, grid, 0, caretCol);
2896
+ }
2897
+ function setAlignment(text, table, col, align) {
2898
+ const grid = tableToGrid(text, table);
2899
+ if (col >= 0 && col < grid.align.length) grid.align[col] = align;
2900
+ const caretCol = clampInt(col, 0, grid.align.length - 1);
2901
+ return commit(text, table, grid, 1, caretCol);
2902
+ }
2903
+
2904
+ // src/core/index.ts
2905
+ var EditorView = class {
2906
+ dom;
2907
+ constructor(options) {
2908
+ this.dom = options.parent;
2909
+ throw new Error(
2910
+ "typewright/core: engine not yet implemented (pre-alpha scaffold). See SPEC.md for the architecture and roadmap."
2911
+ );
2912
+ }
2913
+ /** Current document string. */
2914
+ get doc() {
2915
+ return "";
2916
+ }
2917
+ /** Apply an edit. */
2918
+ dispatch(_change) {
2919
+ }
2920
+ setSelection(_selection) {
2921
+ }
2922
+ destroy() {
2923
+ }
2924
+ };
2925
+
2926
+ exports.COMMANDS = COMMANDS;
2927
+ exports.EditorView = EditorView;
2928
+ exports.TextDoc = TextDoc;
2929
+ exports.activeBlockIndex = activeBlockIndex;
2930
+ exports.addColumn = addColumn;
2931
+ exports.addRow = addRow;
2932
+ exports.applyCommand = applyCommand;
2933
+ exports.cellSourceRange = cellSourceRange;
2934
+ exports.childrenOf = childrenOf;
2935
+ exports.collectMarkers = collectMarkers;
2936
+ exports.headingFoldRanges = headingFoldRanges;
2937
+ exports.hiddenMarkers = hiddenMarkers;
2938
+ exports.highlightToHtml = highlightToHtml;
2939
+ exports.isInline = isInline;
2940
+ exports.mapAnchor = mapAnchor;
2941
+ exports.parse = parse;
2942
+ exports.parseIncremental = parseIncremental;
2943
+ exports.removeColumn = removeColumn;
2944
+ exports.removeRow = removeRow;
2945
+ exports.renderInline = renderInline;
2946
+ exports.renderNode = renderNode;
2947
+ exports.renderToHtml = renderToHtml;
2948
+ exports.safeUrl = safeUrl;
2949
+ exports.setAlignment = setAlignment;
2950
+ exports.walk = walk;
2951
+ //# sourceMappingURL=index.cjs.map
2952
+ //# sourceMappingURL=index.cjs.map