@inshapardaz/likhari-converters 0.1.16 → 0.1.18-dev.124
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/html/parse.d.ts.map +1 -1
- package/dist/html/parse.js +46 -1
- package/dist/html/serialize.d.ts.map +1 -1
- package/dist/html/serialize.js +36 -2
- package/dist/markdown/mdast.d.ts +5 -0
- package/dist/markdown/mdast.d.ts.map +1 -1
- package/dist/markdown/parse.d.ts.map +1 -1
- package/dist/markdown/parse.js +26 -1
- package/dist/markdown/serialize.d.ts.map +1 -1
- package/dist/markdown/serialize.js +20 -0
- package/dist/plain-text/index.d.ts.map +1 -1
- package/dist/plain-text/index.js +38 -6
- package/dist/shared/serialized.d.ts +5 -0
- package/dist/shared/serialized.d.ts.map +1 -1
- package/dist/shared/serialized.js +15 -0
- package/package.json +1 -1
package/dist/html/parse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/html/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAiCjD,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIlD;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvD;
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/html/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAiCjD,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIlD;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvD;AA4UD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,qBAAqB,CAMvF"}
|
package/dist/html/parse.js
CHANGED
|
@@ -92,6 +92,10 @@ function collectInline(node, state, out, preserveWs) {
|
|
|
92
92
|
out.push({ type: 'linebreak', version: 1 });
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
|
+
if (tag === 'sup' && el.hasAttribute('data-likhari-footnote-ref')) {
|
|
96
|
+
out.push({ type: 'footnote-reference', version: 1, footnoteId: el.getAttribute('data-likhari-footnote-ref') ?? '' });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
95
99
|
if (tag === 'a') {
|
|
96
100
|
const url = safeUrl(el.getAttribute('href') ?? '');
|
|
97
101
|
const children = [];
|
|
@@ -247,6 +251,45 @@ function convertLayout(container) {
|
|
|
247
251
|
const templateColumns = container.style.gridTemplateColumns || `repeat(${Math.max(items.length, 2)}, 1fr)`;
|
|
248
252
|
return elementBase('layout-container', items.length > 0 ? items : [elementBase('layout-item', [paragraph()])], { templateColumns });
|
|
249
253
|
}
|
|
254
|
+
const POETRY_LAYOUTS = new Set(['single', 'two-column']);
|
|
255
|
+
const POETRY_ALIGNS = new Set(['justify', 'left', 'right', 'start']);
|
|
256
|
+
function convertPoetry(el) {
|
|
257
|
+
const rawLayout = el.getAttribute('data-likhari-poetry-layout') ?? 'single';
|
|
258
|
+
const layout = POETRY_LAYOUTS.has(rawLayout) ? rawLayout : 'single';
|
|
259
|
+
const rawAlign = el.getAttribute('data-likhari-poetry-align') ?? 'justify';
|
|
260
|
+
const align = POETRY_ALIGNS.has(rawAlign) ? rawAlign : 'justify';
|
|
261
|
+
if (layout === 'two-column') {
|
|
262
|
+
const containerEl = Array.from(el.children).find((c) => c.hasAttribute('data-likhari-layout-container'));
|
|
263
|
+
const container = containerEl
|
|
264
|
+
? convertLayout(containerEl)
|
|
265
|
+
: elementBase('layout-container', [elementBase('layout-item', [paragraph()]), elementBase('layout-item', [paragraph()])], {
|
|
266
|
+
templateColumns: 'repeat(2, 1fr)',
|
|
267
|
+
});
|
|
268
|
+
return elementBase('poetry-couplet', [container], { layout, align });
|
|
269
|
+
}
|
|
270
|
+
const children = flowBlocks(el);
|
|
271
|
+
return elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], { layout, align });
|
|
272
|
+
}
|
|
273
|
+
function convertFootnoteList(el) {
|
|
274
|
+
const items = [];
|
|
275
|
+
Array.from(el.children).forEach((child) => {
|
|
276
|
+
if (child.tagName.toLowerCase() !== 'li')
|
|
277
|
+
return;
|
|
278
|
+
const li = child;
|
|
279
|
+
const footnoteId = li.getAttribute('data-likhari-footnote-item') ?? (li.getAttribute('id') ?? '').replace(/^fn-/, '');
|
|
280
|
+
if (!footnoteId)
|
|
281
|
+
return;
|
|
282
|
+
// Drop the "↩" backlink our own serializer appends — it's a generated
|
|
283
|
+
// affordance, not part of the note's content.
|
|
284
|
+
const clone = li.cloneNode(true);
|
|
285
|
+
Array.from(clone.querySelectorAll('a'))
|
|
286
|
+
.filter((a) => a.getAttribute('href') === `#fnref-${footnoteId}`)
|
|
287
|
+
.forEach((a) => a.remove());
|
|
288
|
+
const children = flowBlocks(clone);
|
|
289
|
+
items.push(elementBase('footnote-item', children.length > 0 ? children : [paragraph()], { footnoteId }));
|
|
290
|
+
});
|
|
291
|
+
return elementBase('footnote-list', items);
|
|
292
|
+
}
|
|
250
293
|
function convertImage(img, caption) {
|
|
251
294
|
const src = safeImageSrc(img.getAttribute('src') ?? '');
|
|
252
295
|
if (!src)
|
|
@@ -292,7 +335,7 @@ function convertBlock(el) {
|
|
|
292
335
|
}
|
|
293
336
|
case 'ul':
|
|
294
337
|
case 'ol':
|
|
295
|
-
return [convertList(el)];
|
|
338
|
+
return el.hasAttribute('data-likhari-footnote-list') ? [convertFootnoteList(el)] : [convertList(el)];
|
|
296
339
|
case 'li':
|
|
297
340
|
return convertListItem(el, false).length > 0 ? [elementBase('list', convertListItem(el, false), { listType: 'bullet', start: 1, tag: 'ul' })] : [];
|
|
298
341
|
case 'table':
|
|
@@ -317,6 +360,8 @@ function convertBlock(el) {
|
|
|
317
360
|
return [{ type: 'page-break', version: 1 }];
|
|
318
361
|
if (el.hasAttribute('data-likhari-layout-container'))
|
|
319
362
|
return [convertLayout(el)];
|
|
363
|
+
if (el.hasAttribute('data-likhari-poetry-layout'))
|
|
364
|
+
return [convertPoetry(el)];
|
|
320
365
|
return flowBlocks(el);
|
|
321
366
|
case 'figcaption':
|
|
322
367
|
return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/html/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/html/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAEkG,KAAK,KAAK,EAClH,MAAM,sBAAsB,CAAC;AAE9B,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAuID,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,gBAAgB,GAAG,MAAM,CA+CtE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAI1F"}
|
package/dist/html/serialize.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FORMAT_BOLD, FORMAT_CODE, FORMAT_HIGHLIGHT, FORMAT_ITALIC, FORMAT_STRIKETHROUGH, FORMAT_SUBSCRIPT, FORMAT_SUPERSCRIPT, FORMAT_UNDERLINE, INDENT_PX, rootChildren, } from '../shared/serialized.js';
|
|
1
|
+
import { FORMAT_BOLD, FORMAT_CODE, FORMAT_HIGHLIGHT, FORMAT_ITALIC, FORMAT_STRIKETHROUGH, FORMAT_SUBSCRIPT, FORMAT_SUPERSCRIPT, FORMAT_UNDERLINE, INDENT_PX, collectFootnoteOrder, rootChildren, } from '../shared/serialized.js';
|
|
2
2
|
export function escapeHtml(value) {
|
|
3
3
|
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
4
4
|
}
|
|
@@ -87,6 +87,26 @@ function tableToHtml(node, ctx) {
|
|
|
87
87
|
.join('');
|
|
88
88
|
return `<table><tbody>${rows}</tbody></table>`;
|
|
89
89
|
}
|
|
90
|
+
/** Set once per serializeHtml() call and read by the footnote cases below —
|
|
91
|
+
* avoids threading a numbering map through every recursive nodeToHtml call
|
|
92
|
+
* for what is otherwise a leaf concern of two node types. Safe because
|
|
93
|
+
* serialization is synchronous and this module is never re-entered mid-call. */
|
|
94
|
+
let footnoteOrder = new Map();
|
|
95
|
+
function footnoteReferenceToHtml(node) {
|
|
96
|
+
const id = String(node.footnoteId ?? '');
|
|
97
|
+
const n = footnoteOrder.get(id) ?? '?';
|
|
98
|
+
return `<sup data-likhari-footnote-ref="${escapeAttr(id)}"><a href="#fn-${escapeAttr(id)}" id="fnref-${escapeAttr(id)}">${n}</a></sup>`;
|
|
99
|
+
}
|
|
100
|
+
function footnoteListToHtml(node, ctx) {
|
|
101
|
+
const items = (node.children ?? [])
|
|
102
|
+
.map((item) => {
|
|
103
|
+
const id = String(item.footnoteId ?? '');
|
|
104
|
+
const body = (item.children ?? []).map((child) => nodeToHtml(child, ctx)).join('');
|
|
105
|
+
return `<li id="fn-${escapeAttr(id)}" data-likhari-footnote-item="${escapeAttr(id)}">${body} <a href="#fnref-${escapeAttr(id)}">↩</a></li>`;
|
|
106
|
+
})
|
|
107
|
+
.join('');
|
|
108
|
+
return `<ol data-likhari-footnote-list>${items}</ol>`;
|
|
109
|
+
}
|
|
90
110
|
function layoutToHtml(node, ctx) {
|
|
91
111
|
const items = node.children ?? [];
|
|
92
112
|
const templateColumns = typeof node.templateColumns === 'string' ? node.templateColumns : `repeat(${items.length}, 1fr)`;
|
|
@@ -95,6 +115,12 @@ function layoutToHtml(node, ctx) {
|
|
|
95
115
|
.join('');
|
|
96
116
|
return `<div data-likhari-layout-container style="display: grid; grid-template-columns: ${escapeAttr(templateColumns)}">${columns}</div>`;
|
|
97
117
|
}
|
|
118
|
+
function poetryToHtml(node, ctx) {
|
|
119
|
+
const layout = node.layout === 'two-column' ? 'two-column' : 'single';
|
|
120
|
+
const align = typeof node.align === 'string' ? node.align : 'justify';
|
|
121
|
+
const inner = (node.children ?? []).map((child) => nodeToHtml(child, ctx)).join('');
|
|
122
|
+
return `<div data-likhari-poetry-layout="${layout}" data-likhari-poetry-align="${align}" style="text-align: ${escapeAttr(align)}">${inner}</div>`;
|
|
123
|
+
}
|
|
98
124
|
function imageToHtml(node, ctx) {
|
|
99
125
|
const rawSrc = String(node.src ?? '');
|
|
100
126
|
const src = ctx?.resolveImageUrl ? ctx.resolveImageUrl(rawSrc) : rawSrc;
|
|
@@ -143,11 +169,19 @@ export function nodeToHtml(node, ctx) {
|
|
|
143
169
|
return tableToHtml(node, ctx);
|
|
144
170
|
case 'layout-container':
|
|
145
171
|
return layoutToHtml(node, ctx);
|
|
172
|
+
case 'footnote-reference':
|
|
173
|
+
return footnoteReferenceToHtml(node);
|
|
174
|
+
case 'footnote-list':
|
|
175
|
+
return footnoteListToHtml(node, ctx);
|
|
176
|
+
case 'poetry-couplet':
|
|
177
|
+
return poetryToHtml(node, ctx);
|
|
146
178
|
default:
|
|
147
179
|
// Unknown node (e.g. a future feature): keep its content, drop the wrapper.
|
|
148
180
|
return inlineChildren(node, ctx);
|
|
149
181
|
}
|
|
150
182
|
}
|
|
151
183
|
export function serializeHtml(state, ctx) {
|
|
152
|
-
|
|
184
|
+
const blocks = rootChildren(state);
|
|
185
|
+
footnoteOrder = collectFootnoteOrder(blocks);
|
|
186
|
+
return blocks.map((node) => nodeToHtml(node, ctx)).join('\n');
|
|
153
187
|
}
|
package/dist/markdown/mdast.d.ts
CHANGED
|
@@ -16,6 +16,11 @@ export interface MdNode {
|
|
|
16
16
|
name?: string;
|
|
17
17
|
attributes?: Record<string, string | null | undefined> | null;
|
|
18
18
|
align?: Array<'left' | 'right' | 'center' | null> | null;
|
|
19
|
+
/** GFM footnotes (remark-gfm): `identifier` is normalized/lowercased by
|
|
20
|
+
* remark itself, `label` keeps the original casing. We only ever use
|
|
21
|
+
* plain numerals for both, so the distinction doesn't matter here. */
|
|
22
|
+
identifier?: string;
|
|
23
|
+
label?: string;
|
|
19
24
|
}
|
|
20
25
|
export type Attrs = Record<string, string>;
|
|
21
26
|
export declare function directive(type: 'textDirective' | 'leafDirective' | 'containerDirective', name: string, attributes: Attrs, children?: MdNode[]): MdNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdast.d.ts","sourceRoot":"","sources":["../../src/markdown/mdast.ts"],"names":[],"mappings":"AAAA;qFACqF;AACrF,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;IAC9D,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"mdast.d.ts","sourceRoot":"","sources":["../../src/markdown/mdast.ts"],"names":[],"mappings":"AAAA;qFACqF;AACrF,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;IAC9D,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD;;0EAEsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE3C,wBAAgB,SAAS,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe,GAAG,oBAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,MAAM,CAE1J"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/markdown/parse.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/markdown/parse.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAwTjD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,qBAAqB,CAO3F"}
|
package/dist/markdown/parse.js
CHANGED
|
@@ -64,6 +64,9 @@ function inline(nodes, state, out) {
|
|
|
64
64
|
out.push(elementBase('link', children, { url, rel: null, target: null, title: node.title ?? null }));
|
|
65
65
|
break;
|
|
66
66
|
}
|
|
67
|
+
case 'footnoteReference':
|
|
68
|
+
out.push({ type: 'footnote-reference', version: 1, footnoteId: node.identifier ?? '' });
|
|
69
|
+
break;
|
|
67
70
|
case 'textDirective': {
|
|
68
71
|
const name = node.name ?? '';
|
|
69
72
|
if (name === 'span') {
|
|
@@ -234,6 +237,13 @@ function blocks(nodes, extra = {}) {
|
|
|
234
237
|
out.push(elementBase('layout-container', columns, { templateColumns: `repeat(${columns.length}, 1fr)` }));
|
|
235
238
|
}
|
|
236
239
|
}
|
|
240
|
+
else if (node.name === 'poetry') {
|
|
241
|
+
const attrs = node.attributes ?? {};
|
|
242
|
+
const layout = attrs.layout === 'two-column' ? 'two-column' : 'single';
|
|
243
|
+
const align = ALIGNMENTS.has(attrs.align ?? '') ? attrs.align : 'justify';
|
|
244
|
+
const children = blocks(node.children ?? []);
|
|
245
|
+
out.push(elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], { layout, align }));
|
|
246
|
+
}
|
|
237
247
|
else {
|
|
238
248
|
// Unknown containers (including a stray "column" outside "columns")
|
|
239
249
|
// are transparent: their content is kept.
|
|
@@ -290,8 +300,23 @@ function restoreUnknownDirectives(node, source) {
|
|
|
290
300
|
return c;
|
|
291
301
|
});
|
|
292
302
|
}
|
|
303
|
+
/** Footnote definitions are always top-level in CommonMark/GFM (never nested
|
|
304
|
+
* inside a list/quote/etc.), wherever in the source they appear — so unlike
|
|
305
|
+
* everything else `blocks()` handles, they're pulled out before the normal
|
|
306
|
+
* recursive walk and collected into the one footnote-list our editor model
|
|
307
|
+
* expects, instead of staying scattered where the source happened to put them. */
|
|
308
|
+
function extractFootnoteList(topLevel) {
|
|
309
|
+
const defs = topLevel.filter((node) => node.type === 'footnoteDefinition');
|
|
310
|
+
if (defs.length === 0)
|
|
311
|
+
return null;
|
|
312
|
+
const items = defs.map((def) => elementBase('footnote-item', blocks(def.children ?? []), { footnoteId: def.identifier ?? '' }));
|
|
313
|
+
return elementBase('footnote-list', items);
|
|
314
|
+
}
|
|
293
315
|
export function parseMarkdown(input, _ctx) {
|
|
294
316
|
const tree = processor.parse(input);
|
|
295
317
|
restoreUnknownDirectives(tree, input);
|
|
296
|
-
|
|
318
|
+
const topLevel = tree.children ?? [];
|
|
319
|
+
const footnoteList = extractFootnoteList(topLevel);
|
|
320
|
+
const body = blocks(topLevel.filter((node) => node.type !== 'footnoteDefinition'));
|
|
321
|
+
return makeState(footnoteList ? [...body, footnoteList] : body);
|
|
297
322
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/markdown/serialize.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAKjD,OAAO,EAAa,KAAK,KAAK,EAAe,MAAM,SAAS,CAAC;AAyC7D,2GAA2G;AAC3G,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAUjD;
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/markdown/serialize.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAKjD,OAAO,EAAa,KAAK,KAAK,EAAe,MAAM,SAAS,CAAC;AAyC7D,2GAA2G;AAC3G,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAUjD;AAqHD,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,CAMhE;AAoID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAI9F"}
|
|
@@ -152,6 +152,11 @@ function inlineNodes(children, ctx) {
|
|
|
152
152
|
flush();
|
|
153
153
|
out.push({ type: 'link', url: String(child.url ?? ''), title: child.title ? String(child.title) : null, children: inlineNodes(child.children ?? [], ctx) });
|
|
154
154
|
}
|
|
155
|
+
else if (child.type === 'footnote-reference') {
|
|
156
|
+
flush();
|
|
157
|
+
const id = String(child.footnoteId ?? '');
|
|
158
|
+
out.push({ type: 'footnoteReference', identifier: id, label: id });
|
|
159
|
+
}
|
|
155
160
|
else if (child.children) {
|
|
156
161
|
flush();
|
|
157
162
|
out.push(...inlineNodes(child.children, ctx));
|
|
@@ -241,6 +246,12 @@ function layoutToMd(node, ctx) {
|
|
|
241
246
|
const columns = items.map((item) => directive('containerDirective', 'column', {}, (item.children ?? []).flatMap((child) => blockToMd(child, ctx))));
|
|
242
247
|
return directive('containerDirective', 'columns', { count: String(items.length) }, columns);
|
|
243
248
|
}
|
|
249
|
+
function poetryToMd(node, ctx) {
|
|
250
|
+
const layout = node.layout === 'two-column' ? 'two-column' : 'single';
|
|
251
|
+
const align = typeof node.align === 'string' ? node.align : 'justify';
|
|
252
|
+
const inner = (node.children ?? []).flatMap((child) => blockToMd(child, ctx));
|
|
253
|
+
return directive('containerDirective', 'poetry', { layout, align }, inner);
|
|
254
|
+
}
|
|
244
255
|
function tableToMd(node, ctx) {
|
|
245
256
|
const rows = (node.children ?? []).map((row) => ({
|
|
246
257
|
type: 'tableRow',
|
|
@@ -284,6 +295,15 @@ function blockToMd(node, ctx) {
|
|
|
284
295
|
return [tableToMd(node, ctx)];
|
|
285
296
|
case 'layout-container':
|
|
286
297
|
return [layoutToMd(node, ctx)];
|
|
298
|
+
case 'poetry-couplet':
|
|
299
|
+
return [poetryToMd(node, ctx)];
|
|
300
|
+
case 'footnote-list':
|
|
301
|
+
return (node.children ?? []).map((item) => ({
|
|
302
|
+
type: 'footnoteDefinition',
|
|
303
|
+
identifier: String(item.footnoteId ?? ''),
|
|
304
|
+
label: String(item.footnoteId ?? ''),
|
|
305
|
+
children: (item.children ?? []).flatMap((child) => blockToMd(child, ctx)),
|
|
306
|
+
}));
|
|
287
307
|
default:
|
|
288
308
|
// Fallback rule (dialect spec §6): unknown blocks keep their text as paragraphs.
|
|
289
309
|
return (node.children ?? []).flatMap((child) => blockToMd(child, ctx));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plain-text/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plain-text/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAuFhD,eAAO,MAAM,kBAAkB,EAAE,eAsDhC,CAAC"}
|
package/dist/plain-text/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { collectFootnoteOrder } from '../shared/serialized.js';
|
|
1
2
|
const LIST_ITEM_PREFIX = {
|
|
2
3
|
bullet: () => '- ',
|
|
3
4
|
number: (index) => `${index + 1}. `,
|
|
@@ -9,7 +10,7 @@ const LIST_ITEM_PREFIX = {
|
|
|
9
10
|
* all formatting — this is a documented, exhaustive mapping per node type,
|
|
10
11
|
* not an ad hoc `.textContent` walk.
|
|
11
12
|
*/
|
|
12
|
-
function nodeToText(node, listItemIndex = 0) {
|
|
13
|
+
function nodeToText(node, order, listItemIndex = 0) {
|
|
13
14
|
const withChildren = node;
|
|
14
15
|
if (node.type === 'text' || node.type === 'linebreak') {
|
|
15
16
|
return node.text ?? (node.type === 'linebreak' ? '\n' : '');
|
|
@@ -20,28 +21,59 @@ function nodeToText(node, listItemIndex = 0) {
|
|
|
20
21
|
const image = node;
|
|
21
22
|
return image.altText || image.caption || '';
|
|
22
23
|
}
|
|
24
|
+
// Inline marker only — its matching note is in the trailing list (below),
|
|
25
|
+
// never inline, per the fidelity matrix's "inline bracketed number".
|
|
26
|
+
if (node.type === 'footnote-reference') {
|
|
27
|
+
const id = String(node.footnoteId ?? '');
|
|
28
|
+
return `[${order.get(id) ?? '?'}]`;
|
|
29
|
+
}
|
|
30
|
+
// Linearized per the fidelity matrix ("Poetry layout variants ⚠️ linearized"):
|
|
31
|
+
// the two misras, one per line, regardless of single/two-column layout —
|
|
32
|
+
// the columns primitive a two-column couplet is built on otherwise has no
|
|
33
|
+
// per-node case and would fall through to running the lines together.
|
|
34
|
+
if (node.type === 'poetry-couplet') {
|
|
35
|
+
const children = withChildren.children ?? [];
|
|
36
|
+
const misraHolders = children[0]?.type === 'layout-container' ? (children[0].children ?? []) : children;
|
|
37
|
+
return misraHolders.map((child) => nodeToText(child, order)).join('\n');
|
|
38
|
+
}
|
|
23
39
|
if (node.type === 'list' && withChildren.children) {
|
|
24
40
|
const listType = node.listType ?? 'bullet';
|
|
25
41
|
return withChildren.children
|
|
26
42
|
.map((child, i) => {
|
|
27
43
|
const prefix = LIST_ITEM_PREFIX[listType]?.(i) ?? '- ';
|
|
28
|
-
return prefix + nodeToText(child, i);
|
|
44
|
+
return prefix + nodeToText(child, order, i);
|
|
29
45
|
})
|
|
30
46
|
.join('\n');
|
|
31
47
|
}
|
|
32
48
|
if (withChildren.children) {
|
|
33
|
-
return withChildren.children.map((child) => nodeToText(child, listItemIndex)).join('');
|
|
49
|
+
return withChildren.children.map((child) => nodeToText(child, order, listItemIndex)).join('');
|
|
34
50
|
}
|
|
35
51
|
return '';
|
|
36
52
|
}
|
|
37
|
-
function blockToText(node) {
|
|
38
|
-
return nodeToText(node);
|
|
53
|
+
function blockToText(node, order) {
|
|
54
|
+
return nodeToText(node, order);
|
|
55
|
+
}
|
|
56
|
+
/** `[n] note text` per footnote, in display-number order — the "trailing
|
|
57
|
+
* list" half of the fidelity matrix's plain-text footnote entry. */
|
|
58
|
+
function footnoteListToText(node, order) {
|
|
59
|
+
const items = node.children ?? [];
|
|
60
|
+
return items
|
|
61
|
+
.map((item) => {
|
|
62
|
+
const id = String(item.footnoteId ?? '');
|
|
63
|
+
return `[${order.get(id) ?? '?'}] ${nodeToText(item, order)}`;
|
|
64
|
+
})
|
|
65
|
+
.join('\n');
|
|
39
66
|
}
|
|
40
67
|
export const plainTextConverter = {
|
|
41
68
|
id: 'plain-text',
|
|
42
69
|
serialize(editorState) {
|
|
43
70
|
const root = editorState.root;
|
|
44
|
-
const
|
|
71
|
+
const children = root.children ?? [];
|
|
72
|
+
const order = collectFootnoteOrder(children);
|
|
73
|
+
const footnoteLists = children.filter((n) => n.type === 'footnote-list');
|
|
74
|
+
const blocks = children.filter((n) => n.type !== 'footnote-list').map((n) => blockToText(n, order));
|
|
75
|
+
if (footnoteLists.length > 0)
|
|
76
|
+
blocks.push(footnoteLists.map((n) => footnoteListToText(n, order)).join('\n'));
|
|
45
77
|
return blocks.join('\n\n');
|
|
46
78
|
},
|
|
47
79
|
parse(input) {
|
|
@@ -28,4 +28,9 @@ export declare function paragraph(children?: SNode[], extra?: Record<string, unk
|
|
|
28
28
|
export declare function makeState(blocks: SNode[]): SerializedEditorState;
|
|
29
29
|
/** Inline node types: everything else is a block. */
|
|
30
30
|
export declare function isInlineNode(node: SNode): boolean;
|
|
31
|
+
/** Footnote reference numbers (lexical-editor-spec.md §4.10) are never
|
|
32
|
+
* stored — they're each `footnote-reference` node's position, in document
|
|
33
|
+
* order, among all such nodes. Walks the whole tree once so every converter
|
|
34
|
+
* numbers references and their matching footnote-list entries identically. */
|
|
35
|
+
export declare function collectFootnoteOrder(blocks: SNode[]): Map<string, number>;
|
|
31
36
|
//# sourceMappingURL=serialized.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialized.d.ts","sourceRoot":"","sources":["../../src/shared/serialized.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAErD;;;;GAIG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,6DAA6D;AAC7D,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;AAE7C,wBAAgB,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,KAAK,EAAE,CAElE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,KAAK,SAAK,GAAG,KAAK,CAEpE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,KAAK,CAEvG;AAED,wBAAgB,SAAS,CAAC,QAAQ,GAAE,KAAK,EAAO,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,KAAK,CAE5F;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,qBAAqB,CAKhE;AAED,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,OAAO,CAEjD"}
|
|
1
|
+
{"version":3,"file":"serialized.d.ts","sourceRoot":"","sources":["../../src/shared/serialized.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAErD;;;;GAIG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,6DAA6D;AAC7D,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;AAE7C,wBAAgB,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,KAAK,EAAE,CAElE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,KAAK,SAAK,GAAG,KAAK,CAEpE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,KAAK,CAEvG;AAED,wBAAgB,SAAS,CAAC,QAAQ,GAAE,KAAK,EAAO,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,KAAK,CAE5F;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,qBAAqB,CAKhE;AAED,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,OAAO,CAEjD;AAED;;;8EAG8E;AAC9E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAUzE"}
|
|
@@ -30,3 +30,18 @@ export function makeState(blocks) {
|
|
|
30
30
|
export function isInlineNode(node) {
|
|
31
31
|
return node.type === 'text' || node.type === 'linebreak' || node.type === 'tab' || node.type === 'link' || node.type === 'autolink';
|
|
32
32
|
}
|
|
33
|
+
/** Footnote reference numbers (lexical-editor-spec.md §4.10) are never
|
|
34
|
+
* stored — they're each `footnote-reference` node's position, in document
|
|
35
|
+
* order, among all such nodes. Walks the whole tree once so every converter
|
|
36
|
+
* numbers references and their matching footnote-list entries identically. */
|
|
37
|
+
export function collectFootnoteOrder(blocks) {
|
|
38
|
+
const order = new Map();
|
|
39
|
+
const visit = (node) => {
|
|
40
|
+
if (node.type === 'footnote-reference' && typeof node.footnoteId === 'string' && !order.has(node.footnoteId)) {
|
|
41
|
+
order.set(node.footnoteId, order.size + 1);
|
|
42
|
+
}
|
|
43
|
+
(node.children ?? []).forEach(visit);
|
|
44
|
+
};
|
|
45
|
+
blocks.forEach(visit);
|
|
46
|
+
return order;
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inshapardaz/likhari-converters",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18-dev.124",
|
|
4
4
|
"description": "Format transformers (Markdown/HTML/plain-text/Lexical JSON) against the canonical Lexical EditorState, isolated from UI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|