@haklex/rich-litexml 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,1234 +1,1210 @@
1
+ import { customAlphabet } from "nanoid";
1
2
  import { parseHTML } from "linkedom";
3
+ //#region src/readers/builtin.ts
2
4
  function extractBlockId$1(el) {
3
- const id = el.getAttribute("id");
4
- return id ? { $: { blockId: id } } : {};
5
+ const id = el.getAttribute("id");
6
+ return id ? { $: { blockId: id } } : {};
5
7
  }
6
- const ELEMENT_DEFAULTS = {
7
- direction: "ltr",
8
- format: "",
9
- indent: 0,
10
- textFormat: 0,
11
- textStyle: "",
12
- version: 1
8
+ var ELEMENT_DEFAULTS = {
9
+ direction: "ltr",
10
+ format: "",
11
+ indent: 0,
12
+ textFormat: 0,
13
+ textStyle: "",
14
+ version: 1
13
15
  };
14
16
  function registerBuiltinReaders(registry) {
15
- registry.registerReader(
16
- "p",
17
- (el, ctx) => ({
18
- type: "paragraph",
19
- ...extractBlockId$1(el),
20
- children: ctx.parseChildren(el),
21
- ...ELEMENT_DEFAULTS
22
- })
23
- );
24
- for (let level = 1; level <= 6; level++) {
25
- registry.registerReader(
26
- `h${level}`,
27
- (el, ctx) => ({
28
- type: "heading",
29
- tag: `h${level}`,
30
- ...extractBlockId$1(el),
31
- children: ctx.parseChildren(el),
32
- ...ELEMENT_DEFAULTS
33
- })
34
- );
35
- }
36
- registry.registerReader(
37
- "blockquote",
38
- (el, ctx) => ({
39
- type: "quote",
40
- ...extractBlockId$1(el),
41
- children: ctx.parseChildren(el),
42
- ...ELEMENT_DEFAULTS
43
- })
44
- );
45
- registry.registerReader(
46
- "hr",
47
- (el) => ({
48
- type: "horizontalrule",
49
- ...extractBlockId$1(el),
50
- version: 1
51
- })
52
- );
53
- registry.registerReader("ul", (el, ctx) => {
54
- const isCheck = el.getAttribute("type") === "check";
55
- return {
56
- type: "list",
57
- listType: isCheck ? "check" : "bullet",
58
- tag: "ul",
59
- start: 1,
60
- ...extractBlockId$1(el),
61
- children: ctx.parseChildren(el),
62
- direction: "ltr",
63
- format: "",
64
- indent: 0,
65
- version: 1
66
- };
67
- });
68
- registry.registerReader(
69
- "ol",
70
- (el, ctx) => ({
71
- type: "list",
72
- listType: "number",
73
- tag: "ol",
74
- start: Number(el.getAttribute("start") ?? 1),
75
- ...extractBlockId$1(el),
76
- children: ctx.parseChildren(el),
77
- direction: "ltr",
78
- format: "",
79
- indent: 0,
80
- version: 1
81
- })
82
- );
83
- registry.registerReader("li", (el, ctx) => {
84
- const checked = el.getAttribute("checked");
85
- return {
86
- type: "listitem",
87
- ...extractBlockId$1(el),
88
- ...checked !== null ? { checked: checked === "true" } : {},
89
- children: ctx.parseChildren(el),
90
- ...ELEMENT_DEFAULTS,
91
- value: 1
92
- };
93
- });
94
- registry.registerReader(
95
- "a",
96
- (el, ctx) => ({
97
- type: "link",
98
- url: el.getAttribute("href") ?? "",
99
- target: el.getAttribute("target") ?? null,
100
- title: el.getAttribute("title") ?? null,
101
- rel: null,
102
- children: ctx.parseChildren(el),
103
- direction: "ltr",
104
- format: "",
105
- indent: 0,
106
- version: 1
107
- })
108
- );
109
- registry.registerReader(
110
- "table",
111
- (el, ctx) => ({
112
- type: "table",
113
- ...extractBlockId$1(el),
114
- children: ctx.parseChildren(el),
115
- direction: "ltr",
116
- format: "",
117
- indent: 0,
118
- version: 1
119
- })
120
- );
121
- registry.registerReader(
122
- "tr",
123
- (el, ctx) => ({
124
- type: "tablerow",
125
- children: ctx.parseChildren(el),
126
- direction: "ltr",
127
- format: "",
128
- indent: 0,
129
- version: 1
130
- })
131
- );
132
- registry.registerReader(
133
- "th",
134
- (el, ctx) => ({
135
- type: "tablecell",
136
- headerState: 1,
137
- children: ctx.parseChildren(el),
138
- direction: "ltr",
139
- format: "",
140
- indent: 0,
141
- version: 1
142
- })
143
- );
144
- registry.registerReader(
145
- "td",
146
- (el, ctx) => ({
147
- type: "tablecell",
148
- headerState: 0,
149
- children: ctx.parseChildren(el),
150
- direction: "ltr",
151
- format: "",
152
- indent: 0,
153
- version: 1
154
- })
155
- );
17
+ registry.registerReader("p", (el, ctx) => ({
18
+ type: "paragraph",
19
+ ...extractBlockId$1(el),
20
+ children: ctx.parseChildren(el),
21
+ ...ELEMENT_DEFAULTS
22
+ }));
23
+ for (let level = 1; level <= 6; level++) registry.registerReader(`h${level}`, (el, ctx) => ({
24
+ type: "heading",
25
+ tag: `h${level}`,
26
+ ...extractBlockId$1(el),
27
+ children: ctx.parseChildren(el),
28
+ ...ELEMENT_DEFAULTS
29
+ }));
30
+ registry.registerReader("blockquote", (el, ctx) => ({
31
+ type: "quote",
32
+ ...extractBlockId$1(el),
33
+ children: ctx.parseChildren(el),
34
+ ...ELEMENT_DEFAULTS
35
+ }));
36
+ registry.registerReader("hr", (el) => ({
37
+ type: "horizontalrule",
38
+ ...extractBlockId$1(el),
39
+ version: 1
40
+ }));
41
+ registry.registerReader("ul", (el, ctx) => {
42
+ return {
43
+ type: "list",
44
+ listType: el.getAttribute("type") === "check" ? "check" : "bullet",
45
+ tag: "ul",
46
+ start: 1,
47
+ ...extractBlockId$1(el),
48
+ children: ctx.parseChildren(el),
49
+ direction: "ltr",
50
+ format: "",
51
+ indent: 0,
52
+ version: 1
53
+ };
54
+ });
55
+ registry.registerReader("ol", (el, ctx) => ({
56
+ type: "list",
57
+ listType: "number",
58
+ tag: "ol",
59
+ start: Number(el.getAttribute("start") ?? 1),
60
+ ...extractBlockId$1(el),
61
+ children: ctx.parseChildren(el),
62
+ direction: "ltr",
63
+ format: "",
64
+ indent: 0,
65
+ version: 1
66
+ }));
67
+ registry.registerReader("li", (el, ctx) => {
68
+ const checked = el.getAttribute("checked");
69
+ return {
70
+ type: "listitem",
71
+ ...extractBlockId$1(el),
72
+ ...checked !== null ? { checked: checked === "true" } : {},
73
+ children: ctx.parseChildren(el),
74
+ ...ELEMENT_DEFAULTS,
75
+ value: 1
76
+ };
77
+ });
78
+ registry.registerReader("a", (el, ctx) => ({
79
+ type: "link",
80
+ url: el.getAttribute("href") ?? "",
81
+ target: el.getAttribute("target") ?? null,
82
+ title: el.getAttribute("title") ?? null,
83
+ rel: null,
84
+ children: ctx.parseChildren(el),
85
+ direction: "ltr",
86
+ format: "",
87
+ indent: 0,
88
+ version: 1
89
+ }));
90
+ registry.registerReader("table", (el, ctx) => ({
91
+ type: "table",
92
+ ...extractBlockId$1(el),
93
+ children: ctx.parseChildren(el),
94
+ direction: "ltr",
95
+ format: "",
96
+ indent: 0,
97
+ version: 1
98
+ }));
99
+ registry.registerReader("tr", (el, ctx) => ({
100
+ type: "tablerow",
101
+ children: ctx.parseChildren(el),
102
+ direction: "ltr",
103
+ format: "",
104
+ indent: 0,
105
+ version: 1
106
+ }));
107
+ registry.registerReader("th", (el, ctx) => ({
108
+ type: "tablecell",
109
+ headerState: 1,
110
+ children: ctx.parseChildren(el),
111
+ direction: "ltr",
112
+ format: "",
113
+ indent: 0,
114
+ version: 1
115
+ }));
116
+ registry.registerReader("td", (el, ctx) => ({
117
+ type: "tablecell",
118
+ headerState: 0,
119
+ children: ctx.parseChildren(el),
120
+ direction: "ltr",
121
+ format: "",
122
+ indent: 0,
123
+ version: 1
124
+ }));
156
125
  }
126
+ //#endregion
127
+ //#region src/readers/custom.ts
128
+ var idAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
129
+ var makePollIdSuffix = customAlphabet(idAlphabet, 10);
130
+ var makeOptionIdSuffix = customAlphabet(idAlphabet, 6);
157
131
  function extractBlockId(el) {
158
- const id = el.getAttribute("id");
159
- return id ? { $: { blockId: id } } : {};
132
+ const id = el.getAttribute("id");
133
+ return id ? { $: { blockId: id } } : {};
160
134
  }
161
135
  function numAttr(el, name) {
162
- const v = el.getAttribute(name);
163
- return v !== null ? Number(v) : void 0;
136
+ const v = el.getAttribute(name);
137
+ return v !== null ? Number(v) : void 0;
164
138
  }
139
+ /**
140
+ * Extract CDATA text content from an element.
141
+ * linkedom (HTML parser) converts <![CDATA[...]]> to a comment node
142
+ * with value "[CDATA[...]]", so we detect that pattern.
143
+ */
165
144
  function extractCdataText(el) {
166
- for (const child of el.childNodes) {
167
- if (child.nodeType === 8) {
168
- const val = child.nodeValue ?? "";
169
- if (val.startsWith("[CDATA[") && val.endsWith("]]")) {
170
- return val.slice(7, -2);
171
- }
172
- }
173
- }
174
- return el.textContent?.trim() ?? "";
145
+ for (const child of el.childNodes) if (child.nodeType === 8) {
146
+ const val = child.nodeValue ?? "";
147
+ if (val.startsWith("[CDATA[") && val.endsWith("]]")) return val.slice(7, -2);
148
+ }
149
+ return el.textContent?.trim() ?? "";
175
150
  }
176
151
  function registerCustomReaders(registry) {
177
- registry.registerReader("img", (el) => {
178
- if (el.parentElement?.tagName.toLowerCase() === "gallery") return false;
179
- return {
180
- type: "image",
181
- ...extractBlockId(el),
182
- src: el.getAttribute("src") ?? "",
183
- altText: el.getAttribute("alt") ?? "",
184
- width: numAttr(el, "width"),
185
- height: numAttr(el, "height"),
186
- caption: el.getAttribute("caption") ?? void 0,
187
- thumbhash: el.getAttribute("thumbhash") ?? void 0,
188
- accent: el.getAttribute("accent") ?? void 0,
189
- version: 1
190
- };
191
- });
192
- registry.registerReader(
193
- "video",
194
- (el) => ({
195
- type: "video",
196
- ...extractBlockId(el),
197
- src: el.getAttribute("src") ?? "",
198
- poster: el.getAttribute("poster") ?? void 0,
199
- width: numAttr(el, "width"),
200
- height: numAttr(el, "height"),
201
- version: 1
202
- })
203
- );
204
- registry.registerReader(
205
- "linkcard",
206
- (el) => ({
207
- type: "link-card",
208
- ...extractBlockId(el),
209
- url: el.getAttribute("url") ?? "",
210
- source: el.getAttribute("source") ?? void 0,
211
- title: el.getAttribute("title") ?? void 0,
212
- description: el.getAttribute("description") ?? void 0,
213
- favicon: el.getAttribute("favicon") ?? void 0,
214
- image: el.getAttribute("image") ?? void 0,
215
- version: 1
216
- })
217
- );
218
- registry.registerReader(
219
- "embed",
220
- (el) => ({
221
- type: "embed",
222
- ...extractBlockId(el),
223
- url: el.getAttribute("url") ?? "",
224
- source: el.getAttribute("source") ?? null,
225
- version: 1
226
- })
227
- );
228
- registry.registerReader(
229
- "codeblock",
230
- (el) => ({
231
- type: "code-block",
232
- ...extractBlockId(el),
233
- code: el.textContent ?? "",
234
- language: el.getAttribute("lang") ?? "",
235
- version: 1
236
- })
237
- );
238
- registry.registerReader(
239
- "mermaid",
240
- (el) => ({
241
- type: "mermaid",
242
- ...extractBlockId(el),
243
- diagram: el.textContent ?? "",
244
- version: 1
245
- })
246
- );
247
- registry.registerReader("math", (el) => {
248
- const display = el.getAttribute("display");
249
- if (display === "block") {
250
- return {
251
- type: "katex-block",
252
- ...extractBlockId(el),
253
- equation: el.textContent ?? "",
254
- version: 1
255
- };
256
- }
257
- const color = el.getAttribute("color");
258
- return {
259
- type: "katex-inline",
260
- equation: el.textContent ?? "",
261
- ...color ? { color } : {},
262
- version: 1
263
- };
264
- });
265
- registry.registerReader("alert", (el, ctx) => {
266
- const children = ctx.parseChildren(el);
267
- const content = {
268
- root: {
269
- type: "root",
270
- children,
271
- direction: "ltr",
272
- format: "",
273
- indent: 0,
274
- version: 1
275
- }
276
- };
277
- return {
278
- type: "alert-quote",
279
- ...extractBlockId(el),
280
- alertType: el.getAttribute("type") ?? "note",
281
- content,
282
- version: 1
283
- };
284
- });
285
- registry.registerReader("banner", (el, ctx) => {
286
- const children = ctx.parseChildren(el);
287
- const content = {
288
- root: {
289
- type: "root",
290
- children,
291
- direction: "ltr",
292
- format: "",
293
- indent: 0,
294
- version: 1
295
- }
296
- };
297
- return {
298
- type: "banner",
299
- ...extractBlockId(el),
300
- bannerType: el.getAttribute("type") ?? "note",
301
- content,
302
- version: 1
303
- };
304
- });
305
- registry.registerReader("nesteddoc", (el, ctx) => {
306
- const children = ctx.parseChildren(el);
307
- const content = {
308
- root: {
309
- type: "root",
310
- children,
311
- direction: "ltr",
312
- format: "",
313
- indent: 0,
314
- version: 1
315
- }
316
- };
317
- return {
318
- type: "nested-doc",
319
- ...extractBlockId(el),
320
- content,
321
- version: 1
322
- };
323
- });
324
- registry.registerReader(
325
- "details",
326
- (el, ctx) => ({
327
- type: "details",
328
- ...extractBlockId(el),
329
- summary: el.getAttribute("summary") ?? "",
330
- open: el.getAttribute("open") === "true",
331
- children: ctx.parseChildren(el),
332
- direction: "ltr",
333
- format: "",
334
- indent: 0,
335
- version: 1
336
- })
337
- );
338
- registry.registerReader(
339
- "spoiler",
340
- (el, ctx) => ({
341
- type: "spoiler",
342
- children: ctx.parseChildren(el),
343
- direction: "ltr",
344
- format: "",
345
- indent: 0,
346
- textFormat: 0,
347
- textStyle: "",
348
- version: 1
349
- })
350
- );
351
- registry.registerReader(
352
- "ruby",
353
- (el, ctx) => ({
354
- type: "ruby",
355
- reading: el.getAttribute("rt") ?? "",
356
- children: ctx.parseChildren(el),
357
- direction: "ltr",
358
- format: "",
359
- indent: 0,
360
- textFormat: 0,
361
- textStyle: "",
362
- version: 1
363
- })
364
- );
365
- registry.registerReader(
366
- "mention",
367
- (el) => ({
368
- type: "mention",
369
- platform: el.getAttribute("platform") ?? "",
370
- handle: el.getAttribute("handle") ?? "",
371
- displayName: el.textContent || void 0,
372
- version: 1
373
- })
374
- );
375
- registry.registerReader(
376
- "tag",
377
- (el) => ({
378
- type: "tag",
379
- text: el.textContent ?? "",
380
- format: 0,
381
- detail: 0,
382
- mode: "normal",
383
- style: "",
384
- version: 1
385
- })
386
- );
387
- registry.registerReader(
388
- "comment",
389
- (el) => ({
390
- type: "comment",
391
- text: el.textContent ?? "",
392
- format: 0,
393
- detail: 0,
394
- mode: "normal",
395
- style: "",
396
- version: 1
397
- })
398
- );
399
- registry.registerReader(
400
- "footnote",
401
- (el) => ({
402
- type: "footnote",
403
- identifier: el.getAttribute("ref") ?? "",
404
- version: 1
405
- })
406
- );
407
- registry.registerReader("footnotesection", (el) => {
408
- const definitions = {};
409
- for (const child of el.children) {
410
- if (child.tagName.toLowerCase() === "def") {
411
- const ref = child.getAttribute("ref") ?? "";
412
- definitions[ref] = child.textContent ?? "";
413
- }
414
- }
415
- return {
416
- type: "footnote-section",
417
- ...extractBlockId(el),
418
- definitions,
419
- version: 1
420
- };
421
- });
422
- registry.registerReader("gallery", (el) => {
423
- const images = [];
424
- for (const child of el.children) {
425
- if (child.tagName.toLowerCase() === "img") {
426
- images.push({
427
- src: child.getAttribute("src") ?? "",
428
- alt: child.getAttribute("alt") ?? void 0
429
- });
430
- }
431
- }
432
- return {
433
- type: "gallery",
434
- ...extractBlockId(el),
435
- images,
436
- layout: el.getAttribute("layout") ?? "grid",
437
- version: 1
438
- };
439
- });
440
- registry.registerReader(
441
- "excalidraw",
442
- (el) => ({
443
- type: "excalidraw",
444
- ...extractBlockId(el),
445
- snapshot: extractCdataText(el) || el.getAttribute("snapshot") || "",
446
- version: 1
447
- })
448
- );
449
- registry.registerReader("grid", (el, ctx) => {
450
- const cells = [];
451
- for (const child of el.children) {
452
- if (child.tagName.toLowerCase() === "cell") {
453
- const children = ctx.parseChildren(child);
454
- cells.push({
455
- root: {
456
- type: "root",
457
- children,
458
- direction: "ltr",
459
- format: "",
460
- indent: 0,
461
- version: 1
462
- }
463
- });
464
- }
465
- }
466
- return {
467
- type: "grid-container",
468
- ...extractBlockId(el),
469
- cols: numAttr(el, "cols") ?? 2,
470
- gap: el.getAttribute("gap") ?? "16px",
471
- cells,
472
- version: 1
473
- };
474
- });
475
- registry.registerReader(
476
- "agentdiff",
477
- (el) => ({
478
- type: "agent-diff",
479
- ...extractBlockId(el),
480
- opType: el.getAttribute("op") ?? "insert",
481
- diffEntryId: el.getAttribute("entry") ?? "",
482
- version: 1
483
- })
484
- );
485
- registry.registerReader("codesnippet", (el) => {
486
- const files = [];
487
- for (const child of el.children) {
488
- if (child.tagName.toLowerCase() === "file") {
489
- files.push({
490
- filename: child.getAttribute("name") ?? "",
491
- code: child.textContent ?? "",
492
- language: child.getAttribute("lang") ?? ""
493
- });
494
- }
495
- }
496
- return {
497
- type: "code-snippet",
498
- ...extractBlockId(el),
499
- files,
500
- version: 1
501
- };
502
- });
503
- }
504
- class LitexmlRegistry {
505
- constructor() {
506
- this.writers = /* @__PURE__ */ new Map();
507
- this.readers = /* @__PURE__ */ new Map();
508
- }
509
- registerWriter(nodeType, writer) {
510
- this.writers.set(nodeType, writer);
511
- }
512
- registerReader(tagName, reader) {
513
- this.readers.set(tagName.toLowerCase(), reader);
514
- }
515
- getWriter(nodeType) {
516
- return this.writers.get(nodeType);
517
- }
518
- getReader(tagName) {
519
- return this.readers.get(tagName.toLowerCase());
520
- }
152
+ registry.registerReader("img", (el) => {
153
+ if (el.parentElement?.tagName.toLowerCase() === "gallery") return false;
154
+ return {
155
+ type: "image",
156
+ ...extractBlockId(el),
157
+ src: el.getAttribute("src") ?? "",
158
+ altText: el.getAttribute("alt") ?? "",
159
+ width: numAttr(el, "width"),
160
+ height: numAttr(el, "height"),
161
+ caption: el.getAttribute("caption") ?? void 0,
162
+ thumbhash: el.getAttribute("thumbhash") ?? void 0,
163
+ accent: el.getAttribute("accent") ?? void 0,
164
+ version: 1
165
+ };
166
+ });
167
+ registry.registerReader("video", (el) => ({
168
+ type: "video",
169
+ ...extractBlockId(el),
170
+ src: el.getAttribute("src") ?? "",
171
+ poster: el.getAttribute("poster") ?? void 0,
172
+ width: numAttr(el, "width"),
173
+ height: numAttr(el, "height"),
174
+ version: 1
175
+ }));
176
+ registry.registerReader("linkcard", (el) => ({
177
+ type: "link-card",
178
+ ...extractBlockId(el),
179
+ url: el.getAttribute("url") ?? "",
180
+ source: el.getAttribute("source") ?? void 0,
181
+ title: el.getAttribute("title") ?? void 0,
182
+ description: el.getAttribute("description") ?? void 0,
183
+ favicon: el.getAttribute("favicon") ?? void 0,
184
+ image: el.getAttribute("image") ?? void 0,
185
+ version: 1
186
+ }));
187
+ registry.registerReader("embed", (el) => ({
188
+ type: "embed",
189
+ ...extractBlockId(el),
190
+ url: el.getAttribute("url") ?? "",
191
+ source: el.getAttribute("source") ?? null,
192
+ version: 1
193
+ }));
194
+ registry.registerReader("codeblock", (el) => ({
195
+ type: "code-block",
196
+ ...extractBlockId(el),
197
+ code: el.textContent ?? "",
198
+ language: el.getAttribute("lang") ?? "",
199
+ version: 1
200
+ }));
201
+ registry.registerReader("mermaid", (el) => ({
202
+ type: "mermaid",
203
+ ...extractBlockId(el),
204
+ diagram: el.textContent ?? "",
205
+ version: 1
206
+ }));
207
+ registry.registerReader("math", (el) => {
208
+ if (el.getAttribute("display") === "block") return {
209
+ type: "katex-block",
210
+ ...extractBlockId(el),
211
+ equation: el.textContent ?? "",
212
+ version: 1
213
+ };
214
+ const color = el.getAttribute("color");
215
+ return {
216
+ type: "katex-inline",
217
+ equation: el.textContent ?? "",
218
+ ...color ? { color } : {},
219
+ version: 1
220
+ };
221
+ });
222
+ registry.registerReader("alert", (el, ctx) => {
223
+ const content = { root: {
224
+ type: "root",
225
+ children: ctx.parseChildren(el),
226
+ direction: "ltr",
227
+ format: "",
228
+ indent: 0,
229
+ version: 1
230
+ } };
231
+ return {
232
+ type: "alert-quote",
233
+ ...extractBlockId(el),
234
+ alertType: el.getAttribute("type") ?? "note",
235
+ content,
236
+ version: 1
237
+ };
238
+ });
239
+ registry.registerReader("banner", (el, ctx) => {
240
+ const content = { root: {
241
+ type: "root",
242
+ children: ctx.parseChildren(el),
243
+ direction: "ltr",
244
+ format: "",
245
+ indent: 0,
246
+ version: 1
247
+ } };
248
+ return {
249
+ type: "banner",
250
+ ...extractBlockId(el),
251
+ bannerType: el.getAttribute("type") ?? "note",
252
+ content,
253
+ version: 1
254
+ };
255
+ });
256
+ registry.registerReader("nesteddoc", (el, ctx) => {
257
+ const content = { root: {
258
+ type: "root",
259
+ children: ctx.parseChildren(el),
260
+ direction: "ltr",
261
+ format: "",
262
+ indent: 0,
263
+ version: 1
264
+ } };
265
+ return {
266
+ type: "nested-doc",
267
+ ...extractBlockId(el),
268
+ content,
269
+ version: 1
270
+ };
271
+ });
272
+ registry.registerReader("details", (el, ctx) => ({
273
+ type: "details",
274
+ ...extractBlockId(el),
275
+ summary: el.getAttribute("summary") ?? "",
276
+ open: el.getAttribute("open") === "true",
277
+ children: ctx.parseChildren(el),
278
+ direction: "ltr",
279
+ format: "",
280
+ indent: 0,
281
+ version: 1
282
+ }));
283
+ registry.registerReader("spoiler", (el, ctx) => ({
284
+ type: "spoiler",
285
+ children: ctx.parseChildren(el),
286
+ direction: "ltr",
287
+ format: "",
288
+ indent: 0,
289
+ textFormat: 0,
290
+ textStyle: "",
291
+ version: 1
292
+ }));
293
+ registry.registerReader("ruby", (el, ctx) => ({
294
+ type: "ruby",
295
+ reading: el.getAttribute("rt") ?? "",
296
+ children: ctx.parseChildren(el),
297
+ direction: "ltr",
298
+ format: "",
299
+ indent: 0,
300
+ textFormat: 0,
301
+ textStyle: "",
302
+ version: 1
303
+ }));
304
+ registry.registerReader("mention", (el) => ({
305
+ type: "mention",
306
+ platform: el.getAttribute("platform") ?? "",
307
+ handle: el.getAttribute("handle") ?? "",
308
+ displayName: el.textContent || void 0,
309
+ version: 1
310
+ }));
311
+ registry.registerReader("tag", (el) => ({
312
+ type: "tag",
313
+ text: el.textContent ?? "",
314
+ format: 0,
315
+ detail: 0,
316
+ mode: "normal",
317
+ style: "",
318
+ version: 1
319
+ }));
320
+ registry.registerReader("comment", (el) => ({
321
+ type: "comment",
322
+ text: el.textContent ?? "",
323
+ format: 0,
324
+ detail: 0,
325
+ mode: "normal",
326
+ style: "",
327
+ version: 1
328
+ }));
329
+ registry.registerReader("footnote", (el) => ({
330
+ type: "footnote",
331
+ identifier: el.getAttribute("ref") ?? "",
332
+ version: 1
333
+ }));
334
+ registry.registerReader("footnotesection", (el) => {
335
+ const definitions = {};
336
+ for (const child of el.children) if (child.tagName.toLowerCase() === "def") {
337
+ const ref = child.getAttribute("ref") ?? "";
338
+ definitions[ref] = child.textContent ?? "";
339
+ }
340
+ return {
341
+ type: "footnote-section",
342
+ ...extractBlockId(el),
343
+ definitions,
344
+ version: 1
345
+ };
346
+ });
347
+ registry.registerReader("gallery", (el) => {
348
+ const images = [];
349
+ for (const child of el.children) if (child.tagName.toLowerCase() === "img") images.push({
350
+ src: child.getAttribute("src") ?? "",
351
+ alt: child.getAttribute("alt") ?? void 0
352
+ });
353
+ return {
354
+ type: "gallery",
355
+ ...extractBlockId(el),
356
+ images,
357
+ layout: el.getAttribute("layout") ?? "grid",
358
+ version: 1
359
+ };
360
+ });
361
+ registry.registerReader("excalidraw", (el) => ({
362
+ type: "excalidraw",
363
+ ...extractBlockId(el),
364
+ snapshot: extractCdataText(el) || el.getAttribute("snapshot") || "",
365
+ version: 1
366
+ }));
367
+ registry.registerReader("grid", (el, ctx) => {
368
+ const cells = [];
369
+ for (const child of el.children) if (child.tagName.toLowerCase() === "cell") {
370
+ const children = ctx.parseChildren(child);
371
+ cells.push({ root: {
372
+ type: "root",
373
+ children,
374
+ direction: "ltr",
375
+ format: "",
376
+ indent: 0,
377
+ version: 1
378
+ } });
379
+ }
380
+ return {
381
+ type: "grid-container",
382
+ ...extractBlockId(el),
383
+ cols: numAttr(el, "cols") ?? 2,
384
+ gap: el.getAttribute("gap") ?? "16px",
385
+ cells,
386
+ version: 1
387
+ };
388
+ });
389
+ registry.registerReader("agentdiff", (el) => ({
390
+ type: "agent-diff",
391
+ ...extractBlockId(el),
392
+ opType: el.getAttribute("op") ?? "insert",
393
+ diffEntryId: el.getAttribute("entry") ?? "",
394
+ version: 1
395
+ }));
396
+ registry.registerReader("codesnippet", (el) => {
397
+ const files = [];
398
+ for (const child of el.children) if (child.tagName.toLowerCase() === "file") files.push({
399
+ filename: child.getAttribute("name") ?? "",
400
+ code: child.textContent ?? "",
401
+ language: child.getAttribute("lang") ?? ""
402
+ });
403
+ return {
404
+ type: "code-snippet",
405
+ ...extractBlockId(el),
406
+ files,
407
+ version: 1
408
+ };
409
+ });
410
+ registry.registerReader("poll", (el) => {
411
+ let question = "";
412
+ const options = [];
413
+ for (const child of el.children) {
414
+ const tag = child.tagName.toLowerCase();
415
+ if (tag === "question") question = child.textContent ?? "";
416
+ else if (tag === "option") {
417
+ const id = child.getAttribute("id") || `o_${makeOptionIdSuffix()}`;
418
+ options.push({
419
+ id,
420
+ label: child.textContent ?? ""
421
+ });
422
+ }
423
+ }
424
+ const mode = el.getAttribute("mode") === "multiple" ? "multiple" : "single";
425
+ const closeAt = el.getAttribute("close-at");
426
+ const showResultsAttr = el.getAttribute("show-results");
427
+ const showResults = showResultsAttr === "always" || showResultsAttr === "after-vote" || showResultsAttr === "after-close" ? showResultsAttr : void 0;
428
+ return {
429
+ type: "poll",
430
+ ...extractBlockId(el),
431
+ pollId: el.getAttribute("poll-id") || `p_${makePollIdSuffix()}`,
432
+ question,
433
+ options,
434
+ mode,
435
+ ...closeAt ? { closeAt } : {},
436
+ ...showResults ? { showResults } : {},
437
+ version: 1
438
+ };
439
+ });
521
440
  }
441
+ //#endregion
442
+ //#region src/registry.ts
443
+ var LitexmlRegistry = class {
444
+ constructor() {
445
+ this.writers = /* @__PURE__ */ new Map();
446
+ this.readers = /* @__PURE__ */ new Map();
447
+ }
448
+ registerWriter(nodeType, writer) {
449
+ this.writers.set(nodeType, writer);
450
+ }
451
+ registerReader(tagName, reader) {
452
+ this.readers.set(tagName.toLowerCase(), reader);
453
+ }
454
+ getWriter(nodeType) {
455
+ return this.writers.get(nodeType);
456
+ }
457
+ getReader(tagName) {
458
+ return this.readers.get(tagName.toLowerCase());
459
+ }
460
+ };
461
+ //#endregion
462
+ //#region src/writers/builtin.ts
522
463
  function blockId$1(node) {
523
- return node.$?.blockId ? { id: node.$.blockId } : {};
464
+ return node.$?.blockId ? { id: node.$.blockId } : {};
524
465
  }
525
466
  function registerBuiltinWriters(registry) {
526
- registry.registerWriter("paragraph", (node, ctx) => {
527
- const n = node;
528
- return {
529
- tag: "p",
530
- attrs: blockId$1(n),
531
- children: ctx.serializeChildren(n.children ?? [])
532
- };
533
- });
534
- registry.registerWriter("heading", (node, ctx) => {
535
- const n = node;
536
- const tag = n.tag ?? "h1";
537
- return {
538
- tag,
539
- attrs: blockId$1(n),
540
- children: ctx.serializeChildren(n.children ?? [])
541
- };
542
- });
543
- registry.registerWriter("quote", (node, ctx) => {
544
- const n = node;
545
- return {
546
- tag: "blockquote",
547
- attrs: blockId$1(n),
548
- children: ctx.serializeChildren(n.children ?? [])
549
- };
550
- });
551
- registry.registerWriter("horizontalrule", (node) => {
552
- const n = node;
553
- return { tag: "hr", attrs: blockId$1(n), selfClosing: true };
554
- });
555
- registry.registerWriter("list", (node, ctx) => {
556
- const n = node;
557
- const tag = n.listType === "number" ? "ol" : "ul";
558
- const attrs = { ...blockId$1(n) };
559
- if (n.listType === "check") attrs.type = "check";
560
- return {
561
- tag,
562
- attrs,
563
- children: ctx.serializeChildren(n.children ?? [])
564
- };
565
- });
566
- registry.registerWriter("listitem", (node, ctx) => {
567
- const n = node;
568
- const attrs = { ...blockId$1(n) };
569
- if (n.checked !== void 0) attrs.checked = String(n.checked);
570
- return {
571
- tag: "li",
572
- attrs,
573
- children: ctx.serializeChildren(n.children ?? [])
574
- };
575
- });
576
- registry.registerWriter("link", (node, ctx) => {
577
- const n = node;
578
- const attrs = { href: n.url ?? "" };
579
- if (n.target) attrs.target = n.target;
580
- if (n.title) attrs.title = n.title;
581
- return {
582
- tag: "a",
583
- attrs,
584
- children: ctx.serializeChildren(n.children ?? [])
585
- };
586
- });
587
- registry.registerWriter("autolink", (node, ctx) => {
588
- const n = node;
589
- return {
590
- tag: "a",
591
- attrs: { href: n.url ?? "" },
592
- children: ctx.serializeChildren(n.children ?? [])
593
- };
594
- });
595
- registry.registerWriter("table", (node, ctx) => {
596
- const n = node;
597
- return {
598
- tag: "table",
599
- attrs: blockId$1(n),
600
- children: ctx.serializeChildren(n.children ?? [])
601
- };
602
- });
603
- registry.registerWriter("tablerow", (node, ctx) => {
604
- const n = node;
605
- return {
606
- tag: "tr",
607
- children: ctx.serializeChildren(n.children ?? [])
608
- };
609
- });
610
- registry.registerWriter("tablecell", (node, ctx) => {
611
- const n = node;
612
- const tag = n.headerState === 1 ? "th" : "td";
613
- return {
614
- tag,
615
- children: ctx.serializeChildren(n.children ?? [])
616
- };
617
- });
467
+ registry.registerWriter("paragraph", (node, ctx) => {
468
+ const n = node;
469
+ return {
470
+ tag: "p",
471
+ attrs: blockId$1(n),
472
+ children: ctx.serializeChildren(n.children ?? [])
473
+ };
474
+ });
475
+ registry.registerWriter("heading", (node, ctx) => {
476
+ const n = node;
477
+ return {
478
+ tag: n.tag ?? "h1",
479
+ attrs: blockId$1(n),
480
+ children: ctx.serializeChildren(n.children ?? [])
481
+ };
482
+ });
483
+ registry.registerWriter("quote", (node, ctx) => {
484
+ const n = node;
485
+ return {
486
+ tag: "blockquote",
487
+ attrs: blockId$1(n),
488
+ children: ctx.serializeChildren(n.children ?? [])
489
+ };
490
+ });
491
+ registry.registerWriter("horizontalrule", (node) => {
492
+ return {
493
+ tag: "hr",
494
+ attrs: blockId$1(node),
495
+ selfClosing: true
496
+ };
497
+ });
498
+ registry.registerWriter("list", (node, ctx) => {
499
+ const n = node;
500
+ const tag = n.listType === "number" ? "ol" : "ul";
501
+ const attrs = { ...blockId$1(n) };
502
+ if (n.listType === "check") attrs.type = "check";
503
+ return {
504
+ tag,
505
+ attrs,
506
+ children: ctx.serializeChildren(n.children ?? [])
507
+ };
508
+ });
509
+ registry.registerWriter("listitem", (node, ctx) => {
510
+ const n = node;
511
+ const attrs = { ...blockId$1(n) };
512
+ if (n.checked !== void 0) attrs.checked = String(n.checked);
513
+ return {
514
+ tag: "li",
515
+ attrs,
516
+ children: ctx.serializeChildren(n.children ?? [])
517
+ };
518
+ });
519
+ registry.registerWriter("link", (node, ctx) => {
520
+ const n = node;
521
+ const attrs = { href: n.url ?? "" };
522
+ if (n.target) attrs.target = n.target;
523
+ if (n.title) attrs.title = n.title;
524
+ return {
525
+ tag: "a",
526
+ attrs,
527
+ children: ctx.serializeChildren(n.children ?? [])
528
+ };
529
+ });
530
+ registry.registerWriter("autolink", (node, ctx) => {
531
+ const n = node;
532
+ return {
533
+ tag: "a",
534
+ attrs: { href: n.url ?? "" },
535
+ children: ctx.serializeChildren(n.children ?? [])
536
+ };
537
+ });
538
+ registry.registerWriter("table", (node, ctx) => {
539
+ const n = node;
540
+ return {
541
+ tag: "table",
542
+ attrs: blockId$1(n),
543
+ children: ctx.serializeChildren(n.children ?? [])
544
+ };
545
+ });
546
+ registry.registerWriter("tablerow", (node, ctx) => {
547
+ const n = node;
548
+ return {
549
+ tag: "tr",
550
+ children: ctx.serializeChildren(n.children ?? [])
551
+ };
552
+ });
553
+ registry.registerWriter("tablecell", (node, ctx) => {
554
+ const n = node;
555
+ return {
556
+ tag: n.headerState === 1 ? "th" : "td",
557
+ children: ctx.serializeChildren(n.children ?? [])
558
+ };
559
+ });
618
560
  }
561
+ //#endregion
562
+ //#region src/writers/custom.ts
619
563
  function blockId(node) {
620
- return node.$?.blockId ? { id: node.$.blockId } : {};
564
+ return node.$?.blockId ? { id: node.$.blockId } : {};
621
565
  }
622
566
  function optAttr(attrs) {
623
- const result = {};
624
- for (const [k, v] of Object.entries(attrs)) {
625
- if (v !== void 0 && v !== null && v !== "") result[k] = String(v);
626
- }
627
- return result;
567
+ const result = {};
568
+ for (const [k, v] of Object.entries(attrs)) if (v !== void 0 && v !== null && v !== "") result[k] = String(v);
569
+ return result;
628
570
  }
629
571
  function registerCustomWriters(registry) {
630
- registry.registerWriter("image", (node) => {
631
- const n = node;
632
- return {
633
- tag: "img",
634
- attrs: optAttr({
635
- ...blockId(n),
636
- src: n.src,
637
- alt: n.altText,
638
- width: n.width != null ? String(n.width) : void 0,
639
- height: n.height != null ? String(n.height) : void 0,
640
- caption: n.caption,
641
- thumbhash: n.thumbhash,
642
- accent: n.accent
643
- }),
644
- selfClosing: true
645
- };
646
- });
647
- registry.registerWriter("video", (node) => {
648
- const n = node;
649
- return {
650
- tag: "video",
651
- attrs: optAttr({ ...blockId(n), src: n.src, poster: n.poster }),
652
- selfClosing: true
653
- };
654
- });
655
- registry.registerWriter("link-card", (node) => {
656
- const n = node;
657
- return {
658
- tag: "linkcard",
659
- attrs: optAttr({
660
- ...blockId(n),
661
- url: n.url,
662
- source: n.source,
663
- title: n.title,
664
- description: n.description,
665
- favicon: n.favicon,
666
- image: n.image
667
- }),
668
- selfClosing: true
669
- };
670
- });
671
- registry.registerWriter("embed", (node) => {
672
- const n = node;
673
- return {
674
- tag: "embed",
675
- attrs: optAttr({ ...blockId(n), url: n.url, source: n.source }),
676
- selfClosing: true
677
- };
678
- });
679
- registry.registerWriter("code-block", (node) => {
680
- const n = node;
681
- return {
682
- tag: "codeblock",
683
- attrs: optAttr({ ...blockId(n), lang: n.language }),
684
- children: [n.code ?? ""]
685
- };
686
- });
687
- registry.registerWriter("mermaid", (node) => {
688
- const n = node;
689
- return {
690
- tag: "mermaid",
691
- attrs: blockId(n),
692
- children: [n.diagram ?? ""]
693
- };
694
- });
695
- registry.registerWriter("katex-block", (node) => {
696
- const n = node;
697
- return {
698
- tag: "math",
699
- attrs: { ...blockId(n), display: "block" },
700
- children: [n.equation ?? ""]
701
- };
702
- });
703
- registry.registerWriter("katex-inline", (node) => {
704
- const n = node;
705
- const attrs = {};
706
- if (n.color) attrs.color = n.color;
707
- return {
708
- tag: "math",
709
- attrs: Object.keys(attrs).length > 0 ? attrs : void 0,
710
- children: [n.equation ?? ""]
711
- };
712
- });
713
- registry.registerWriter("alert-quote", (node, ctx) => {
714
- const n = node;
715
- return {
716
- tag: "alert",
717
- attrs: optAttr({ ...blockId(n), type: n.alertType }),
718
- children: ctx.serializeNestedState(n.content)
719
- };
720
- });
721
- registry.registerWriter("banner", (node, ctx) => {
722
- const n = node;
723
- return {
724
- tag: "banner",
725
- attrs: optAttr({ ...blockId(n), type: n.bannerType }),
726
- children: ctx.serializeNestedState(n.content)
727
- };
728
- });
729
- registry.registerWriter("nested-doc", (node, ctx) => {
730
- const n = node;
731
- return {
732
- tag: "nesteddoc",
733
- attrs: blockId(n),
734
- children: ctx.serializeNestedState(n.content ?? n.contentState)
735
- };
736
- });
737
- registry.registerWriter("excalidraw", (node) => {
738
- const n = node;
739
- if (!n.snapshot) {
740
- return {
741
- tag: "excalidraw",
742
- attrs: optAttr(blockId(n)),
743
- selfClosing: true
744
- };
745
- }
746
- return {
747
- tag: "excalidraw",
748
- attrs: optAttr(blockId(n)),
749
- children: [{ cdata: n.snapshot }]
750
- };
751
- });
752
- registry.registerWriter("grid-container", (node, ctx) => {
753
- const n = node;
754
- const cells = (n.cells ?? []).map((cellState) => ({
755
- tag: "cell",
756
- children: ctx.serializeNestedState(cellState)
757
- }));
758
- return {
759
- tag: "grid",
760
- attrs: optAttr({
761
- ...blockId(n),
762
- cols: n.cols != null ? String(n.cols) : void 0,
763
- gap: n.gap
764
- }),
765
- children: cells
766
- };
767
- });
768
- registry.registerWriter("agent-diff", (node) => {
769
- const n = node;
770
- return {
771
- tag: "agentdiff",
772
- attrs: optAttr({ ...blockId(n), op: n.opType, entry: n.diffEntryId }),
773
- selfClosing: true
774
- };
775
- });
776
- registry.registerWriter("details", (node, ctx) => {
777
- const n = node;
778
- return {
779
- tag: "details",
780
- attrs: optAttr({
781
- ...blockId(n),
782
- summary: n.summary,
783
- open: n.open != null ? String(n.open) : void 0
784
- }),
785
- children: ctx.serializeChildren(n.children ?? [])
786
- };
787
- });
788
- registry.registerWriter("spoiler", (node, ctx) => {
789
- const n = node;
790
- return {
791
- tag: "spoiler",
792
- children: ctx.serializeChildren(n.children ?? [])
793
- };
794
- });
795
- registry.registerWriter("ruby", (node, ctx) => {
796
- const n = node;
797
- return {
798
- tag: "ruby",
799
- attrs: optAttr({ rt: n.reading }),
800
- children: ctx.serializeChildren(n.children ?? [])
801
- };
802
- });
803
- registry.registerWriter("mention", (node) => {
804
- const n = node;
805
- return {
806
- tag: "mention",
807
- attrs: optAttr({ platform: n.platform, handle: n.handle }),
808
- children: [n.displayName ?? n.handle ?? ""]
809
- };
810
- });
811
- registry.registerWriter("tag", (node) => {
812
- const n = node;
813
- return { tag: "tag", children: [n.text ?? ""] };
814
- });
815
- registry.registerWriter("comment", (node) => {
816
- const n = node;
817
- return { tag: "comment", children: [n.text ?? ""] };
818
- });
819
- registry.registerWriter("footnote", (node) => {
820
- const n = node;
821
- return { tag: "footnote", attrs: { ref: n.identifier ?? "" }, selfClosing: true };
822
- });
823
- registry.registerWriter("footnote-section", (node) => {
824
- const n = node;
825
- const defs = n.definitions ?? {};
826
- const children = Object.entries(defs).map(([ref, text]) => ({
827
- tag: "def",
828
- attrs: { ref },
829
- children: [text]
830
- }));
831
- return { tag: "footnotesection", attrs: blockId(n), children };
832
- });
833
- registry.registerWriter("gallery", (node) => {
834
- const n = node;
835
- const images = (n.images ?? []).map((img) => ({
836
- tag: "img",
837
- attrs: optAttr({ src: img.src, alt: img.alt }),
838
- selfClosing: true
839
- }));
840
- return {
841
- tag: "gallery",
842
- attrs: optAttr({ ...blockId(n), layout: n.layout }),
843
- children: images
844
- };
845
- });
846
- registry.registerWriter("code-snippet", (node) => {
847
- const n = node;
848
- const files = (n.files ?? []).map((f) => ({
849
- tag: "file",
850
- attrs: optAttr({ name: f.filename, lang: f.language }),
851
- children: [f.code ?? ""]
852
- }));
853
- return { tag: "codesnippet", attrs: blockId(n), children: files };
854
- });
572
+ registry.registerWriter("image", (node) => {
573
+ const n = node;
574
+ return {
575
+ tag: "img",
576
+ attrs: optAttr({
577
+ ...blockId(n),
578
+ src: n.src,
579
+ alt: n.altText,
580
+ width: n.width != null ? String(n.width) : void 0,
581
+ height: n.height != null ? String(n.height) : void 0,
582
+ caption: n.caption,
583
+ thumbhash: n.thumbhash,
584
+ accent: n.accent
585
+ }),
586
+ selfClosing: true
587
+ };
588
+ });
589
+ registry.registerWriter("video", (node) => {
590
+ const n = node;
591
+ return {
592
+ tag: "video",
593
+ attrs: optAttr({
594
+ ...blockId(n),
595
+ src: n.src,
596
+ poster: n.poster
597
+ }),
598
+ selfClosing: true
599
+ };
600
+ });
601
+ registry.registerWriter("link-card", (node) => {
602
+ const n = node;
603
+ return {
604
+ tag: "linkcard",
605
+ attrs: optAttr({
606
+ ...blockId(n),
607
+ url: n.url,
608
+ source: n.source,
609
+ title: n.title,
610
+ description: n.description,
611
+ favicon: n.favicon,
612
+ image: n.image
613
+ }),
614
+ selfClosing: true
615
+ };
616
+ });
617
+ registry.registerWriter("embed", (node) => {
618
+ const n = node;
619
+ return {
620
+ tag: "embed",
621
+ attrs: optAttr({
622
+ ...blockId(n),
623
+ url: n.url,
624
+ source: n.source
625
+ }),
626
+ selfClosing: true
627
+ };
628
+ });
629
+ registry.registerWriter("code-block", (node) => {
630
+ const n = node;
631
+ return {
632
+ tag: "codeblock",
633
+ attrs: optAttr({
634
+ ...blockId(n),
635
+ lang: n.language
636
+ }),
637
+ children: [n.code ?? ""]
638
+ };
639
+ });
640
+ registry.registerWriter("mermaid", (node) => {
641
+ const n = node;
642
+ return {
643
+ tag: "mermaid",
644
+ attrs: blockId(n),
645
+ children: [n.diagram ?? ""]
646
+ };
647
+ });
648
+ registry.registerWriter("katex-block", (node) => {
649
+ const n = node;
650
+ return {
651
+ tag: "math",
652
+ attrs: {
653
+ ...blockId(n),
654
+ display: "block"
655
+ },
656
+ children: [n.equation ?? ""]
657
+ };
658
+ });
659
+ registry.registerWriter("katex-inline", (node) => {
660
+ const n = node;
661
+ const attrs = {};
662
+ if (n.color) attrs.color = n.color;
663
+ return {
664
+ tag: "math",
665
+ attrs: Object.keys(attrs).length > 0 ? attrs : void 0,
666
+ children: [n.equation ?? ""]
667
+ };
668
+ });
669
+ registry.registerWriter("alert-quote", (node, ctx) => {
670
+ const n = node;
671
+ return {
672
+ tag: "alert",
673
+ attrs: optAttr({
674
+ ...blockId(n),
675
+ type: n.alertType
676
+ }),
677
+ children: ctx.serializeNestedState(n.content)
678
+ };
679
+ });
680
+ registry.registerWriter("banner", (node, ctx) => {
681
+ const n = node;
682
+ return {
683
+ tag: "banner",
684
+ attrs: optAttr({
685
+ ...blockId(n),
686
+ type: n.bannerType
687
+ }),
688
+ children: ctx.serializeNestedState(n.content)
689
+ };
690
+ });
691
+ registry.registerWriter("nested-doc", (node, ctx) => {
692
+ const n = node;
693
+ return {
694
+ tag: "nesteddoc",
695
+ attrs: blockId(n),
696
+ children: ctx.serializeNestedState(n.content ?? n.contentState)
697
+ };
698
+ });
699
+ registry.registerWriter("excalidraw", (node) => {
700
+ const n = node;
701
+ if (!n.snapshot) return {
702
+ tag: "excalidraw",
703
+ attrs: optAttr(blockId(n)),
704
+ selfClosing: true
705
+ };
706
+ return {
707
+ tag: "excalidraw",
708
+ attrs: optAttr(blockId(n)),
709
+ children: [{ cdata: n.snapshot }]
710
+ };
711
+ });
712
+ registry.registerWriter("grid-container", (node, ctx) => {
713
+ const n = node;
714
+ const cells = (n.cells ?? []).map((cellState) => ({
715
+ tag: "cell",
716
+ children: ctx.serializeNestedState(cellState)
717
+ }));
718
+ return {
719
+ tag: "grid",
720
+ attrs: optAttr({
721
+ ...blockId(n),
722
+ cols: n.cols != null ? String(n.cols) : void 0,
723
+ gap: n.gap
724
+ }),
725
+ children: cells
726
+ };
727
+ });
728
+ registry.registerWriter("agent-diff", (node) => {
729
+ const n = node;
730
+ return {
731
+ tag: "agentdiff",
732
+ attrs: optAttr({
733
+ ...blockId(n),
734
+ op: n.opType,
735
+ entry: n.diffEntryId
736
+ }),
737
+ selfClosing: true
738
+ };
739
+ });
740
+ registry.registerWriter("details", (node, ctx) => {
741
+ const n = node;
742
+ return {
743
+ tag: "details",
744
+ attrs: optAttr({
745
+ ...blockId(n),
746
+ summary: n.summary,
747
+ open: n.open != null ? String(n.open) : void 0
748
+ }),
749
+ children: ctx.serializeChildren(n.children ?? [])
750
+ };
751
+ });
752
+ registry.registerWriter("spoiler", (node, ctx) => {
753
+ const n = node;
754
+ return {
755
+ tag: "spoiler",
756
+ children: ctx.serializeChildren(n.children ?? [])
757
+ };
758
+ });
759
+ registry.registerWriter("ruby", (node, ctx) => {
760
+ const n = node;
761
+ return {
762
+ tag: "ruby",
763
+ attrs: optAttr({ rt: n.reading }),
764
+ children: ctx.serializeChildren(n.children ?? [])
765
+ };
766
+ });
767
+ registry.registerWriter("mention", (node) => {
768
+ const n = node;
769
+ return {
770
+ tag: "mention",
771
+ attrs: optAttr({
772
+ platform: n.platform,
773
+ handle: n.handle
774
+ }),
775
+ children: [n.displayName ?? n.handle ?? ""]
776
+ };
777
+ });
778
+ registry.registerWriter("tag", (node) => {
779
+ return {
780
+ tag: "tag",
781
+ children: [node.text ?? ""]
782
+ };
783
+ });
784
+ registry.registerWriter("comment", (node) => {
785
+ return {
786
+ tag: "comment",
787
+ children: [node.text ?? ""]
788
+ };
789
+ });
790
+ registry.registerWriter("footnote", (node) => {
791
+ return {
792
+ tag: "footnote",
793
+ attrs: { ref: node.identifier ?? "" },
794
+ selfClosing: true
795
+ };
796
+ });
797
+ registry.registerWriter("footnote-section", (node) => {
798
+ const n = node;
799
+ const defs = n.definitions ?? {};
800
+ const children = Object.entries(defs).map(([ref, text]) => ({
801
+ tag: "def",
802
+ attrs: { ref },
803
+ children: [text]
804
+ }));
805
+ return {
806
+ tag: "footnotesection",
807
+ attrs: blockId(n),
808
+ children
809
+ };
810
+ });
811
+ registry.registerWriter("gallery", (node) => {
812
+ const n = node;
813
+ const images = (n.images ?? []).map((img) => ({
814
+ tag: "img",
815
+ attrs: optAttr({
816
+ src: img.src,
817
+ alt: img.alt
818
+ }),
819
+ selfClosing: true
820
+ }));
821
+ return {
822
+ tag: "gallery",
823
+ attrs: optAttr({
824
+ ...blockId(n),
825
+ layout: n.layout
826
+ }),
827
+ children: images
828
+ };
829
+ });
830
+ registry.registerWriter("code-snippet", (node) => {
831
+ const n = node;
832
+ const files = (n.files ?? []).map((f) => ({
833
+ tag: "file",
834
+ attrs: optAttr({
835
+ name: f.filename,
836
+ lang: f.language
837
+ }),
838
+ children: [f.code ?? ""]
839
+ }));
840
+ return {
841
+ tag: "codesnippet",
842
+ attrs: blockId(n),
843
+ children: files
844
+ };
845
+ });
846
+ registry.registerWriter("poll", (node) => {
847
+ const n = node;
848
+ const optionElements = (n.options ?? []).map((option) => ({
849
+ tag: "option",
850
+ attrs: optAttr({ id: option.id }),
851
+ children: [option.label ?? ""]
852
+ }));
853
+ return {
854
+ tag: "poll",
855
+ attrs: optAttr({
856
+ ...blockId(n),
857
+ "poll-id": n.pollId,
858
+ "mode": n.mode,
859
+ "close-at": n.closeAt,
860
+ "show-results": n.showResults
861
+ }),
862
+ children: [{
863
+ tag: "question",
864
+ children: [n.question ?? ""]
865
+ }, ...optionElements]
866
+ };
867
+ });
855
868
  }
869
+ //#endregion
870
+ //#region src/default-registry.ts
856
871
  function createDefaultRegistry() {
857
- const registry = new LitexmlRegistry();
858
- registerBuiltinWriters(registry);
859
- registerBuiltinReaders(registry);
860
- registerCustomWriters(registry);
861
- registerCustomReaders(registry);
862
- return registry;
872
+ const registry = new LitexmlRegistry();
873
+ registerBuiltinWriters(registry);
874
+ registerBuiltinReaders(registry);
875
+ registerCustomWriters(registry);
876
+ registerCustomReaders(registry);
877
+ return registry;
863
878
  }
864
- const FORMAT_BIT_TO_TAG = [
865
- [1, "b"],
866
- [2, "i"],
867
- [4, "s"],
868
- [8, "u"],
869
- [16, "code"],
870
- [32, "sub"],
871
- [64, "sup"],
872
- [128, "mark"]
879
+ //#endregion
880
+ //#region src/text-format.ts
881
+ /** Ordered list: bit value → XML tag. Order determines nesting (outer→inner). */
882
+ var FORMAT_BIT_TO_TAG = [
883
+ [1, "b"],
884
+ [2, "i"],
885
+ [4, "s"],
886
+ [8, "u"],
887
+ [16, "code"],
888
+ [32, "sub"],
889
+ [64, "sup"],
890
+ [128, "mark"]
873
891
  ];
874
- const FORMAT_TAG_TO_BIT = {
875
- b: 1,
876
- strong: 1,
877
- i: 2,
878
- em: 2,
879
- s: 4,
880
- del: 4,
881
- strike: 4,
882
- u: 8,
883
- code: 16,
884
- sub: 32,
885
- sup: 64,
886
- mark: 128
892
+ /** Reverse map: XML tag name → bit value (includes aliases) */
893
+ var FORMAT_TAG_TO_BIT = {
894
+ b: 1,
895
+ strong: 1,
896
+ i: 2,
897
+ em: 2,
898
+ s: 4,
899
+ del: 4,
900
+ strike: 4,
901
+ u: 8,
902
+ code: 16,
903
+ sub: 32,
904
+ sup: 64,
905
+ mark: 128
887
906
  };
907
+ /** Wrap text content with nested format tags based on bitmask. */
888
908
  function wrapWithFormatTags(text, format) {
889
- if (format === 0) return [text];
890
- let content = [text];
891
- for (let idx = FORMAT_BIT_TO_TAG.length - 1; idx >= 0; idx--) {
892
- const [bit, tag] = FORMAT_BIT_TO_TAG[idx];
893
- if (format & bit) {
894
- content = [{ tag, children: content }];
895
- }
896
- }
897
- return content;
909
+ if (format === 0) return [text];
910
+ let content = [text];
911
+ for (let idx = FORMAT_BIT_TO_TAG.length - 1; idx >= 0; idx--) {
912
+ const [bit, tag] = FORMAT_BIT_TO_TAG[idx];
913
+ if (format & bit) content = [{
914
+ tag,
915
+ children: content
916
+ }];
917
+ }
918
+ return content;
898
919
  }
920
+ /** Check if a tag name is a known text format tag. */
899
921
  function isFormatTag(tagName) {
900
- return tagName.toLowerCase() in FORMAT_TAG_TO_BIT;
922
+ return tagName.toLowerCase() in FORMAT_TAG_TO_BIT;
901
923
  }
924
+ /** Get the format bit for a tag name. Returns 0 if not a format tag. */
902
925
  function getFormatBit(tagName) {
903
- return FORMAT_TAG_TO_BIT[tagName.toLowerCase()] ?? 0;
926
+ return FORMAT_TAG_TO_BIT[tagName.toLowerCase()] ?? 0;
904
927
  }
928
+ //#endregion
929
+ //#region src/deserializer.ts
905
930
  function parseXml(xml) {
906
- const { document } = parseHTML(`<!DOCTYPE html><html><body>${xml}</body></html>`);
907
- return document;
931
+ const { document } = parseHTML(`<!DOCTYPE html><html><body>${xml}</body></html>`);
932
+ return document;
908
933
  }
909
934
  function deserializeFromXml(xml, registry) {
910
- const doc = parseXml(xml);
911
- const docEl = doc.querySelector("doc") ?? doc.body;
912
- const ctx = createReaderContext(registry);
913
- const children = ctx.parseChildren(docEl);
914
- return {
915
- root: {
916
- type: "root",
917
- children,
918
- direction: "ltr",
919
- format: "",
920
- indent: 0,
921
- version: 1
922
- }
923
- };
935
+ const doc = parseXml(xml);
936
+ const docEl = doc.querySelector("doc") ?? doc.body;
937
+ return { root: {
938
+ type: "root",
939
+ children: createReaderContext(registry).parseChildren(docEl),
940
+ direction: "ltr",
941
+ format: "",
942
+ indent: 0,
943
+ version: 1
944
+ } };
924
945
  }
925
946
  function deserializeNodesFromXml(xml, registry) {
926
- const doc = parseXml(`<fragment>${xml}</fragment>`);
927
- const fragment = doc.querySelector("fragment") ?? doc.body;
928
- const ctx = createReaderContext(registry);
929
- return ctx.parseChildren(fragment);
947
+ const doc = parseXml(`<fragment>${xml}</fragment>`);
948
+ const fragment = doc.querySelector("fragment") ?? doc.body;
949
+ return createReaderContext(registry).parseChildren(fragment);
930
950
  }
931
- const BLOCK_TAGS = /* @__PURE__ */ new Set([
932
- "doc",
933
- "fragment",
934
- "root",
935
- "ul",
936
- "ol",
937
- "table",
938
- "tr",
939
- "alert",
940
- "banner",
941
- "details",
942
- "nesteddoc",
943
- "gallery",
944
- "codesnippet",
945
- "footnotesection",
946
- "grid",
947
- "cell",
948
- "excalidraw"
951
+ /** Structural containers whose whitespace-only text nodes are layout artifacts, not content */
952
+ var BLOCK_TAGS = new Set([
953
+ "doc",
954
+ "fragment",
955
+ "root",
956
+ "ul",
957
+ "ol",
958
+ "table",
959
+ "tr",
960
+ "alert",
961
+ "banner",
962
+ "details",
963
+ "nesteddoc",
964
+ "gallery",
965
+ "codesnippet",
966
+ "footnotesection",
967
+ "grid",
968
+ "cell",
969
+ "excalidraw"
949
970
  ]);
950
971
  function isBlockContainer(element) {
951
- return BLOCK_TAGS.has(element.tagName.toLowerCase());
972
+ return BLOCK_TAGS.has(element.tagName.toLowerCase());
952
973
  }
953
974
  function createReaderContext(registry) {
954
- const ctx = {
955
- parseChildren(element) {
956
- const blockLevel = isBlockContainer(element);
957
- const nodes = [];
958
- for (const child of element.childNodes) {
959
- if (child.nodeType === 3) {
960
- const text = child.textContent ?? "";
961
- if (blockLevel && text.trim() === "") continue;
962
- if (text === "") continue;
963
- nodes.push(makeTextNode(text, 0));
964
- } else if (child.nodeType === 1) {
965
- const el = child;
966
- const parsed = parseElement(el, registry, ctx, 0);
967
- if (parsed) {
968
- if (Array.isArray(parsed)) nodes.push(...parsed);
969
- else nodes.push(parsed);
970
- }
971
- }
972
- }
973
- return nodes;
974
- },
975
- parseNestedState(xml) {
976
- return deserializeFromXml(xml, registry);
977
- }
978
- };
979
- return ctx;
975
+ const ctx = {
976
+ parseChildren(element) {
977
+ const blockLevel = isBlockContainer(element);
978
+ const nodes = [];
979
+ for (const child of element.childNodes) if (child.nodeType === 3) {
980
+ const text = child.textContent ?? "";
981
+ if (blockLevel && text.trim() === "") continue;
982
+ if (text === "") continue;
983
+ nodes.push(makeTextNode(text, 0));
984
+ } else if (child.nodeType === 1) {
985
+ const parsed = parseElement(child, registry, ctx, 0);
986
+ if (parsed) if (Array.isArray(parsed)) nodes.push(...parsed);
987
+ else nodes.push(parsed);
988
+ }
989
+ return nodes;
990
+ },
991
+ parseNestedState(xml) {
992
+ return deserializeFromXml(xml, registry);
993
+ }
994
+ };
995
+ return ctx;
980
996
  }
981
997
  function parseElement(element, registry, ctx, inheritedFormat) {
982
- const tag = element.tagName.toLowerCase();
983
- if (isFormatTag(tag)) {
984
- const format = inheritedFormat | getFormatBit(tag);
985
- return parseInlineChildren(element, registry, ctx, format);
986
- }
987
- if (tag === "br") {
988
- return { type: "linebreak", version: 1 };
989
- }
990
- if (tag === "node") {
991
- return parseFallbackNode(element);
992
- }
993
- const reader = registry.getReader(tag);
994
- if (reader) {
995
- const result = reader(element, ctx);
996
- if (result !== false) return result;
997
- }
998
- return ctx.parseChildren(element);
998
+ const tag = element.tagName.toLowerCase();
999
+ if (isFormatTag(tag)) return parseInlineChildren(element, registry, ctx, inheritedFormat | getFormatBit(tag));
1000
+ if (tag === "br") return {
1001
+ type: "linebreak",
1002
+ version: 1
1003
+ };
1004
+ if (tag === "node") return parseFallbackNode(element);
1005
+ const reader = registry.getReader(tag);
1006
+ if (reader) {
1007
+ const result = reader(element, ctx);
1008
+ if (result !== false) return result;
1009
+ }
1010
+ return ctx.parseChildren(element);
999
1011
  }
1000
1012
  function parseInlineChildren(element, registry, ctx, format) {
1001
- const nodes = [];
1002
- for (const child of element.childNodes) {
1003
- if (child.nodeType === 3) {
1004
- const text = child.textContent ?? "";
1005
- if (text === "") continue;
1006
- nodes.push(makeTextNode(text, format));
1007
- } else if (child.nodeType === 1) {
1008
- const el = child;
1009
- const parsed = parseElement(el, registry, ctx, format);
1010
- if (parsed) {
1011
- if (Array.isArray(parsed)) nodes.push(...parsed);
1012
- else nodes.push(parsed);
1013
- }
1014
- }
1015
- }
1016
- return nodes;
1013
+ const nodes = [];
1014
+ for (const child of element.childNodes) if (child.nodeType === 3) {
1015
+ const text = child.textContent ?? "";
1016
+ if (text === "") continue;
1017
+ nodes.push(makeTextNode(text, format));
1018
+ } else if (child.nodeType === 1) {
1019
+ const parsed = parseElement(child, registry, ctx, format);
1020
+ if (parsed) if (Array.isArray(parsed)) nodes.push(...parsed);
1021
+ else nodes.push(parsed);
1022
+ }
1023
+ return nodes;
1017
1024
  }
1018
1025
  function parseFallbackNode(element) {
1019
- const type = element.getAttribute("type") ?? "unknown";
1020
- const id = element.getAttribute("id");
1021
- const dataStr = element.getAttribute("data");
1022
- const data = dataStr ? JSON.parse(dataStr) : {};
1023
- return {
1024
- type,
1025
- ...id ? { $: { blockId: id } } : {},
1026
- ...data,
1027
- version: 1
1028
- };
1026
+ const type = element.getAttribute("type") ?? "unknown";
1027
+ const id = element.getAttribute("id");
1028
+ const dataStr = element.getAttribute("data");
1029
+ const data = dataStr ? JSON.parse(dataStr) : {};
1030
+ return {
1031
+ type,
1032
+ ...id ? { $: { blockId: id } } : {},
1033
+ ...data,
1034
+ version: 1
1035
+ };
1029
1036
  }
1030
1037
  function makeTextNode(text, format) {
1031
- return {
1032
- type: "text",
1033
- text,
1034
- format,
1035
- detail: 0,
1036
- mode: "normal",
1037
- style: "",
1038
- version: 1
1039
- };
1038
+ return {
1039
+ type: "text",
1040
+ text,
1041
+ format,
1042
+ detail: 0,
1043
+ mode: "normal",
1044
+ style: "",
1045
+ version: 1
1046
+ };
1040
1047
  }
1041
- const ESCAPE_MAP = {
1042
- "&": "&amp;",
1043
- "<": "&lt;",
1044
- ">": "&gt;",
1045
- '"': "&quot;",
1046
- "'": "&apos;"
1048
+ //#endregion
1049
+ //#region src/xml-utils.ts
1050
+ var ESCAPE_MAP = {
1051
+ "&": "&amp;",
1052
+ "<": "&lt;",
1053
+ ">": "&gt;",
1054
+ "\"": "&quot;",
1055
+ "'": "&apos;"
1047
1056
  };
1048
1057
  function escapeXml(text) {
1049
- return text.replaceAll(/["&'<>]/g, (ch) => ESCAPE_MAP[ch]);
1058
+ return text.replaceAll(/["&'<>]/g, (ch) => ESCAPE_MAP[ch]);
1050
1059
  }
1051
1060
  function buildAttrs(attrs) {
1052
- const parts = Object.entries(attrs).filter(([, v]) => v !== void 0 && v !== "").map(([k, v]) => ` ${k}="${escapeXml(v)}"`);
1053
- return parts.join("");
1061
+ return Object.entries(attrs).filter(([, v]) => v !== void 0 && v !== "").map(([k, v]) => ` ${k}="${escapeXml(v)}"`).join("");
1054
1062
  }
1055
1063
  function isCdata(item) {
1056
- return typeof item === "object" && "cdata" in item;
1064
+ return typeof item === "object" && "cdata" in item;
1057
1065
  }
1058
1066
  function renderCdata(item) {
1059
- return `<![CDATA[${item.cdata}]]>`;
1067
+ return `<![CDATA[${item.cdata}]]>`;
1060
1068
  }
1069
+ /** Render XmlContent tree to XML string. */
1061
1070
  function renderXml(content, indent = 0, options = {}) {
1062
- const lines = [];
1063
- for (const item of content) {
1064
- if (typeof item === "string") {
1065
- lines.push(escapeXml(item));
1066
- } else if (isCdata(item)) {
1067
- lines.push(renderCdata(item));
1068
- } else {
1069
- lines.push(renderElement(item, indent, options));
1070
- }
1071
- }
1072
- return lines.join("");
1071
+ const lines = [];
1072
+ for (const item of content) if (typeof item === "string") lines.push(escapeXml(item));
1073
+ else if (isCdata(item)) lines.push(renderCdata(item));
1074
+ else lines.push(renderElement(item, indent, options));
1075
+ return lines.join("");
1073
1076
  }
1074
1077
  function renderElement(el, indent, options) {
1075
- const attrs = el.attrs ? buildAttrs(el.attrs) : "";
1076
- const compact = options.compact === true;
1077
- if (el.selfClosing) {
1078
- return compact ? `<${el.tag}${attrs} />` : `${pad(indent)}<${el.tag}${attrs} />
1079
- `;
1080
- }
1081
- if (!el.children || el.children.length === 0) {
1082
- return compact ? `<${el.tag}${attrs} />` : `${pad(indent)}<${el.tag}${attrs} />
1083
- `;
1084
- }
1085
- const allInline = el.children.every(
1086
- (c) => typeof c === "string" || isCdata(c) || isInlineElement(c)
1087
- );
1088
- if (allInline) {
1089
- const inner2 = el.children.map((c) => {
1090
- if (typeof c === "string") return escapeXml(c);
1091
- if (isCdata(c)) return renderCdata(c);
1092
- return renderInline(c);
1093
- }).join("");
1094
- return compact ? `<${el.tag}${attrs}>${inner2}</${el.tag}>` : `${pad(indent)}<${el.tag}${attrs}>${inner2}</${el.tag}>
1095
- `;
1096
- }
1097
- if (compact) {
1098
- const inner2 = el.children.map((c) => {
1099
- if (typeof c === "string") return escapeXml(c);
1100
- if (isCdata(c)) return renderCdata(c);
1101
- return renderElement(c, indent + 1, options);
1102
- }).join("");
1103
- return `<${el.tag}${attrs}>${inner2}</${el.tag}>`;
1104
- }
1105
- const inner = el.children.map((c) => {
1106
- if (typeof c === "string") return `${pad(indent + 1)}${escapeXml(c)}
1107
- `;
1108
- if (isCdata(c)) return `${pad(indent + 1)}${renderCdata(c)}
1109
- `;
1110
- return renderElement(c, indent + 1, options);
1111
- }).join("");
1112
- return `${pad(indent)}<${el.tag}${attrs}>
1113
- ${inner}${pad(indent)}</${el.tag}>
1114
- `;
1078
+ const attrs = el.attrs ? buildAttrs(el.attrs) : "";
1079
+ const compact = options.compact === true;
1080
+ if (el.selfClosing) return compact ? `<${el.tag}${attrs} />` : `${pad(indent)}<${el.tag}${attrs} />\n`;
1081
+ if (!el.children || el.children.length === 0) return compact ? `<${el.tag}${attrs} />` : `${pad(indent)}<${el.tag}${attrs} />\n`;
1082
+ if (el.children.every((c) => typeof c === "string" || isCdata(c) || isInlineElement(c))) {
1083
+ const inner = el.children.map((c) => {
1084
+ if (typeof c === "string") return escapeXml(c);
1085
+ if (isCdata(c)) return renderCdata(c);
1086
+ return renderInline(c);
1087
+ }).join("");
1088
+ return compact ? `<${el.tag}${attrs}>${inner}</${el.tag}>` : `${pad(indent)}<${el.tag}${attrs}>${inner}</${el.tag}>\n`;
1089
+ }
1090
+ if (compact) {
1091
+ const inner = el.children.map((c) => {
1092
+ if (typeof c === "string") return escapeXml(c);
1093
+ if (isCdata(c)) return renderCdata(c);
1094
+ return renderElement(c, indent + 1, options);
1095
+ }).join("");
1096
+ return `<${el.tag}${attrs}>${inner}</${el.tag}>`;
1097
+ }
1098
+ const inner = el.children.map((c) => {
1099
+ if (typeof c === "string") return `${pad(indent + 1)}${escapeXml(c)}\n`;
1100
+ if (isCdata(c)) return `${pad(indent + 1)}${renderCdata(c)}\n`;
1101
+ return renderElement(c, indent + 1, options);
1102
+ }).join("");
1103
+ return `${pad(indent)}<${el.tag}${attrs}>\n${inner}${pad(indent)}</${el.tag}>\n`;
1115
1104
  }
1116
1105
  function renderInline(el) {
1117
- const attrs = el.attrs ? buildAttrs(el.attrs) : "";
1118
- if (el.selfClosing || !el.children || el.children.length === 0) {
1119
- return `<${el.tag}${attrs} />`;
1120
- }
1121
- const inner = el.children.map((c) => {
1122
- if (typeof c === "string") return escapeXml(c);
1123
- if (isCdata(c)) return renderCdata(c);
1124
- return renderInline(c);
1125
- }).join("");
1126
- return `<${el.tag}${attrs}>${inner}</${el.tag}>`;
1106
+ const attrs = el.attrs ? buildAttrs(el.attrs) : "";
1107
+ if (el.selfClosing || !el.children || el.children.length === 0) return `<${el.tag}${attrs} />`;
1108
+ const inner = el.children.map((c) => {
1109
+ if (typeof c === "string") return escapeXml(c);
1110
+ if (isCdata(c)) return renderCdata(c);
1111
+ return renderInline(c);
1112
+ }).join("");
1113
+ return `<${el.tag}${attrs}>${inner}</${el.tag}>`;
1127
1114
  }
1128
1115
  function isInlineElement(el) {
1129
- const inlineTags = /* @__PURE__ */ new Set([
1130
- "b",
1131
- "i",
1132
- "u",
1133
- "s",
1134
- "code",
1135
- "sub",
1136
- "sup",
1137
- "mark",
1138
- "strong",
1139
- "em",
1140
- "del",
1141
- "a",
1142
- "mention",
1143
- "tag",
1144
- "spoiler",
1145
- "ruby",
1146
- "math",
1147
- "footnote",
1148
- "comment"
1149
- ]);
1150
- return inlineTags.has(el.tag);
1116
+ return new Set([
1117
+ "b",
1118
+ "i",
1119
+ "u",
1120
+ "s",
1121
+ "code",
1122
+ "sub",
1123
+ "sup",
1124
+ "mark",
1125
+ "strong",
1126
+ "em",
1127
+ "del",
1128
+ "a",
1129
+ "mention",
1130
+ "tag",
1131
+ "spoiler",
1132
+ "ruby",
1133
+ "math",
1134
+ "footnote",
1135
+ "comment"
1136
+ ]).has(el.tag);
1151
1137
  }
1152
1138
  function pad(indent) {
1153
- return " ".repeat(indent);
1139
+ return " ".repeat(indent);
1154
1140
  }
1141
+ //#endregion
1142
+ //#region src/serializer.ts
1155
1143
  function serializeToXml(state, registry, options = {}) {
1156
- const root = state.root;
1157
- const children = root.children ?? [];
1158
- const selectedBlockIds = options.selectedBlockIds;
1159
- const ctx = createWriterContext(registry);
1160
- const content = children.flatMap((child) => {
1161
- const result = ctx.serializeNode(child);
1162
- const items = Array.isArray(result) ? result : [result];
1163
- if (!selectedBlockIds?.size) return items;
1164
- const blockId2 = child.$?.blockId;
1165
- if (!blockId2 || !selectedBlockIds.has(blockId2)) return items;
1166
- return items.map((item) => {
1167
- if (typeof item === "string" || "cdata" in item) return item;
1168
- return { ...item, attrs: { ...item.attrs, selected: "true" } };
1169
- });
1170
- });
1171
- if (options.compact) {
1172
- return `<doc>${renderXml(content, 0, options)}</doc>`;
1173
- }
1174
- return `<doc>
1175
- ${renderXml(content, 1, options)}</doc>
1176
- `;
1144
+ const children = state.root.children ?? [];
1145
+ const selectedBlockIds = options.selectedBlockIds;
1146
+ const ctx = createWriterContext(registry);
1147
+ const content = children.flatMap((child) => {
1148
+ const result = ctx.serializeNode(child);
1149
+ const items = Array.isArray(result) ? result : [result];
1150
+ if (!selectedBlockIds?.size) return items;
1151
+ const blockId = child.$?.blockId;
1152
+ if (!blockId || !selectedBlockIds.has(blockId)) return items;
1153
+ return items.map((item) => {
1154
+ if (typeof item === "string" || "cdata" in item) return item;
1155
+ return {
1156
+ ...item,
1157
+ attrs: {
1158
+ ...item.attrs,
1159
+ selected: "true"
1160
+ }
1161
+ };
1162
+ });
1163
+ });
1164
+ if (options.compact) return `<doc>${renderXml(content, 0, options)}</doc>`;
1165
+ return `<doc>\n${renderXml(content, 1, options)}</doc>\n`;
1177
1166
  }
1178
1167
  function serializeNodesToXml(nodes, registry, options = {}) {
1179
- const ctx = createWriterContext(registry);
1180
- const content = nodes.flatMap((node) => ctx.serializeNode(node));
1181
- return renderXml(content, 0, options);
1168
+ const ctx = createWriterContext(registry);
1169
+ return renderXml(nodes.flatMap((node) => ctx.serializeNode(node)), 0, options);
1182
1170
  }
1183
1171
  function createWriterContext(registry) {
1184
- const ctx = {
1185
- serializeChildren(children) {
1186
- return children.flatMap((child) => ctx.serializeNode(child));
1187
- },
1188
- serializeNode(node) {
1189
- const n = node;
1190
- if (n.type === "text") {
1191
- return wrapWithFormatTags(n.text ?? "", n.format ?? 0);
1192
- }
1193
- if (n.type === "linebreak") {
1194
- return { tag: "br", selfClosing: true };
1195
- }
1196
- const writer = registry.getWriter(n.type);
1197
- if (writer) {
1198
- const result = writer(node, ctx);
1199
- if (result !== false) return result;
1200
- }
1201
- return serializeFallback(node);
1202
- },
1203
- serializeNestedState(state) {
1204
- const root = state.root;
1205
- const children = root.children ?? [];
1206
- return children.flatMap((child) => ctx.serializeNode(child));
1207
- }
1208
- };
1209
- return ctx;
1172
+ const ctx = {
1173
+ serializeChildren(children) {
1174
+ return children.flatMap((child) => ctx.serializeNode(child));
1175
+ },
1176
+ serializeNode(node) {
1177
+ const n = node;
1178
+ if (n.type === "text") return wrapWithFormatTags(n.text ?? "", n.format ?? 0);
1179
+ if (n.type === "linebreak") return {
1180
+ tag: "br",
1181
+ selfClosing: true
1182
+ };
1183
+ const writer = registry.getWriter(n.type);
1184
+ if (writer) {
1185
+ const result = writer(node, ctx);
1186
+ if (result !== false) return result;
1187
+ }
1188
+ return serializeFallback(node);
1189
+ },
1190
+ serializeNestedState(state) {
1191
+ return (state.root.children ?? []).flatMap((child) => ctx.serializeNode(child));
1192
+ }
1193
+ };
1194
+ return ctx;
1210
1195
  }
1211
1196
  function serializeFallback(node) {
1212
- const n = node;
1213
- const blockId2 = n.$?.blockId;
1214
- const { type, $: _meta, version: _v, ...rest } = n;
1215
- const attrs = { type };
1216
- if (blockId2) attrs.id = blockId2;
1217
- const dataKeys = Object.keys(rest);
1218
- if (dataKeys.length > 0) {
1219
- attrs.data = JSON.stringify(rest);
1220
- }
1221
- return { tag: "node", attrs, selfClosing: true };
1197
+ const n = node;
1198
+ const blockId = n.$?.blockId;
1199
+ const { type, $: _meta, version: _v, ...rest } = n;
1200
+ const attrs = { type };
1201
+ if (blockId) attrs.id = blockId;
1202
+ if (Object.keys(rest).length > 0) attrs.data = JSON.stringify(rest);
1203
+ return {
1204
+ tag: "node",
1205
+ attrs,
1206
+ selfClosing: true
1207
+ };
1222
1208
  }
1223
- export {
1224
- LitexmlRegistry,
1225
- createDefaultRegistry,
1226
- deserializeFromXml,
1227
- deserializeNodesFromXml,
1228
- registerBuiltinReaders,
1229
- registerBuiltinWriters,
1230
- registerCustomReaders,
1231
- registerCustomWriters,
1232
- serializeNodesToXml,
1233
- serializeToXml
1234
- };
1209
+ //#endregion
1210
+ export { LitexmlRegistry, createDefaultRegistry, deserializeFromXml, deserializeNodesFromXml, registerBuiltinReaders, registerBuiltinWriters, registerCustomReaders, registerCustomWriters, serializeNodesToXml, serializeToXml };