create-markdown 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1814 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __moduleCache = /* @__PURE__ */ new WeakMap;
6
- var __toCommonJS = (from) => {
7
- var entry = __moduleCache.get(from), desc;
8
- if (entry)
9
- return entry;
10
- entry = __defProp({}, "__esModule", { value: true });
11
- if (from && typeof from === "object" || typeof from === "function")
12
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
- get: () => from[key],
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- }));
16
- __moduleCache.set(from, entry);
17
- return entry;
18
- };
19
- var __export = (target, all) => {
20
- for (var name in all)
21
- __defProp(target, name, {
22
- get: all[name],
23
- enumerable: true,
24
- configurable: true,
25
- set: (newValue) => all[name] = () => newValue
26
- });
27
- };
28
-
29
- // src/react/index.ts
30
- var exports_react = {};
31
- __export(exports_react, {
32
- useMarkdown: () => useMarkdown,
33
- useDocument: () => useDocument,
34
- useBlockEditor: () => useBlockEditor,
35
- text: () => text,
36
- paragraph: () => paragraph,
37
- numberedList: () => numberedList,
38
- link: () => link,
39
- italic: () => italic,
40
- image: () => image,
41
- heading: () => heading,
42
- h6: () => h6,
43
- h5: () => h5,
44
- h4: () => h4,
45
- h3: () => h3,
46
- h2: () => h2,
47
- h1: () => h1,
48
- divider: () => divider,
49
- codeBlock: () => codeBlock,
50
- code: () => code,
51
- checkListItem: () => checkListItem,
52
- checkList: () => checkList,
53
- callout: () => callout,
54
- bulletList: () => bulletList,
55
- bold: () => bold,
56
- blockquote: () => blockquote,
57
- InlineContent: () => InlineContent,
58
- BlockRenderer: () => BlockRenderer,
59
- BlockElement: () => BlockElement
60
- });
61
- module.exports = __toCommonJS(exports_react);
62
-
63
- // src/react/block-renderer.tsx
64
- var jsx_dev_runtime = require("react/jsx-dev-runtime");
65
- function BlockRenderer({
66
- blocks,
67
- className,
68
- customRenderers
69
- }) {
70
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
71
- className,
72
- children: blocks.map((block) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV(BlockElement, {
73
- block,
74
- customRenderers
75
- }, block.id, false, undefined, this))
76
- }, undefined, false, undefined, this);
77
- }
78
- function BlockElement({
79
- block,
80
- customRenderers
81
- }) {
82
- const CustomRenderer = customRenderers?.[block.type];
83
- if (CustomRenderer) {
84
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(CustomRenderer, {
85
- block
86
- }, undefined, false, undefined, this);
87
- }
88
- switch (block.type) {
89
- case "paragraph":
90
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(ParagraphRenderer, {
91
- block
92
- }, undefined, false, undefined, this);
93
- case "heading":
94
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(HeadingRenderer, {
95
- block
96
- }, undefined, false, undefined, this);
97
- case "bulletList":
98
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(BulletListRenderer, {
99
- block,
100
- customRenderers
101
- }, undefined, false, undefined, this);
102
- case "numberedList":
103
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(NumberedListRenderer, {
104
- block,
105
- customRenderers
106
- }, undefined, false, undefined, this);
107
- case "checkList":
108
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(CheckListRenderer, {
109
- block
110
- }, undefined, false, undefined, this);
111
- case "codeBlock":
112
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(CodeBlockRenderer, {
113
- block
114
- }, undefined, false, undefined, this);
115
- case "blockquote":
116
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(BlockquoteRenderer, {
117
- block
118
- }, undefined, false, undefined, this);
119
- case "table":
120
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(TableRenderer, {
121
- block
122
- }, undefined, false, undefined, this);
123
- case "image":
124
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(ImageRenderer, {
125
- block
126
- }, undefined, false, undefined, this);
127
- case "divider":
128
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(DividerRenderer, {}, undefined, false, undefined, this);
129
- case "callout":
130
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(CalloutRenderer, {
131
- block
132
- }, undefined, false, undefined, this);
133
- default:
134
- return null;
135
- }
136
- }
137
- function InlineContent({ spans }) {
138
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(jsx_dev_runtime.Fragment, {
139
- children: spans.map((span, index) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV(SpanElement, {
140
- span
141
- }, index, false, undefined, this))
142
- }, undefined, false, undefined, this);
143
- }
144
- function SpanElement({ span }) {
145
- let content = span.text;
146
- const styles = span.styles;
147
- if (styles.code) {
148
- content = /* @__PURE__ */ jsx_dev_runtime.jsxDEV("code", {
149
- children: content
150
- }, undefined, false, undefined, this);
151
- }
152
- if (styles.highlight) {
153
- content = /* @__PURE__ */ jsx_dev_runtime.jsxDEV("mark", {
154
- children: content
155
- }, undefined, false, undefined, this);
156
- }
157
- if (styles.strikethrough) {
158
- content = /* @__PURE__ */ jsx_dev_runtime.jsxDEV("del", {
159
- children: content
160
- }, undefined, false, undefined, this);
161
- }
162
- if (styles.underline) {
163
- content = /* @__PURE__ */ jsx_dev_runtime.jsxDEV("u", {
164
- children: content
165
- }, undefined, false, undefined, this);
166
- }
167
- if (styles.italic) {
168
- content = /* @__PURE__ */ jsx_dev_runtime.jsxDEV("em", {
169
- children: content
170
- }, undefined, false, undefined, this);
171
- }
172
- if (styles.bold) {
173
- content = /* @__PURE__ */ jsx_dev_runtime.jsxDEV("strong", {
174
- children: content
175
- }, undefined, false, undefined, this);
176
- }
177
- if (styles.link) {
178
- content = /* @__PURE__ */ jsx_dev_runtime.jsxDEV("a", {
179
- href: styles.link.url,
180
- title: styles.link.title,
181
- children: content
182
- }, undefined, false, undefined, this);
183
- }
184
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(jsx_dev_runtime.Fragment, {
185
- children: content
186
- }, undefined, false, undefined, this);
187
- }
188
- function ParagraphRenderer({ block }) {
189
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("p", {
190
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(InlineContent, {
191
- spans: block.content
192
- }, undefined, false, undefined, this)
193
- }, undefined, false, undefined, this);
194
- }
195
- function HeadingRenderer({ block }) {
196
- const Tag = `h${block.props.level}`;
197
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Tag, {
198
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(InlineContent, {
199
- spans: block.content
200
- }, undefined, false, undefined, this)
201
- }, undefined, false, undefined, this);
202
- }
203
- function BulletListRenderer({
204
- block,
205
- customRenderers
206
- }) {
207
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("ul", {
208
- children: block.children.map((child) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV("li", {
209
- children: [
210
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(InlineContent, {
211
- spans: child.content
212
- }, undefined, false, undefined, this),
213
- child.children.length > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(BlockElement, {
214
- block: child,
215
- customRenderers
216
- }, undefined, false, undefined, this)
217
- ]
218
- }, child.id, true, undefined, this))
219
- }, undefined, false, undefined, this);
220
- }
221
- function NumberedListRenderer({
222
- block,
223
- customRenderers
224
- }) {
225
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("ol", {
226
- children: block.children.map((child) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV("li", {
227
- children: [
228
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(InlineContent, {
229
- spans: child.content
230
- }, undefined, false, undefined, this),
231
- child.children.length > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(BlockElement, {
232
- block: child,
233
- customRenderers
234
- }, undefined, false, undefined, this)
235
- ]
236
- }, child.id, true, undefined, this))
237
- }, undefined, false, undefined, this);
238
- }
239
- function CheckListRenderer({ block }) {
240
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
241
- style: { display: "flex", alignItems: "flex-start", gap: "0.5rem" },
242
- children: [
243
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV("input", {
244
- type: "checkbox",
245
- checked: block.props.checked,
246
- readOnly: true,
247
- style: { marginTop: "0.25rem" }
248
- }, undefined, false, undefined, this),
249
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV("span", {
250
- style: { textDecoration: block.props.checked ? "line-through" : "none" },
251
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(InlineContent, {
252
- spans: block.content
253
- }, undefined, false, undefined, this)
254
- }, undefined, false, undefined, this)
255
- ]
256
- }, undefined, true, undefined, this);
257
- }
258
- function CodeBlockRenderer({ block }) {
259
- const code = block.content.map((span) => span.text).join("");
260
- const language = block.props.language;
261
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("pre", {
262
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV("code", {
263
- className: language ? `language-${language}` : undefined,
264
- children: code
265
- }, undefined, false, undefined, this)
266
- }, undefined, false, undefined, this);
267
- }
268
- function BlockquoteRenderer({ block }) {
269
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("blockquote", {
270
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(InlineContent, {
271
- spans: block.content
272
- }, undefined, false, undefined, this)
273
- }, undefined, false, undefined, this);
274
- }
275
- function TableRenderer({ block }) {
276
- const { headers, rows, alignments } = block.props;
277
- const getAlignment = (index) => {
278
- return alignments?.[index] ?? undefined;
279
- };
280
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("table", {
281
- children: [
282
- headers.length > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV("thead", {
283
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV("tr", {
284
- children: headers.map((header, i) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV("th", {
285
- style: { textAlign: getAlignment(i) },
286
- children: header
287
- }, i, false, undefined, this))
288
- }, undefined, false, undefined, this)
289
- }, undefined, false, undefined, this),
290
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV("tbody", {
291
- children: rows.map((row, rowIndex) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV("tr", {
292
- children: row.map((cell, cellIndex) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV("td", {
293
- style: { textAlign: getAlignment(cellIndex) },
294
- children: cell
295
- }, cellIndex, false, undefined, this))
296
- }, rowIndex, false, undefined, this))
297
- }, undefined, false, undefined, this)
298
- ]
299
- }, undefined, true, undefined, this);
300
- }
301
- function ImageRenderer({ block }) {
302
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("figure", {
303
- children: [
304
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV("img", {
305
- src: block.props.url,
306
- alt: block.props.alt ?? "",
307
- title: block.props.title,
308
- width: block.props.width,
309
- height: block.props.height
310
- }, undefined, false, undefined, this),
311
- block.props.alt && /* @__PURE__ */ jsx_dev_runtime.jsxDEV("figcaption", {
312
- children: block.props.alt
313
- }, undefined, false, undefined, this)
314
- ]
315
- }, undefined, true, undefined, this);
316
- }
317
- function DividerRenderer() {
318
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("hr", {}, undefined, false, undefined, this);
319
- }
320
- function CalloutRenderer({ block }) {
321
- const calloutType = block.props.type;
322
- const styles = {
323
- padding: "1rem",
324
- borderRadius: "0.25rem",
325
- borderLeft: "4px solid",
326
- marginBottom: "1rem"
327
- };
328
- const colors = {
329
- info: { borderColor: "#3b82f6", backgroundColor: "#eff6ff" },
330
- warning: { borderColor: "#f59e0b", backgroundColor: "#fffbeb" },
331
- tip: { borderColor: "#10b981", backgroundColor: "#ecfdf5" },
332
- danger: { borderColor: "#ef4444", backgroundColor: "#fef2f2" },
333
- note: { borderColor: "#6b7280", backgroundColor: "#f9fafb" }
334
- };
335
- const colorStyle = colors[calloutType] ?? colors.note;
336
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
337
- style: {
338
- ...styles,
339
- borderLeftColor: colorStyle.borderColor,
340
- backgroundColor: colorStyle.backgroundColor
341
- },
342
- role: "alert",
343
- children: [
344
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV("strong", {
345
- style: { textTransform: "capitalize", display: "block", marginBottom: "0.5rem" },
346
- children: calloutType
347
- }, undefined, false, undefined, this),
348
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(InlineContent, {
349
- spans: block.content
350
- }, undefined, false, undefined, this)
351
- ]
352
- }, undefined, true, undefined, this);
353
- }
354
- // src/react/hooks.ts
355
- var import_react = require("react");
356
-
357
- // src/core/utils.ts
358
- var ID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
359
- var ID_LENGTH = 8;
360
- function generateId(length = ID_LENGTH) {
361
- let id = "";
362
- const charsLength = ID_CHARS.length;
363
- for (let i = 0;i < length; i++) {
364
- id += ID_CHARS.charAt(Math.floor(Math.random() * charsLength));
365
- }
366
- return id;
367
- }
368
- function deepClone(block, regenerateIds = true) {
369
- return {
370
- id: regenerateIds ? generateId() : block.id,
371
- type: block.type,
372
- content: block.content.map((span) => ({
373
- text: span.text,
374
- styles: { ...span.styles }
375
- })),
376
- children: block.children.map((child) => deepClone(child, regenerateIds)),
377
- props: { ...block.props }
378
- };
379
- }
380
- function deepCloneBlocks(blocks, regenerateIds = true) {
381
- return blocks.map((block) => deepClone(block, regenerateIds));
382
- }
383
- function spansToPlainText(spans) {
384
- return spans.map((span) => span.text).join("");
385
- }
386
- function plainSpan(text) {
387
- return { text, styles: {} };
388
- }
389
- function plainContent(text) {
390
- return [plainSpan(text)];
391
- }
392
-
393
- // src/core/document.ts
394
- var DOCUMENT_VERSION = 1;
395
- function createDocument(blocks = [], options = {}) {
396
- const { meta = {} } = options;
397
- return {
398
- version: DOCUMENT_VERSION,
399
- blocks: deepCloneBlocks(blocks, false),
400
- meta: {
401
- createdAt: new Date,
402
- updatedAt: new Date,
403
- ...meta
404
- }
405
- };
406
- }
407
- function insertBlock(doc, block, index) {
408
- const newBlocks = [...doc.blocks];
409
- const insertIndex = index ?? newBlocks.length;
410
- newBlocks.splice(insertIndex, 0, deepClone(block, false));
411
- return {
412
- ...doc,
413
- blocks: newBlocks,
414
- meta: {
415
- ...doc.meta,
416
- updatedAt: new Date
417
- }
418
- };
419
- }
420
- function appendBlock(doc, block) {
421
- return insertBlock(doc, block);
422
- }
423
- function removeBlock(doc, blockId) {
424
- const newBlocks = doc.blocks.filter((block) => block.id !== blockId);
425
- if (newBlocks.length === doc.blocks.length) {
426
- return doc;
427
- }
428
- return {
429
- ...doc,
430
- blocks: newBlocks,
431
- meta: {
432
- ...doc.meta,
433
- updatedAt: new Date
434
- }
435
- };
436
- }
437
- function updateBlock(doc, blockId, updates) {
438
- const blockIndex = doc.blocks.findIndex((block) => block.id === blockId);
439
- if (blockIndex === -1) {
440
- return doc;
441
- }
442
- const newBlocks = [...doc.blocks];
443
- const existingBlock = newBlocks[blockIndex];
444
- newBlocks[blockIndex] = {
445
- ...existingBlock,
446
- ...updates,
447
- id: existingBlock.id,
448
- type: existingBlock.type,
449
- content: updates.content ?? existingBlock.content,
450
- children: updates.children ?? existingBlock.children,
451
- props: updates.props ? { ...existingBlock.props, ...updates.props } : existingBlock.props
452
- };
453
- return {
454
- ...doc,
455
- blocks: newBlocks,
456
- meta: {
457
- ...doc.meta,
458
- updatedAt: new Date
459
- }
460
- };
461
- }
462
- function moveBlock(doc, blockId, newIndex) {
463
- const currentIndex = doc.blocks.findIndex((block) => block.id === blockId);
464
- if (currentIndex === -1) {
465
- return doc;
466
- }
467
- const targetIndex = Math.max(0, Math.min(newIndex, doc.blocks.length - 1));
468
- if (currentIndex === targetIndex) {
469
- return doc;
470
- }
471
- const newBlocks = [...doc.blocks];
472
- const [movedBlock] = newBlocks.splice(currentIndex, 1);
473
- newBlocks.splice(targetIndex, 0, movedBlock);
474
- return {
475
- ...doc,
476
- blocks: newBlocks,
477
- meta: {
478
- ...doc.meta,
479
- updatedAt: new Date
480
- }
481
- };
482
- }
483
- function findBlock(doc, blockId) {
484
- return doc.blocks.find((block) => block.id === blockId);
485
- }
486
- function getBlockIndex(doc, blockId) {
487
- return doc.blocks.findIndex((block) => block.id === blockId);
488
- }
489
- function updateMeta(doc, meta) {
490
- return {
491
- ...doc,
492
- meta: {
493
- ...doc.meta,
494
- ...meta,
495
- updatedAt: new Date
496
- }
497
- };
498
- }
499
- function clearBlocks(doc) {
500
- return {
501
- ...doc,
502
- blocks: [],
503
- meta: {
504
- ...doc.meta,
505
- updatedAt: new Date
506
- }
507
- };
508
- }
509
- function setBlocks(doc, blocks) {
510
- return {
511
- ...doc,
512
- blocks: deepCloneBlocks(blocks, false),
513
- meta: {
514
- ...doc.meta,
515
- updatedAt: new Date
516
- }
517
- };
518
- }
519
-
520
- // src/parsers/tokenizer.ts
521
- var PATTERNS = {
522
- heading: /^(#{1,6})\s+(.*)$/,
523
- setextH1: /^=+\s*$/,
524
- setextH2: /^-+\s*$/,
525
- bulletList: /^(\s*)([-*+])\s+(.*)$/,
526
- numberedList: /^(\s*)(\d+)\.\s+(.*)$/,
527
- checkList: /^(\s*)[-*+]\s+\[([ xX])\]\s+(.*)$/,
528
- codeFence: /^(\s*)(`{3,}|~{3,})(\w*)?\s*$/,
529
- blockquote: /^>\s?(.*)$/,
530
- callout: /^>\s*\[!(\w+)\]\s*$/,
531
- divider: /^(\s*)(-{3,}|\*{3,}|_{3,})\s*$/,
532
- tableRow: /^\|(.+)\|$/,
533
- tableSeparator: /^\|[\s\-:|]+\|$/,
534
- image: /^!\[([^\]]*)\]\(([^)]+)\)$/,
535
- blank: /^\s*$/
536
- };
537
- function tokenize(markdown) {
538
- const lines = markdown.split(/\r?\n/);
539
- const tokens = [];
540
- const state = {
541
- inCodeBlock: false,
542
- codeBlockFence: "",
543
- lineNumber: 0
544
- };
545
- for (let i = 0;i < lines.length; i++) {
546
- state.lineNumber = i + 1;
547
- const line = lines[i];
548
- const token = tokenizeLine(line, state, lines, i);
549
- if (token) {
550
- tokens.push(token);
551
- }
552
- }
553
- return tokens;
554
- }
555
- function tokenizeLine(line, state, allLines, lineIndex) {
556
- const lineNumber = state.lineNumber;
557
- if (state.inCodeBlock) {
558
- const fenceMatch = line.match(PATTERNS.codeFence);
559
- if (fenceMatch && line.trim().startsWith(state.codeBlockFence.charAt(0))) {
560
- state.inCodeBlock = false;
561
- state.codeBlockFence = "";
562
- return {
563
- type: "code_fence_end",
564
- raw: line,
565
- content: "",
566
- indent: 0,
567
- line: lineNumber
568
- };
569
- }
570
- return {
571
- type: "code_content",
572
- raw: line,
573
- content: line,
574
- indent: 0,
575
- line: lineNumber
576
- };
577
- }
578
- const codeFenceMatch = line.match(PATTERNS.codeFence);
579
- if (codeFenceMatch) {
580
- state.inCodeBlock = true;
581
- state.codeBlockFence = codeFenceMatch[2];
582
- const language = codeFenceMatch[3] || "";
583
- return {
584
- type: "code_fence_start",
585
- raw: line,
586
- content: "",
587
- indent: (codeFenceMatch[1] || "").length,
588
- line: lineNumber,
589
- meta: { language }
590
- };
591
- }
592
- if (PATTERNS.blank.test(line)) {
593
- return {
594
- type: "blank",
595
- raw: line,
596
- content: "",
597
- indent: 0,
598
- line: lineNumber
599
- };
600
- }
601
- const dividerMatch = line.match(PATTERNS.divider);
602
- if (dividerMatch) {
603
- const prevLine = lineIndex > 0 ? allLines[lineIndex - 1] : "";
604
- if (prevLine.trim() && !PATTERNS.blank.test(prevLine)) {
605
- if (PATTERNS.setextH2.test(line)) {}
606
- } else {
607
- return {
608
- type: "divider",
609
- raw: line,
610
- content: "",
611
- indent: (dividerMatch[1] || "").length,
612
- line: lineNumber
613
- };
614
- }
615
- }
616
- const headingMatch = line.match(PATTERNS.heading);
617
- if (headingMatch) {
618
- return {
619
- type: "heading",
620
- raw: line,
621
- content: headingMatch[2].trim(),
622
- indent: 0,
623
- line: lineNumber,
624
- meta: { level: headingMatch[1].length }
625
- };
626
- }
627
- const calloutMatch = line.match(PATTERNS.callout);
628
- if (calloutMatch) {
629
- return {
630
- type: "callout",
631
- raw: line,
632
- content: "",
633
- indent: 0,
634
- line: lineNumber,
635
- meta: { calloutType: calloutMatch[1].toLowerCase() }
636
- };
637
- }
638
- const blockquoteMatch = line.match(PATTERNS.blockquote);
639
- if (blockquoteMatch) {
640
- return {
641
- type: "blockquote",
642
- raw: line,
643
- content: blockquoteMatch[1],
644
- indent: 0,
645
- line: lineNumber
646
- };
647
- }
648
- const checkListMatch = line.match(PATTERNS.checkList);
649
- if (checkListMatch) {
650
- const checked = checkListMatch[2].toLowerCase() === "x";
651
- return {
652
- type: "check_list_item",
653
- raw: line,
654
- content: checkListMatch[3],
655
- indent: checkListMatch[1].length,
656
- line: lineNumber,
657
- meta: { checked }
658
- };
659
- }
660
- const bulletMatch = line.match(PATTERNS.bulletList);
661
- if (bulletMatch) {
662
- return {
663
- type: "bullet_list_item",
664
- raw: line,
665
- content: bulletMatch[3],
666
- indent: bulletMatch[1].length,
667
- line: lineNumber,
668
- meta: { listMarker: bulletMatch[2] }
669
- };
670
- }
671
- const numberedMatch = line.match(PATTERNS.numberedList);
672
- if (numberedMatch) {
673
- return {
674
- type: "numbered_list_item",
675
- raw: line,
676
- content: numberedMatch[3],
677
- indent: numberedMatch[1].length,
678
- line: lineNumber,
679
- meta: { listIndex: parseInt(numberedMatch[2], 10) }
680
- };
681
- }
682
- if (PATTERNS.tableSeparator.test(line)) {
683
- return {
684
- type: "table_separator",
685
- raw: line,
686
- content: line,
687
- indent: 0,
688
- line: lineNumber
689
- };
690
- }
691
- const tableRowMatch = line.match(PATTERNS.tableRow);
692
- if (tableRowMatch) {
693
- return {
694
- type: "table_row",
695
- raw: line,
696
- content: tableRowMatch[1],
697
- indent: 0,
698
- line: lineNumber
699
- };
700
- }
701
- const imageMatch = line.match(PATTERNS.image);
702
- if (imageMatch) {
703
- return {
704
- type: "image",
705
- raw: line,
706
- content: imageMatch[2],
707
- indent: 0,
708
- line: lineNumber,
709
- meta: { language: imageMatch[1] }
710
- };
711
- }
712
- if (PATTERNS.setextH1.test(line) || PATTERNS.setextH2.test(line)) {
713
- const prevLine = lineIndex > 0 ? allLines[lineIndex - 1] : "";
714
- if (prevLine.trim() && !PATTERNS.blank.test(prevLine)) {
715
- return {
716
- type: "heading",
717
- raw: line,
718
- content: prevLine.trim(),
719
- indent: 0,
720
- line: lineNumber,
721
- meta: { level: PATTERNS.setextH1.test(line) ? 1 : 2 }
722
- };
723
- }
724
- return {
725
- type: "divider",
726
- raw: line,
727
- content: "",
728
- indent: 0,
729
- line: lineNumber
730
- };
731
- }
732
- return {
733
- type: "paragraph",
734
- raw: line,
735
- content: line.trim(),
736
- indent: line.length - line.trimStart().length,
737
- line: lineNumber
738
- };
739
- }
740
-
741
- // src/parsers/inline.ts
742
- function parseInlineContent(text) {
743
- if (!text) {
744
- return [];
745
- }
746
- const tokens = tokenizeInline(text);
747
- return tokensToSpans(tokens);
748
- }
749
- function tokenizeInline(text) {
750
- const tokens = [];
751
- let remaining = text;
752
- let pos = 0;
753
- while (pos < remaining.length) {
754
- const matched = tryMatchInline(remaining, pos);
755
- if (matched) {
756
- if (matched.startIndex > pos) {
757
- tokens.push({
758
- type: "text",
759
- content: remaining.slice(pos, matched.startIndex)
760
- });
761
- }
762
- tokens.push(matched.token);
763
- pos = matched.endIndex;
764
- } else {
765
- pos++;
766
- }
767
- }
768
- return parseInlineTokens(text);
769
- }
770
- function parseInlineTokens(text) {
771
- const tokens = [];
772
- let i = 0;
773
- let textBuffer = "";
774
- const flushText = () => {
775
- if (textBuffer) {
776
- tokens.push({ type: "text", content: textBuffer });
777
- textBuffer = "";
778
- }
779
- };
780
- while (i < text.length) {
781
- if (text[i] === "\\" && i + 1 < text.length) {
782
- textBuffer += text[i + 1];
783
- i += 2;
784
- continue;
785
- }
786
- if (text[i] === "`") {
787
- const match = matchCode(text, i);
788
- if (match) {
789
- flushText();
790
- tokens.push(match.token);
791
- i = match.end;
792
- continue;
793
- }
794
- }
795
- if (text[i] === "[" || text[i] === "!" && text[i + 1] === "[") {
796
- const isImage = text[i] === "!";
797
- const match = matchLink(text, isImage ? i + 1 : i, isImage);
798
- if (match) {
799
- flushText();
800
- tokens.push(match.token);
801
- i = match.end;
802
- continue;
803
- }
804
- }
805
- if (text[i] === "*" || text[i] === "_") {
806
- const match = matchEmphasis(text, i);
807
- if (match) {
808
- flushText();
809
- tokens.push(match.token);
810
- i = match.end;
811
- continue;
812
- }
813
- }
814
- if (text[i] === "~" && text[i + 1] === "~") {
815
- const match = matchStrikethrough(text, i);
816
- if (match) {
817
- flushText();
818
- tokens.push(match.token);
819
- i = match.end;
820
- continue;
821
- }
822
- }
823
- if (text[i] === "=" && text[i + 1] === "=") {
824
- const match = matchHighlight(text, i);
825
- if (match) {
826
- flushText();
827
- tokens.push(match.token);
828
- i = match.end;
829
- continue;
830
- }
831
- }
832
- textBuffer += text[i];
833
- i++;
834
- }
835
- flushText();
836
- return tokens;
837
- }
838
- function matchCode(text, start) {
839
- let backticks = 0;
840
- let i = start;
841
- while (i < text.length && text[i] === "`") {
842
- backticks++;
843
- i++;
844
- }
845
- if (backticks === 0)
846
- return null;
847
- const closePattern = "`".repeat(backticks);
848
- const closeIndex = text.indexOf(closePattern, i);
849
- if (closeIndex === -1)
850
- return null;
851
- if (text[closeIndex + backticks] === "`") {
852
- return null;
853
- }
854
- const content = text.slice(i, closeIndex);
855
- return {
856
- token: { type: "code", content: content.trim() },
857
- end: closeIndex + backticks
858
- };
859
- }
860
- function matchLink(text, start, isImage = false) {
861
- if (text[start] !== "[")
862
- return null;
863
- let bracketDepth = 1;
864
- let i = start + 1;
865
- while (i < text.length && bracketDepth > 0) {
866
- if (text[i] === "[")
867
- bracketDepth++;
868
- else if (text[i] === "]")
869
- bracketDepth--;
870
- else if (text[i] === "\\")
871
- i++;
872
- i++;
873
- }
874
- if (bracketDepth !== 0)
875
- return null;
876
- const linkText = text.slice(start + 1, i - 1);
877
- if (text[i] !== "(")
878
- return null;
879
- const urlStart = i + 1;
880
- let urlEnd = urlStart;
881
- let parenDepth = 1;
882
- while (urlEnd < text.length && parenDepth > 0) {
883
- if (text[urlEnd] === "(")
884
- parenDepth++;
885
- else if (text[urlEnd] === ")")
886
- parenDepth--;
887
- else if (text[urlEnd] === "\\")
888
- urlEnd++;
889
- urlEnd++;
890
- }
891
- if (parenDepth !== 0)
892
- return null;
893
- const urlPart = text.slice(urlStart, urlEnd - 1).trim();
894
- let url = urlPart;
895
- let title;
896
- const titleMatch = urlPart.match(/^(.+?)\s+["'](.+?)["']$/);
897
- if (titleMatch) {
898
- url = titleMatch[1];
899
- title = titleMatch[2];
900
- }
901
- const children = parseInlineTokens(linkText);
902
- const token = isImage ? { type: "image", content: linkText, url, title, children } : { type: "link", content: linkText, url, title, children };
903
- return {
904
- token,
905
- end: isImage ? urlEnd + 1 : urlEnd
906
- };
907
- }
908
- function matchEmphasis(text, start) {
909
- const char = text[start];
910
- if (char !== "*" && char !== "_")
911
- return null;
912
- let count = 0;
913
- let i = start;
914
- while (i < text.length && text[i] === char && count < 3) {
915
- count++;
916
- i++;
917
- }
918
- if (count === 0)
919
- return null;
920
- const closePattern = char.repeat(count);
921
- let searchStart = i;
922
- while (searchStart < text.length) {
923
- const closeIndex = text.indexOf(closePattern, searchStart);
924
- if (closeIndex === -1)
925
- return null;
926
- if (text[closeIndex + count] === char) {
927
- searchStart = closeIndex + 1;
928
- continue;
929
- }
930
- if (text[closeIndex - 1] === " ") {
931
- searchStart = closeIndex + 1;
932
- continue;
933
- }
934
- const content = text.slice(i, closeIndex);
935
- if (!content.trim()) {
936
- searchStart = closeIndex + 1;
937
- continue;
938
- }
939
- const children = parseInlineTokens(content);
940
- if (count === 3) {
941
- return {
942
- token: {
943
- type: "bold",
944
- content,
945
- children: [{ type: "italic", content, children }]
946
- },
947
- end: closeIndex + count
948
- };
949
- } else if (count === 2) {
950
- return {
951
- token: { type: "bold", content, children },
952
- end: closeIndex + count
953
- };
954
- } else {
955
- return {
956
- token: { type: "italic", content, children },
957
- end: closeIndex + count
958
- };
959
- }
960
- }
961
- return null;
962
- }
963
- function matchStrikethrough(text, start) {
964
- if (text[start] !== "~" || text[start + 1] !== "~")
965
- return null;
966
- const closeIndex = text.indexOf("~~", start + 2);
967
- if (closeIndex === -1)
968
- return null;
969
- const content = text.slice(start + 2, closeIndex);
970
- if (!content.trim())
971
- return null;
972
- const children = parseInlineTokens(content);
973
- return {
974
- token: { type: "strikethrough", content, children },
975
- end: closeIndex + 2
976
- };
977
- }
978
- function matchHighlight(text, start) {
979
- if (text[start] !== "=" || text[start + 1] !== "=")
980
- return null;
981
- const closeIndex = text.indexOf("==", start + 2);
982
- if (closeIndex === -1)
983
- return null;
984
- const content = text.slice(start + 2, closeIndex);
985
- if (!content.trim())
986
- return null;
987
- const children = parseInlineTokens(content);
988
- return {
989
- token: { type: "highlight", content, children },
990
- end: closeIndex + 2
991
- };
992
- }
993
- function tokensToSpans(tokens) {
994
- const spans = [];
995
- for (const token of tokens) {
996
- const newSpans = tokenToSpans(token, {});
997
- spans.push(...newSpans);
998
- }
999
- return mergeAdjacentSpans(spans);
1000
- }
1001
- function tokenToSpans(token, inheritedStyles) {
1002
- switch (token.type) {
1003
- case "text":
1004
- return [{ text: token.content, styles: { ...inheritedStyles } }];
1005
- case "bold":
1006
- return processStyledToken(token, { ...inheritedStyles, bold: true });
1007
- case "italic":
1008
- return processStyledToken(token, { ...inheritedStyles, italic: true });
1009
- case "code":
1010
- return [{ text: token.content, styles: { ...inheritedStyles, code: true } }];
1011
- case "strikethrough":
1012
- return processStyledToken(token, { ...inheritedStyles, strikethrough: true });
1013
- case "highlight":
1014
- return processStyledToken(token, { ...inheritedStyles, highlight: true });
1015
- case "link":
1016
- return processStyledToken(token, {
1017
- ...inheritedStyles,
1018
- link: { url: token.url || "", title: token.title }
1019
- });
1020
- case "image":
1021
- return [{ text: `[image: ${token.content}]`, styles: inheritedStyles }];
1022
- default:
1023
- return [{ text: token.content, styles: inheritedStyles }];
1024
- }
1025
- }
1026
- function processStyledToken(token, styles) {
1027
- if (token.children && token.children.length > 0) {
1028
- const spans = [];
1029
- for (const child of token.children) {
1030
- spans.push(...tokenToSpans(child, styles));
1031
- }
1032
- return spans;
1033
- }
1034
- return [{ text: token.content, styles }];
1035
- }
1036
- function mergeAdjacentSpans(spans) {
1037
- if (spans.length === 0)
1038
- return [];
1039
- const merged = [spans[0]];
1040
- for (let i = 1;i < spans.length; i++) {
1041
- const current = spans[i];
1042
- const previous = merged[merged.length - 1];
1043
- if (stylesEqual(current.styles, previous.styles)) {
1044
- previous.text += current.text;
1045
- } else {
1046
- merged.push(current);
1047
- }
1048
- }
1049
- return merged;
1050
- }
1051
- function stylesEqual(a, b) {
1052
- return a.bold === b.bold && a.italic === b.italic && a.underline === b.underline && a.strikethrough === b.strikethrough && a.code === b.code && a.highlight === b.highlight && linksEqual(a.link, b.link);
1053
- }
1054
- function linksEqual(a, b) {
1055
- if (!a && !b)
1056
- return true;
1057
- if (!a || !b)
1058
- return false;
1059
- return a.url === b.url && a.title === b.title;
1060
- }
1061
- function tryMatchInline(text, pos) {
1062
- const matchers = [
1063
- () => matchCode(text, pos),
1064
- () => matchEmphasis(text, pos),
1065
- () => matchStrikethrough(text, pos),
1066
- () => matchHighlight(text, pos)
1067
- ];
1068
- for (const match of matchers) {
1069
- const result = match();
1070
- if (result) {
1071
- return {
1072
- token: result.token,
1073
- startIndex: pos,
1074
- endIndex: result.end
1075
- };
1076
- }
1077
- }
1078
- return null;
1079
- }
1080
-
1081
- // src/parsers/markdown.ts
1082
- var DEFAULT_OPTIONS = {
1083
- generateId,
1084
- strict: false
1085
- };
1086
- function markdownToBlocks(markdown, options = {}) {
1087
- const opts = { ...DEFAULT_OPTIONS, ...options };
1088
- const tokens = tokenize(markdown);
1089
- return parseTokens(tokens, opts);
1090
- }
1091
- function parseTokens(tokens, options) {
1092
- const blocks = [];
1093
- const state = {
1094
- options,
1095
- tokens,
1096
- index: 0
1097
- };
1098
- while (state.index < tokens.length) {
1099
- const token = tokens[state.index];
1100
- if (token.type === "blank") {
1101
- state.index++;
1102
- continue;
1103
- }
1104
- const block = parseBlock(state);
1105
- if (block) {
1106
- blocks.push(block);
1107
- }
1108
- }
1109
- return blocks;
1110
- }
1111
- function parseBlock(state) {
1112
- const token = state.tokens[state.index];
1113
- switch (token.type) {
1114
- case "heading":
1115
- return parseHeading(state);
1116
- case "paragraph":
1117
- return parseParagraph(state);
1118
- case "bullet_list_item":
1119
- return parseBulletList(state);
1120
- case "numbered_list_item":
1121
- return parseNumberedList(state);
1122
- case "check_list_item":
1123
- return parseCheckListItem(state);
1124
- case "code_fence_start":
1125
- return parseCodeBlock(state);
1126
- case "blockquote":
1127
- return parseBlockquote(state);
1128
- case "callout":
1129
- return parseCallout(state);
1130
- case "divider":
1131
- return parseDivider(state);
1132
- case "table_row":
1133
- return parseTable(state);
1134
- case "image":
1135
- return parseImage(state);
1136
- default:
1137
- state.index++;
1138
- return null;
1139
- }
1140
- }
1141
- function parseHeading(state) {
1142
- const token = state.tokens[state.index];
1143
- const level = token.meta?.level ?? 1;
1144
- const content = parseInlineContent(token.content);
1145
- state.index++;
1146
- return {
1147
- id: state.options.generateId(),
1148
- type: "heading",
1149
- content,
1150
- children: [],
1151
- props: { level }
1152
- };
1153
- }
1154
- function parseParagraph(state) {
1155
- const lines = [];
1156
- while (state.index < state.tokens.length && state.tokens[state.index].type === "paragraph") {
1157
- lines.push(state.tokens[state.index].content);
1158
- state.index++;
1159
- }
1160
- const content = parseInlineContent(lines.join(" "));
1161
- return {
1162
- id: state.options.generateId(),
1163
- type: "paragraph",
1164
- content,
1165
- children: [],
1166
- props: {}
1167
- };
1168
- }
1169
- function parseBulletList(state) {
1170
- const children = [];
1171
- const baseIndent = state.tokens[state.index].indent;
1172
- while (state.index < state.tokens.length && state.tokens[state.index].type === "bullet_list_item" && state.tokens[state.index].indent >= baseIndent) {
1173
- const token = state.tokens[state.index];
1174
- if (token.indent > baseIndent) {
1175
- state.index++;
1176
- continue;
1177
- }
1178
- const content = parseInlineContent(token.content);
1179
- children.push({
1180
- id: state.options.generateId(),
1181
- type: "paragraph",
1182
- content,
1183
- children: [],
1184
- props: {}
1185
- });
1186
- state.index++;
1187
- }
1188
- return {
1189
- id: state.options.generateId(),
1190
- type: "bulletList",
1191
- content: [],
1192
- children,
1193
- props: {}
1194
- };
1195
- }
1196
- function parseNumberedList(state) {
1197
- const children = [];
1198
- const baseIndent = state.tokens[state.index].indent;
1199
- while (state.index < state.tokens.length && state.tokens[state.index].type === "numbered_list_item" && state.tokens[state.index].indent >= baseIndent) {
1200
- const token = state.tokens[state.index];
1201
- if (token.indent > baseIndent) {
1202
- state.index++;
1203
- continue;
1204
- }
1205
- const content = parseInlineContent(token.content);
1206
- children.push({
1207
- id: state.options.generateId(),
1208
- type: "paragraph",
1209
- content,
1210
- children: [],
1211
- props: {}
1212
- });
1213
- state.index++;
1214
- }
1215
- return {
1216
- id: state.options.generateId(),
1217
- type: "numberedList",
1218
- content: [],
1219
- children,
1220
- props: {}
1221
- };
1222
- }
1223
- function parseCheckListItem(state) {
1224
- const token = state.tokens[state.index];
1225
- const checked = token.meta?.checked ?? false;
1226
- const content = parseInlineContent(token.content);
1227
- state.index++;
1228
- return {
1229
- id: state.options.generateId(),
1230
- type: "checkList",
1231
- content,
1232
- children: [],
1233
- props: { checked }
1234
- };
1235
- }
1236
- function parseCodeBlock(state) {
1237
- const startToken = state.tokens[state.index];
1238
- const language = startToken.meta?.language ?? "";
1239
- const codeLines = [];
1240
- state.index++;
1241
- while (state.index < state.tokens.length && state.tokens[state.index].type === "code_content") {
1242
- codeLines.push(state.tokens[state.index].content);
1243
- state.index++;
1244
- }
1245
- if (state.index < state.tokens.length && state.tokens[state.index].type === "code_fence_end") {
1246
- state.index++;
1247
- }
1248
- return {
1249
- id: state.options.generateId(),
1250
- type: "codeBlock",
1251
- content: plainContent(codeLines.join(`
1252
- `)),
1253
- children: [],
1254
- props: { language: language || undefined }
1255
- };
1256
- }
1257
- function parseBlockquote(state) {
1258
- const lines = [];
1259
- while (state.index < state.tokens.length && state.tokens[state.index].type === "blockquote") {
1260
- lines.push(state.tokens[state.index].content);
1261
- state.index++;
1262
- }
1263
- const content = parseInlineContent(lines.join(`
1264
- `));
1265
- return {
1266
- id: state.options.generateId(),
1267
- type: "blockquote",
1268
- content,
1269
- children: [],
1270
- props: {}
1271
- };
1272
- }
1273
- function parseCallout(state) {
1274
- const calloutToken = state.tokens[state.index];
1275
- const calloutType = calloutToken.meta?.calloutType ?? "note";
1276
- state.index++;
1277
- const lines = [];
1278
- while (state.index < state.tokens.length && state.tokens[state.index].type === "blockquote") {
1279
- lines.push(state.tokens[state.index].content);
1280
- state.index++;
1281
- }
1282
- const content = parseInlineContent(lines.join(`
1283
- `));
1284
- return {
1285
- id: state.options.generateId(),
1286
- type: "callout",
1287
- content,
1288
- children: [],
1289
- props: { type: calloutType }
1290
- };
1291
- }
1292
- function parseDivider(state) {
1293
- state.index++;
1294
- return {
1295
- id: state.options.generateId(),
1296
- type: "divider",
1297
- content: [],
1298
- children: [],
1299
- props: {}
1300
- };
1301
- }
1302
- function parseTable(state) {
1303
- const rows = [];
1304
- let headers = [];
1305
- let alignments = [];
1306
- let isFirstRow = true;
1307
- let hasSeparator = false;
1308
- while (state.index < state.tokens.length && (state.tokens[state.index].type === "table_row" || state.tokens[state.index].type === "table_separator")) {
1309
- const token = state.tokens[state.index];
1310
- if (token.type === "table_separator") {
1311
- alignments = parseTableAlignments(token.content);
1312
- hasSeparator = true;
1313
- state.index++;
1314
- continue;
1315
- }
1316
- const cells = token.content.split("|").map((cell) => cell.trim()).filter((cell) => cell !== "");
1317
- if (isFirstRow && !hasSeparator) {
1318
- headers = cells;
1319
- isFirstRow = false;
1320
- } else if (hasSeparator) {
1321
- rows.push(cells);
1322
- }
1323
- state.index++;
1324
- }
1325
- if (!hasSeparator && headers.length > 0) {
1326
- rows.unshift(headers);
1327
- headers = [];
1328
- }
1329
- return {
1330
- id: state.options.generateId(),
1331
- type: "table",
1332
- content: [],
1333
- children: [],
1334
- props: {
1335
- headers,
1336
- rows,
1337
- alignments: alignments.length > 0 ? alignments : undefined
1338
- }
1339
- };
1340
- }
1341
- function parseTableAlignments(separator) {
1342
- return separator.split("|").map((cell) => cell.trim()).filter((cell) => cell !== "").map((cell) => {
1343
- const leftColon = cell.startsWith(":");
1344
- const rightColon = cell.endsWith(":");
1345
- if (leftColon && rightColon)
1346
- return "center";
1347
- if (leftColon)
1348
- return "left";
1349
- if (rightColon)
1350
- return "right";
1351
- return null;
1352
- });
1353
- }
1354
- function parseImage(state) {
1355
- const token = state.tokens[state.index];
1356
- const url = token.content;
1357
- const alt = token.meta?.language ?? "";
1358
- state.index++;
1359
- return {
1360
- id: state.options.generateId(),
1361
- type: "image",
1362
- content: [],
1363
- children: [],
1364
- props: {
1365
- url,
1366
- alt: alt || undefined
1367
- }
1368
- };
1369
- }
1370
-
1371
- // src/serializers/markdown.ts
1372
- var DEFAULT_OPTIONS2 = {
1373
- lineEnding: `
1374
- `,
1375
- listIndent: 2,
1376
- headingStyle: "atx",
1377
- codeBlockStyle: "fenced",
1378
- bulletChar: "-",
1379
- emphasisChar: "*"
1380
- };
1381
- function blocksToMarkdown(blocks, options = {}) {
1382
- const opts = { ...DEFAULT_OPTIONS2, ...options };
1383
- const serializedBlocks = blocks.map((block, index) => serializeBlock(block, 0, opts, index, blocks));
1384
- return serializedBlocks.filter((s) => s !== null).join(opts.lineEnding + opts.lineEnding);
1385
- }
1386
- function serializeBlock(block, depth, options, index = 0, siblings = []) {
1387
- switch (block.type) {
1388
- case "paragraph":
1389
- return serializeParagraph(block, options);
1390
- case "heading":
1391
- return serializeHeading(block, options);
1392
- case "bulletList":
1393
- return serializeBulletList(block, depth, options);
1394
- case "numberedList":
1395
- return serializeNumberedList(block, depth, options, index, siblings);
1396
- case "checkList":
1397
- return serializeCheckList(block, depth, options);
1398
- case "codeBlock":
1399
- return serializeCodeBlock(block, options);
1400
- case "blockquote":
1401
- return serializeBlockquote(block, options);
1402
- case "table":
1403
- return serializeTable(block, options);
1404
- case "image":
1405
- return serializeImage(block);
1406
- case "divider":
1407
- return "---";
1408
- case "callout":
1409
- return serializeCallout(block, options);
1410
- default:
1411
- return serializeInlineContent(block.content, options);
1412
- }
1413
- }
1414
- function serializeParagraph(block, options) {
1415
- return serializeInlineContent(block.content, options);
1416
- }
1417
- function serializeHeading(block, options) {
1418
- const level = block.props.level;
1419
- const content = serializeInlineContent(block.content, options);
1420
- if (options.headingStyle === "setext" && (level === 1 || level === 2)) {
1421
- const underline = level === 1 ? "=" : "-";
1422
- return `${content}${options.lineEnding}${underline.repeat(content.length)}`;
1423
- }
1424
- return `${"#".repeat(level)} ${content}`;
1425
- }
1426
- function serializeBulletList(block, depth, options) {
1427
- const indent = " ".repeat(depth * options.listIndent);
1428
- const bullet = options.bulletChar;
1429
- return block.children.map((child) => {
1430
- const content = serializeListItemContent(child, depth, options);
1431
- return `${indent}${bullet} ${content}`;
1432
- }).join(options.lineEnding);
1433
- }
1434
- function serializeNumberedList(block, depth, options, _blockIndex = 0, _siblings = []) {
1435
- const indent = " ".repeat(depth * options.listIndent);
1436
- return block.children.map((child, index) => {
1437
- const content = serializeListItemContent(child, depth, options);
1438
- return `${indent}${index + 1}. ${content}`;
1439
- }).join(options.lineEnding);
1440
- }
1441
- function serializeCheckList(block, depth, options) {
1442
- const indent = " ".repeat(depth * options.listIndent);
1443
- const checked = block.props.checked ? "x" : " ";
1444
- const content = serializeInlineContent(block.content, options);
1445
- return `${indent}- [${checked}] ${content}`;
1446
- }
1447
- function serializeListItemContent(item, depth, options) {
1448
- if (item.children.length > 0) {
1449
- const content = serializeInlineContent(item.content, options);
1450
- const nestedContent = item.children.map((child, index) => serializeBlock(child, depth + 1, options, index, item.children)).join(options.lineEnding);
1451
- return `${content}${options.lineEnding}${nestedContent}`;
1452
- }
1453
- return serializeInlineContent(item.content, options);
1454
- }
1455
- function serializeCodeBlock(block, options) {
1456
- const code = spansToPlainText(block.content);
1457
- const language = block.props.language ?? "";
1458
- if (options.codeBlockStyle === "indented") {
1459
- return code.split(`
1460
- `).map((line) => ` ${line}`).join(options.lineEnding);
1461
- }
1462
- return `\`\`\`${language}${options.lineEnding}${code}${options.lineEnding}\`\`\``;
1463
- }
1464
- function serializeBlockquote(block, options) {
1465
- const content = serializeInlineContent(block.content, options);
1466
- return content.split(`
1467
- `).map((line) => `> ${line}`).join(options.lineEnding);
1468
- }
1469
- function serializeImage(block) {
1470
- const alt = block.props.alt ?? "";
1471
- const url = block.props.url ?? "";
1472
- const title = block.props.title;
1473
- if (title) {
1474
- return `![${alt}](${url} "${title}")`;
1475
- }
1476
- return `![${alt}](${url})`;
1477
- }
1478
- function serializeTable(block, options) {
1479
- const { headers, rows, alignments } = block.props;
1480
- const lineEnding = options.lineEnding;
1481
- const columnWidths = headers.map((header, i) => {
1482
- const rowWidths = rows.map((row) => (row[i] ?? "").length);
1483
- return Math.max(header.length, ...rowWidths, 3);
1484
- });
1485
- const headerRow = headers.map((header, i) => header.padEnd(columnWidths[i])).join(" | ");
1486
- const separatorRow = headers.map((_, i) => {
1487
- const width = columnWidths[i];
1488
- const alignment = alignments?.[i] ?? null;
1489
- if (alignment === "left")
1490
- return `:${"-".repeat(width - 1)}`;
1491
- if (alignment === "right")
1492
- return `${"-".repeat(width - 1)}:`;
1493
- if (alignment === "center")
1494
- return `:${"-".repeat(width - 2)}:`;
1495
- return "-".repeat(width);
1496
- }).join(" | ");
1497
- const dataRows = rows.map((row) => row.map((cell, i) => (cell ?? "").padEnd(columnWidths[i])).join(" | "));
1498
- return [
1499
- `| ${headerRow} |`,
1500
- `| ${separatorRow} |`,
1501
- ...dataRows.map((row) => `| ${row} |`)
1502
- ].join(lineEnding);
1503
- }
1504
- function serializeCallout(block, options) {
1505
- const type = block.props.type.toUpperCase();
1506
- const content = serializeInlineContent(block.content, options);
1507
- const lineEnding = options.lineEnding;
1508
- return `> [!${type}]${lineEnding}> ${content.split(`
1509
- `).join(`${lineEnding}> `)}`;
1510
- }
1511
- function serializeInlineContent(spans, options) {
1512
- return spans.map((span) => serializeSpan(span, options)).join("");
1513
- }
1514
- function serializeSpan(span, options) {
1515
- let text = span.text;
1516
- const styles = span.styles;
1517
- if (!hasStyles(styles)) {
1518
- return text;
1519
- }
1520
- if (styles.code) {
1521
- text = `\`${text}\``;
1522
- }
1523
- if (styles.highlight) {
1524
- text = `==${text}==`;
1525
- }
1526
- if (styles.strikethrough) {
1527
- text = `~~${text}~~`;
1528
- }
1529
- if (styles.bold && styles.italic) {
1530
- const char = options.emphasisChar;
1531
- text = `${char}${char}${char}${text}${char}${char}${char}`;
1532
- } else {
1533
- if (styles.italic) {
1534
- const char = options.emphasisChar;
1535
- text = `${char}${text}${char}`;
1536
- }
1537
- if (styles.bold) {
1538
- const char = options.emphasisChar;
1539
- text = `${char}${char}${text}${char}${char}`;
1540
- }
1541
- }
1542
- if (styles.underline) {
1543
- text = `<u>${text}</u>`;
1544
- }
1545
- if (styles.link) {
1546
- const { url, title } = styles.link;
1547
- if (title) {
1548
- text = `[${text}](${url} "${title}")`;
1549
- } else {
1550
- text = `[${text}](${url})`;
1551
- }
1552
- }
1553
- return text;
1554
- }
1555
- function hasStyles(styles) {
1556
- return !!(styles.bold || styles.italic || styles.underline || styles.strikethrough || styles.code || styles.highlight || styles.link);
1557
- }
1558
-
1559
- // src/react/hooks.ts
1560
- function useDocument(initialBlocks = [], options) {
1561
- const [document, setDocument] = import_react.useState(() => createDocument(initialBlocks, options));
1562
- const insert = import_react.useCallback((block, index) => {
1563
- setDocument((doc) => insertBlock(doc, block, index));
1564
- }, []);
1565
- const append = import_react.useCallback((block) => {
1566
- setDocument((doc) => appendBlock(doc, block));
1567
- }, []);
1568
- const remove = import_react.useCallback((blockId) => {
1569
- setDocument((doc) => removeBlock(doc, blockId));
1570
- }, []);
1571
- const update = import_react.useCallback((blockId, updates) => {
1572
- setDocument((doc) => updateBlock(doc, blockId, updates));
1573
- }, []);
1574
- const move = import_react.useCallback((blockId, newIndex) => {
1575
- setDocument((doc) => moveBlock(doc, blockId, newIndex));
1576
- }, []);
1577
- const find = import_react.useCallback((blockId) => findBlock(document, blockId), [document]);
1578
- const getIndex = import_react.useCallback((blockId) => getBlockIndex(document, blockId), [document]);
1579
- const clear = import_react.useCallback(() => {
1580
- setDocument((doc) => clearBlocks(doc));
1581
- }, []);
1582
- const set = import_react.useCallback((blocks) => {
1583
- setDocument((doc) => setBlocks(doc, blocks));
1584
- }, []);
1585
- const toMd = import_react.useCallback(() => blocksToMarkdown(document.blocks), [document]);
1586
- const fromMd = import_react.useCallback((markdown) => {
1587
- const blocks = markdownToBlocks(markdown);
1588
- setDocument((doc) => setBlocks(doc, blocks));
1589
- }, []);
1590
- const meta = import_react.useCallback((newMeta) => {
1591
- setDocument((doc) => updateMeta(doc, newMeta));
1592
- }, []);
1593
- return {
1594
- document,
1595
- blocks: document.blocks,
1596
- insertBlock: insert,
1597
- appendBlock: append,
1598
- removeBlock: remove,
1599
- updateBlock: update,
1600
- moveBlock: move,
1601
- findBlock: find,
1602
- getBlockIndex: getIndex,
1603
- clearBlocks: clear,
1604
- setBlocks: set,
1605
- setDocument,
1606
- toMarkdown: toMd,
1607
- fromMarkdown: fromMd,
1608
- updateMeta: meta
1609
- };
1610
- }
1611
- function useMarkdown(initialMarkdown = "") {
1612
- const [markdown, setMarkdownState] = import_react.useState(initialMarkdown);
1613
- const blocks = import_react.useMemo(() => markdownToBlocks(markdown), [markdown]);
1614
- const setMarkdown = import_react.useCallback((newMarkdown) => {
1615
- setMarkdownState(newMarkdown);
1616
- }, []);
1617
- const setBlocksFromBlocks = import_react.useCallback((newBlocks) => {
1618
- const newMarkdown = blocksToMarkdown(newBlocks);
1619
- setMarkdownState(newMarkdown);
1620
- }, []);
1621
- return {
1622
- markdown,
1623
- blocks,
1624
- setMarkdown,
1625
- setBlocks: setBlocksFromBlocks
1626
- };
1627
- }
1628
- function useBlockEditor(documentHook) {
1629
- const { document, blocks, removeBlock: removeBlock2, updateBlock: updateBlock2, moveBlock: moveBlock2, insertBlock: insertBlock2 } = documentHook;
1630
- const [selectedBlockId, setSelectedBlockId] = import_react.useState(null);
1631
- const selectedBlock = import_react.useMemo(() => selectedBlockId ? findBlock(document, selectedBlockId) : undefined, [document, selectedBlockId]);
1632
- const selectBlock = import_react.useCallback((blockId) => {
1633
- setSelectedBlockId(blockId);
1634
- }, []);
1635
- const selectNext = import_react.useCallback(() => {
1636
- if (!selectedBlockId) {
1637
- if (blocks.length > 0) {
1638
- setSelectedBlockId(blocks[0].id);
1639
- }
1640
- return;
1641
- }
1642
- const currentIndex = getBlockIndex(document, selectedBlockId);
1643
- if (currentIndex < blocks.length - 1) {
1644
- setSelectedBlockId(blocks[currentIndex + 1].id);
1645
- }
1646
- }, [document, blocks, selectedBlockId]);
1647
- const selectPrevious = import_react.useCallback(() => {
1648
- if (!selectedBlockId) {
1649
- if (blocks.length > 0) {
1650
- setSelectedBlockId(blocks[blocks.length - 1].id);
1651
- }
1652
- return;
1653
- }
1654
- const currentIndex = getBlockIndex(document, selectedBlockId);
1655
- if (currentIndex > 0) {
1656
- setSelectedBlockId(blocks[currentIndex - 1].id);
1657
- }
1658
- }, [document, blocks, selectedBlockId]);
1659
- const deleteSelected = import_react.useCallback(() => {
1660
- if (!selectedBlockId)
1661
- return;
1662
- const currentIndex = getBlockIndex(document, selectedBlockId);
1663
- removeBlock2(selectedBlockId);
1664
- if (blocks.length > 1) {
1665
- if (currentIndex < blocks.length - 1) {
1666
- setSelectedBlockId(blocks[currentIndex + 1].id);
1667
- } else if (currentIndex > 0) {
1668
- setSelectedBlockId(blocks[currentIndex - 1].id);
1669
- }
1670
- } else {
1671
- setSelectedBlockId(null);
1672
- }
1673
- }, [document, blocks, selectedBlockId, removeBlock2]);
1674
- const updateSelectedContent = import_react.useCallback((content) => {
1675
- if (!selectedBlockId)
1676
- return;
1677
- updateBlock2(selectedBlockId, { content });
1678
- }, [selectedBlockId, updateBlock2]);
1679
- const duplicateSelected = import_react.useCallback(() => {
1680
- if (!selectedBlockId || !selectedBlock)
1681
- return;
1682
- const currentIndex = getBlockIndex(document, selectedBlockId);
1683
- const clonedBlock = {
1684
- ...selectedBlock,
1685
- id: Math.random().toString(36).substring(2, 9)
1686
- };
1687
- insertBlock2(clonedBlock, currentIndex + 1);
1688
- setSelectedBlockId(clonedBlock.id);
1689
- }, [document, selectedBlockId, selectedBlock, insertBlock2]);
1690
- const moveSelectedUp = import_react.useCallback(() => {
1691
- if (!selectedBlockId)
1692
- return;
1693
- const currentIndex = getBlockIndex(document, selectedBlockId);
1694
- if (currentIndex > 0) {
1695
- moveBlock2(selectedBlockId, currentIndex - 1);
1696
- }
1697
- }, [document, selectedBlockId, moveBlock2]);
1698
- const moveSelectedDown = import_react.useCallback(() => {
1699
- if (!selectedBlockId)
1700
- return;
1701
- const currentIndex = getBlockIndex(document, selectedBlockId);
1702
- if (currentIndex < blocks.length - 1) {
1703
- moveBlock2(selectedBlockId, currentIndex + 1);
1704
- }
1705
- }, [document, blocks, selectedBlockId, moveBlock2]);
1706
- return {
1707
- selectedBlockId,
1708
- selectedBlock,
1709
- selectBlock,
1710
- selectNext,
1711
- selectPrevious,
1712
- deleteSelected,
1713
- updateSelectedContent,
1714
- duplicateSelected,
1715
- moveSelectedUp,
1716
- moveSelectedDown
1717
- };
1718
- }
1719
- // src/core/blocks.ts
1720
- function createBlock(type, content = [], props = {}, children = []) {
1721
- return {
1722
- id: generateId(),
1723
- type,
1724
- content,
1725
- children,
1726
- props
1727
- };
1728
- }
1729
- function text(content) {
1730
- return plainSpan(content);
1731
- }
1732
- function bold(content) {
1733
- return { text: content, styles: { bold: true } };
1734
- }
1735
- function italic(content) {
1736
- return { text: content, styles: { italic: true } };
1737
- }
1738
- function code(content) {
1739
- return { text: content, styles: { code: true } };
1740
- }
1741
- function link(content, url, title) {
1742
- return {
1743
- text: content,
1744
- styles: { link: { url, title } }
1745
- };
1746
- }
1747
- function paragraph(content) {
1748
- const textContent = typeof content === "string" ? plainContent(content) : content;
1749
- return createBlock("paragraph", textContent, {});
1750
- }
1751
- function heading(level, content) {
1752
- const textContent = typeof content === "string" ? plainContent(content) : content;
1753
- return createBlock("heading", textContent, { level });
1754
- }
1755
- var h1 = (content) => heading(1, content);
1756
- var h2 = (content) => heading(2, content);
1757
- var h3 = (content) => heading(3, content);
1758
- var h4 = (content) => heading(4, content);
1759
- var h5 = (content) => heading(5, content);
1760
- var h6 = (content) => heading(6, content);
1761
- function bulletList(items) {
1762
- const children = items.map((item) => {
1763
- if (typeof item === "string") {
1764
- return paragraph(item);
1765
- }
1766
- if (Array.isArray(item)) {
1767
- return paragraph(item);
1768
- }
1769
- return item;
1770
- });
1771
- return createBlock("bulletList", [], {}, children);
1772
- }
1773
- function numberedList(items) {
1774
- const children = items.map((item) => {
1775
- if (typeof item === "string") {
1776
- return paragraph(item);
1777
- }
1778
- if (Array.isArray(item)) {
1779
- return paragraph(item);
1780
- }
1781
- return item;
1782
- });
1783
- return createBlock("numberedList", [], {}, children);
1784
- }
1785
- function checkListItem(content, checked = false) {
1786
- const textContent = typeof content === "string" ? plainContent(content) : content;
1787
- return createBlock("checkList", textContent, { checked });
1788
- }
1789
- function checkList(items) {
1790
- return items.map((item) => checkListItem(item.content, item.checked ?? false));
1791
- }
1792
- function codeBlock(code2, language) {
1793
- return createBlock("codeBlock", plainContent(code2), { language });
1794
- }
1795
- function blockquote(content) {
1796
- const textContent = typeof content === "string" ? plainContent(content) : content;
1797
- return createBlock("blockquote", textContent, {});
1798
- }
1799
- function divider() {
1800
- return createBlock("divider", [], {});
1801
- }
1802
- function image(url, alt, options) {
1803
- return createBlock("image", [], {
1804
- url,
1805
- alt,
1806
- title: options?.title,
1807
- width: options?.width,
1808
- height: options?.height
1809
- });
1810
- }
1811
- function callout(type, content) {
1812
- const textContent = typeof content === "string" ? plainContent(content) : content;
1813
- return createBlock("callout", textContent, { type });
1814
- }