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