@inshapardaz/likhari-converters 0.1.18-dev.156 → 0.1.18-dev.162
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 +23 -33
- package/dist/html/serialize.d.ts +2 -0
- package/dist/html/serialize.d.ts.map +1 -1
- package/dist/html/serialize.js +104 -18
- package/dist/markdown/parse.d.ts.map +1 -1
- package/dist/markdown/parse.js +14 -16
- package/dist/markdown/serialize.d.ts.map +1 -1
- package/dist/markdown/serialize.js +24 -11
- package/dist/plain-text/index.d.ts.map +1 -1
- package/dist/plain-text/index.js +6 -10
- package/dist/shared/poetry.d.ts +17 -0
- package/dist/shared/poetry.d.ts.map +1 -0
- package/dist/shared/poetry.js +33 -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;
|
|
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;AAkCjD,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;AAoVD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,qBAAqB,CAMvF"}
|
package/dist/html/parse.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { layoutOrDefault, scaleOrDefault } from '../shared/poetry.js';
|
|
1
2
|
import { FORMAT_BOLD, FORMAT_CODE, FORMAT_HIGHLIGHT, FORMAT_ITALIC, FORMAT_STRIKETHROUGH, FORMAT_SUBSCRIPT, FORMAT_SUPERSCRIPT, FORMAT_UNDERLINE, INDENT_PX, elementBase, makeState, paragraph, textNode, } from '../shared/serialized.js';
|
|
2
3
|
const ALIGNMENTS = new Set(['left', 'right', 'center', 'justify', 'start', 'end']);
|
|
3
4
|
const TAG_FORMATS = {
|
|
@@ -251,43 +252,30 @@ function convertLayout(container) {
|
|
|
251
252
|
const templateColumns = container.style.gridTemplateColumns || `repeat(${Math.max(items.length, 2)}, 1fr)`;
|
|
252
253
|
return elementBase('layout-container', items.length > 0 ? items : [elementBase('layout-item', [paragraph()])], { templateColumns });
|
|
253
254
|
}
|
|
254
|
-
const POETRY_LAYOUTS = new Set(['single', 'two-column', 'alternating']);
|
|
255
|
-
const POETRY_ALIGNS = new Set(['justify', 'left', 'right', 'start']);
|
|
256
|
-
function convertPoetryCouplet(el) {
|
|
257
|
-
const centered = el.getAttribute('data-likhari-poetry-couplet-centered') === 'true';
|
|
258
|
-
const children = flowBlocks(el);
|
|
259
|
-
return elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], { centered });
|
|
260
|
-
}
|
|
261
255
|
function convertPoetry(el) {
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const couplets = Array.from(el.children)
|
|
271
|
-
.map((c) => {
|
|
272
|
-
if (c.hasAttribute('data-likhari-layout-container'))
|
|
273
|
-
return convertLayout(c);
|
|
274
|
-
if (c.hasAttribute('data-likhari-poetry-couplet'))
|
|
275
|
-
return convertPoetryCouplet(c);
|
|
276
|
-
return null;
|
|
277
|
-
})
|
|
278
|
-
.filter((n) => n !== null);
|
|
279
|
-
if (couplets.length > 0)
|
|
280
|
-
return elementBase('poetry-block', couplets, { layout, align });
|
|
281
|
-
// Fallback: legacy markup with no explicit couplet wrappers at all.
|
|
256
|
+
const layout = layoutOrDefault(el.getAttribute('data-likhari-poetry-layout'));
|
|
257
|
+
const settings = {
|
|
258
|
+
layout,
|
|
259
|
+
spacing: scaleOrDefault(el.getAttribute('data-likhari-poetry-spacing')),
|
|
260
|
+
gutter: scaleOrDefault(el.getAttribute('data-likhari-poetry-gutter')),
|
|
261
|
+
stagger: scaleOrDefault(el.getAttribute('data-likhari-poetry-stagger')),
|
|
262
|
+
...(Number(el.getAttribute('data-likhari-poetry-width')) > 0 ? { width: Number(el.getAttribute('data-likhari-poetry-width')) } : {}),
|
|
263
|
+
};
|
|
282
264
|
if (layout === 'two-column') {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
265
|
+
// One LayoutContainerNode row per couplet — grab all of them, not just
|
|
266
|
+
// the first, or every couplet past the first silently disappears.
|
|
267
|
+
const containerEls = Array.from(el.children).filter((c) => c.hasAttribute('data-likhari-layout-container'));
|
|
268
|
+
const containers = containerEls.length > 0
|
|
269
|
+
? containerEls.map((c) => convertLayout(c))
|
|
270
|
+
: [
|
|
271
|
+
elementBase('layout-container', [elementBase('layout-item', [paragraph()]), elementBase('layout-item', [paragraph()])], {
|
|
272
|
+
templateColumns: 'repeat(2, 1fr)',
|
|
273
|
+
}),
|
|
274
|
+
];
|
|
275
|
+
return elementBase('poetry-couplet', containers, settings);
|
|
288
276
|
}
|
|
289
277
|
const children = flowBlocks(el);
|
|
290
|
-
return elementBase('poetry-
|
|
278
|
+
return elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], settings);
|
|
291
279
|
}
|
|
292
280
|
function convertFootnoteList(el) {
|
|
293
281
|
const items = [];
|
|
@@ -304,6 +292,8 @@ function convertFootnoteList(el) {
|
|
|
304
292
|
Array.from(clone.querySelectorAll('a'))
|
|
305
293
|
.filter((a) => a.getAttribute('href') === `#fnref-${footnoteId}`)
|
|
306
294
|
.forEach((a) => a.remove());
|
|
295
|
+
// Likewise the "[n]" marker our serializer writes in front of the note.
|
|
296
|
+
clone.querySelectorAll('[data-likhari-footnote-number]').forEach((n) => n.remove());
|
|
307
297
|
const children = flowBlocks(clone);
|
|
308
298
|
items.push(elementBase('footnote-item', children.length > 0 ? children : [paragraph()], { footnoteId }));
|
|
309
299
|
});
|
package/dist/html/serialize.d.ts
CHANGED
|
@@ -4,5 +4,7 @@ import { type SNode } from '../shared/serialized.js';
|
|
|
4
4
|
export declare function escapeHtml(value: string): string;
|
|
5
5
|
export declare function escapeAttr(value: string): string;
|
|
6
6
|
export declare function nodeToHtml(node: SNode, ctx?: ConverterContext): string;
|
|
7
|
+
/** Lets another converter (Markdown's HTML-table fallback) number footnotes the same way. */
|
|
8
|
+
export declare function setFootnoteOrder(blocks: SNode[]): void;
|
|
7
9
|
export declare function serializeHtml(state: SerializedEditorState, ctx?: ConverterContext): string;
|
|
8
10
|
//# sourceMappingURL=serialize.d.ts.map
|
|
@@ -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;
|
|
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;AAIjD,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;AAiOD,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,gBAAgB,GAAG,MAAM,CA+CtE;AAED,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAEtD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAI1F"}
|
package/dist/html/serialize.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { POETRY_CENTERED_WIDTH, POETRY_COUPLET_GAP, POETRY_GUTTER_PX, POETRY_RULE, POETRY_STAGGER_WIDTH, layoutOrDefault, scaleOrDefault, } from '../shared/poetry.js';
|
|
1
2
|
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
3
|
export function escapeHtml(value) {
|
|
3
4
|
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
@@ -69,6 +70,13 @@ function listItemToHtml(item, listType, ctx) {
|
|
|
69
70
|
const checked = listType === 'check' ? ` data-checked="${item.checked ? 'true' : 'false'}"` : '';
|
|
70
71
|
return `<li${blockAttrs(item, checked)}>${inlineChildren(item, ctx)}</li>`;
|
|
71
72
|
}
|
|
73
|
+
/** Table and footnote styling matching editor.css, written inline so exported HTML keeps it. */
|
|
74
|
+
const TABLE_BORDER = '#cfcac0';
|
|
75
|
+
const TABLE_HEADER_BG = '#f1efe9';
|
|
76
|
+
const TABLE_STYLE = 'border-collapse: collapse; margin: 1em 0; width: 100%';
|
|
77
|
+
const CELL_STYLE = `border: 1px solid ${TABLE_BORDER}; padding: 6px 10px; min-width: 72px; vertical-align: top`;
|
|
78
|
+
const HEADER_CELL_STYLE = `background: ${TABLE_HEADER_BG}; font-weight: 600; text-align: start`;
|
|
79
|
+
const FOOTNOTE_LIST_STYLE = `list-style: none; margin: 2em 0 0 0; padding: 0.75em 0 0 1.5em; border-top: 1px solid ${TABLE_BORDER}; font-size: 0.9em`;
|
|
72
80
|
function tableToHtml(node, ctx) {
|
|
73
81
|
const rows = (node.children ?? [])
|
|
74
82
|
.map((row) => {
|
|
@@ -79,13 +87,14 @@ function tableToHtml(node, ctx) {
|
|
|
79
87
|
const span = (Number(cell.colSpan) > 1 ? ` colspan="${Number(cell.colSpan)}"` : '') +
|
|
80
88
|
(Number(cell.rowSpan) > 1 ? ` rowspan="${Number(cell.rowSpan)}"` : '');
|
|
81
89
|
const content = (cell.children ?? []).map((child) => nodeToHtml(child, ctx)).join('');
|
|
82
|
-
|
|
90
|
+
const style = header ? `${CELL_STYLE}; ${HEADER_CELL_STYLE}` : CELL_STYLE;
|
|
91
|
+
return `<${tag}${span} style="${escapeAttr(style)}">${content}</${tag}>`;
|
|
83
92
|
})
|
|
84
93
|
.join('');
|
|
85
94
|
return `<tr>${cells}</tr>`;
|
|
86
95
|
})
|
|
87
96
|
.join('');
|
|
88
|
-
return `<table><tbody>${rows}</tbody></table>`;
|
|
97
|
+
return `<table style="${escapeAttr(TABLE_STYLE)}"><tbody>${rows}</tbody></table>`;
|
|
89
98
|
}
|
|
90
99
|
/** Set once per serializeHtml() call and read by the footnote cases below —
|
|
91
100
|
* avoids threading a numbering map through every recursive nodeToHtml call
|
|
@@ -102,10 +111,11 @@ function footnoteListToHtml(node, ctx) {
|
|
|
102
111
|
.map((item) => {
|
|
103
112
|
const id = String(item.footnoteId ?? '');
|
|
104
113
|
const body = (item.children ?? []).map((child) => nodeToHtml(child, ctx)).join('');
|
|
105
|
-
|
|
114
|
+
const number = `<span data-likhari-footnote-number style="font-weight: 600">[${footnoteOrder.get(id) ?? '?'}] </span>`;
|
|
115
|
+
return `<li id="fn-${escapeAttr(id)}" data-likhari-footnote-item="${escapeAttr(id)}" style="margin: 0.3em 0">${number}${body} <a href="#fnref-${escapeAttr(id)}">↩</a></li>`;
|
|
106
116
|
})
|
|
107
117
|
.join('');
|
|
108
|
-
return `<ol data-likhari-footnote-list>${items}</ol>`;
|
|
118
|
+
return `<ol data-likhari-footnote-list style="${escapeAttr(FOOTNOTE_LIST_STYLE)}">${items}</ol>`;
|
|
109
119
|
}
|
|
110
120
|
function layoutToHtml(node, ctx) {
|
|
111
121
|
const items = node.children ?? [];
|
|
@@ -115,17 +125,91 @@ function layoutToHtml(node, ctx) {
|
|
|
115
125
|
.join('');
|
|
116
126
|
return `<div data-likhari-layout-container style="display: grid; grid-template-columns: ${escapeAttr(templateColumns)}">${columns}</div>`;
|
|
117
127
|
}
|
|
118
|
-
function
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
function firstDirection(nodes) {
|
|
129
|
+
for (const node of nodes) {
|
|
130
|
+
if (node.direction === 'rtl' || node.direction === 'ltr')
|
|
131
|
+
return node.direction;
|
|
132
|
+
const inner = firstDirection(node.children ?? []);
|
|
133
|
+
if (inner)
|
|
134
|
+
return inner;
|
|
135
|
+
}
|
|
136
|
+
return 'ltr';
|
|
123
137
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
return `<
|
|
138
|
+
/** One misra line; styles are inline so the exported page keeps the editor's look. */
|
|
139
|
+
function poetryLineToHtml(line, extraStyle, ctx) {
|
|
140
|
+
const dir = line.direction === 'rtl' || line.direction === 'ltr' ? ` dir="${line.direction}"` : '';
|
|
141
|
+
const style = ['margin: 0', extraStyle].filter(Boolean).join('; ');
|
|
142
|
+
return `<p${dir} style="${escapeAttr(style)}">${inlineChildren(line, ctx)}</p>`;
|
|
143
|
+
}
|
|
144
|
+
function poetryToHtml(node, ctx) {
|
|
145
|
+
const layout = layoutOrDefault(node.layout);
|
|
146
|
+
const spacing = scaleOrDefault(node.spacing);
|
|
147
|
+
const gutter = scaleOrDefault(node.gutter);
|
|
148
|
+
const stagger = scaleOrDefault(node.stagger);
|
|
149
|
+
const width = typeof node.width === 'number' && node.width > 0 ? node.width : undefined;
|
|
150
|
+
const children = node.children ?? [];
|
|
151
|
+
const dir = firstDirection(children);
|
|
152
|
+
const gap = POETRY_COUPLET_GAP[spacing];
|
|
153
|
+
const separator = `border-top: ${POETRY_RULE}; margin-top: ${gap}; padding-top: calc(${gap} * 0.6)`;
|
|
154
|
+
let body;
|
|
155
|
+
if (layout === 'two-column') {
|
|
156
|
+
body = children
|
|
157
|
+
.map((row, r) => {
|
|
158
|
+
const items = row.children ?? [];
|
|
159
|
+
const centered = items.length === 1;
|
|
160
|
+
const templateColumns = typeof row.templateColumns === 'string' ? row.templateColumns : `repeat(${Math.max(items.length, 1)}, 1fr)`;
|
|
161
|
+
const rowStyle = [
|
|
162
|
+
'display: grid',
|
|
163
|
+
'gap: 0',
|
|
164
|
+
`grid-template-columns: ${templateColumns}`,
|
|
165
|
+
'margin: 0',
|
|
166
|
+
...(r > 0 ? [separator] : []),
|
|
167
|
+
...(centered ? [`width: ${POETRY_CENTERED_WIDTH}`, 'margin-inline: auto'] : []),
|
|
168
|
+
].join('; ');
|
|
169
|
+
const px = POETRY_GUTTER_PX[gutter];
|
|
170
|
+
const cells = items
|
|
171
|
+
.map((item, i) => {
|
|
172
|
+
const cellStyle = centered
|
|
173
|
+
? 'padding-inline: 0'
|
|
174
|
+
: i === 0
|
|
175
|
+
? `padding-inline-end: ${px}px`
|
|
176
|
+
: `border-inline-start: ${POETRY_RULE}; padding-inline-start: ${px}px`;
|
|
177
|
+
const lines = (item.children ?? []).map((line) => poetryLineToHtml(line, '', ctx)).join('');
|
|
178
|
+
return `<div data-likhari-layout-item style="${escapeAttr(cellStyle)}">${lines}</div>`;
|
|
179
|
+
})
|
|
180
|
+
.join('');
|
|
181
|
+
return `<div data-likhari-layout-container style="${escapeAttr(rowStyle)}">${cells}</div>`;
|
|
182
|
+
})
|
|
183
|
+
.join('');
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
body = children
|
|
187
|
+
.map((line, i) => {
|
|
188
|
+
const couplet = Math.floor(i / 2);
|
|
189
|
+
const parts = [];
|
|
190
|
+
if (i % 2 === 0 && couplet > 0)
|
|
191
|
+
parts.push(separator);
|
|
192
|
+
if (layout === 'staggered') {
|
|
193
|
+
parts.push(`width: ${POETRY_STAGGER_WIDTH[stagger]}`);
|
|
194
|
+
parts.push(couplet % 2 === 0 ? 'margin-inline-end: auto' : 'margin-inline-start: auto');
|
|
195
|
+
}
|
|
196
|
+
return poetryLineToHtml(line, parts.join('; '), ctx);
|
|
197
|
+
})
|
|
198
|
+
.join('');
|
|
199
|
+
}
|
|
200
|
+
const rtl = dir === 'rtl';
|
|
201
|
+
const blockStyle = [
|
|
202
|
+
'box-sizing: border-box',
|
|
203
|
+
'margin: 0 auto 1.4em auto',
|
|
204
|
+
`max-width: ${width !== undefined ? `${width}px` : '70%'}`,
|
|
205
|
+
'padding: 10px 16px',
|
|
206
|
+
'text-align: justify',
|
|
207
|
+
'text-align-last: justify',
|
|
208
|
+
`font-size: ${rtl ? '20px' : '15px'}`,
|
|
209
|
+
`line-height: ${rtl ? '2.1' : '1.6'}`,
|
|
210
|
+
].join('; ');
|
|
211
|
+
const widthAttr = width !== undefined ? ` data-likhari-poetry-width="${width}"` : '';
|
|
212
|
+
return `<div data-likhari-poetry-layout="${layout}" data-likhari-poetry-spacing="${spacing}" data-likhari-poetry-gutter="${gutter}" data-likhari-poetry-stagger="${stagger}"${widthAttr} dir="${dir}" style="${escapeAttr(blockStyle)}">${body}</div>`;
|
|
129
213
|
}
|
|
130
214
|
function imageToHtml(node, ctx) {
|
|
131
215
|
const rawSrc = String(node.src ?? '');
|
|
@@ -179,17 +263,19 @@ export function nodeToHtml(node, ctx) {
|
|
|
179
263
|
return footnoteReferenceToHtml(node);
|
|
180
264
|
case 'footnote-list':
|
|
181
265
|
return footnoteListToHtml(node, ctx);
|
|
182
|
-
case 'poetry-block':
|
|
183
|
-
return poetryToHtml(node, ctx);
|
|
184
266
|
case 'poetry-couplet':
|
|
185
|
-
return
|
|
267
|
+
return poetryToHtml(node, ctx);
|
|
186
268
|
default:
|
|
187
269
|
// Unknown node (e.g. a future feature): keep its content, drop the wrapper.
|
|
188
270
|
return inlineChildren(node, ctx);
|
|
189
271
|
}
|
|
190
272
|
}
|
|
273
|
+
/** Lets another converter (Markdown's HTML-table fallback) number footnotes the same way. */
|
|
274
|
+
export function setFootnoteOrder(blocks) {
|
|
275
|
+
footnoteOrder = collectFootnoteOrder(blocks);
|
|
276
|
+
}
|
|
191
277
|
export function serializeHtml(state, ctx) {
|
|
192
278
|
const blocks = rootChildren(state);
|
|
193
|
-
|
|
279
|
+
setFootnoteOrder(blocks);
|
|
194
280
|
return blocks.map((node) => nodeToHtml(node, ctx)).join('\n');
|
|
195
281
|
}
|
|
@@ -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;AAiUjD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,qBAAqB,CAO3F"}
|
package/dist/markdown/parse.js
CHANGED
|
@@ -2,8 +2,9 @@ import { unified } from 'unified';
|
|
|
2
2
|
import remarkParse from 'remark-parse';
|
|
3
3
|
import remarkGfm from 'remark-gfm';
|
|
4
4
|
import remarkDirective from 'remark-directive';
|
|
5
|
-
import { FORMAT_BOLD, FORMAT_CODE, FORMAT_HIGHLIGHT, FORMAT_ITALIC, FORMAT_STRIKETHROUGH, FORMAT_SUBSCRIPT, FORMAT_SUPERSCRIPT, FORMAT_UNDERLINE, elementBase, makeState, paragraph, textNode, } from '../shared/serialized.js';
|
|
6
|
-
import { safeImageSrc, safeUrl } from '../html/parse.js';
|
|
5
|
+
import { FORMAT_BOLD, FORMAT_CODE, FORMAT_HIGHLIGHT, FORMAT_ITALIC, FORMAT_STRIKETHROUGH, FORMAT_SUBSCRIPT, FORMAT_SUPERSCRIPT, FORMAT_UNDERLINE, elementBase, makeState, paragraph, rootChildren, textNode, } from '../shared/serialized.js';
|
|
6
|
+
import { parseHtml, safeImageSrc, safeUrl } from '../html/parse.js';
|
|
7
|
+
import { layoutOrDefault, scaleOrDefault } from '../shared/poetry.js';
|
|
7
8
|
const processor = unified().use(remarkParse).use(remarkGfm).use(remarkDirective);
|
|
8
9
|
const ALIGNMENTS = new Set(['left', 'right', 'center', 'justify', 'start', 'end']);
|
|
9
10
|
const DIRECTIVE_FORMATS = {
|
|
@@ -225,6 +226,9 @@ function blocks(nodes, extra = {}) {
|
|
|
225
226
|
case 'table':
|
|
226
227
|
out.push(tableBlock(node));
|
|
227
228
|
break;
|
|
229
|
+
case 'html':
|
|
230
|
+
out.push(...rootChildren(parseHtml(node.value ?? '')).filter((n) => n.type === 'table'));
|
|
231
|
+
break;
|
|
228
232
|
case 'containerDirective':
|
|
229
233
|
if (node.name === 'columns') {
|
|
230
234
|
const columns = (node.children ?? [])
|
|
@@ -239,21 +243,15 @@ function blocks(nodes, extra = {}) {
|
|
|
239
243
|
}
|
|
240
244
|
else if (node.name === 'poetry') {
|
|
241
245
|
const attrs = node.attributes ?? {};
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
children = [elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], { centered: false })];
|
|
250
|
-
}
|
|
251
|
-
out.push(elementBase('poetry-block', children, { layout, align }));
|
|
252
|
-
}
|
|
253
|
-
else if (node.name === 'couplet') {
|
|
254
|
-
const centered = (node.attributes ?? {}).centered === 'true';
|
|
246
|
+
const settings = {
|
|
247
|
+
layout: layoutOrDefault(attrs.layout),
|
|
248
|
+
spacing: scaleOrDefault(attrs.spacing),
|
|
249
|
+
gutter: scaleOrDefault(attrs.gutter),
|
|
250
|
+
stagger: scaleOrDefault(attrs.stagger),
|
|
251
|
+
...(Number(attrs.width) > 0 ? { width: Number(attrs.width) } : {}),
|
|
252
|
+
};
|
|
255
253
|
const children = blocks(node.children ?? []);
|
|
256
|
-
out.push(elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()],
|
|
254
|
+
out.push(elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], settings));
|
|
257
255
|
}
|
|
258
256
|
else {
|
|
259
257
|
// Unknown containers (including a stray "column" outside "columns")
|
|
@@ -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;
|
|
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;AA2C7D,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;AAuJD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAM9F"}
|
|
@@ -4,6 +4,8 @@ import remarkGfm from 'remark-gfm';
|
|
|
4
4
|
import remarkDirective from 'remark-directive';
|
|
5
5
|
import { FORMAT_BOLD, FORMAT_CODE, FORMAT_HIGHLIGHT, FORMAT_ITALIC, FORMAT_STRIKETHROUGH, FORMAT_SUBSCRIPT, FORMAT_SUPERSCRIPT, FORMAT_UNDERLINE, rootChildren, } from '../shared/serialized.js';
|
|
6
6
|
import { directive } from './mdast.js';
|
|
7
|
+
import { layoutOrDefault, scaleOrDefault } from '../shared/poetry.js';
|
|
8
|
+
import { nodeToHtml, setFootnoteOrder } from '../html/serialize.js';
|
|
7
9
|
const processor = unified()
|
|
8
10
|
.use(remarkGfm)
|
|
9
11
|
.use(remarkDirective)
|
|
@@ -247,17 +249,28 @@ function layoutToMd(node, ctx) {
|
|
|
247
249
|
return directive('containerDirective', 'columns', { count: String(items.length) }, columns);
|
|
248
250
|
}
|
|
249
251
|
function poetryToMd(node, ctx) {
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
+
const attrs = { layout: layoutOrDefault(node.layout) };
|
|
253
|
+
for (const key of ['spacing', 'gutter', 'stagger']) {
|
|
254
|
+
const value = scaleOrDefault(node[key]);
|
|
255
|
+
if (value !== 'normal')
|
|
256
|
+
attrs[key] = value;
|
|
257
|
+
}
|
|
258
|
+
if (typeof node.width === 'number' && node.width > 0)
|
|
259
|
+
attrs.width = String(node.width);
|
|
252
260
|
const inner = (node.children ?? []).flatMap((child) => blockToMd(child, ctx));
|
|
253
|
-
return directive('containerDirective', 'poetry',
|
|
261
|
+
return directive('containerDirective', 'poetry', attrs, inner);
|
|
254
262
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return
|
|
263
|
+
/** Markdown tables cannot express merged cells or multi-block cells, so those
|
|
264
|
+
* are written as an HTML table block, read back by the HTML importer. */
|
|
265
|
+
function needsHtmlTable(node) {
|
|
266
|
+
return (node.children ?? []).some((row) => (row.children ?? []).some((cell) => Number(cell.colSpan ?? 1) > 1 ||
|
|
267
|
+
Number(cell.rowSpan ?? 1) > 1 ||
|
|
268
|
+
(cell.children ?? []).length !== 1 ||
|
|
269
|
+
cell.children[0].type !== 'paragraph'));
|
|
259
270
|
}
|
|
260
271
|
function tableToMd(node, ctx) {
|
|
272
|
+
if (needsHtmlTable(node))
|
|
273
|
+
return { type: 'html', value: nodeToHtml(node, ctx) };
|
|
261
274
|
const rows = (node.children ?? []).map((row) => ({
|
|
262
275
|
type: 'tableRow',
|
|
263
276
|
children: (row.children ?? []).map((cell) => {
|
|
@@ -300,10 +313,8 @@ function blockToMd(node, ctx) {
|
|
|
300
313
|
return [tableToMd(node, ctx)];
|
|
301
314
|
case 'layout-container':
|
|
302
315
|
return [layoutToMd(node, ctx)];
|
|
303
|
-
case 'poetry-block':
|
|
304
|
-
return [poetryToMd(node, ctx)];
|
|
305
316
|
case 'poetry-couplet':
|
|
306
|
-
return [
|
|
317
|
+
return [poetryToMd(node, ctx)];
|
|
307
318
|
case 'footnote-list':
|
|
308
319
|
return (node.children ?? []).map((item) => ({
|
|
309
320
|
type: 'footnoteDefinition',
|
|
@@ -317,7 +328,9 @@ function blockToMd(node, ctx) {
|
|
|
317
328
|
}
|
|
318
329
|
}
|
|
319
330
|
export function serializeMarkdown(state, ctx) {
|
|
320
|
-
const
|
|
331
|
+
const blocks = rootChildren(state);
|
|
332
|
+
setFootnoteOrder(blocks);
|
|
333
|
+
const tree = { type: 'root', children: blocks.flatMap((n) => blockToMd(n, ctx)) };
|
|
321
334
|
// remark-stringify consumes mdast; our MdNode is a structural subset of it.
|
|
322
335
|
return String(processor.stringify(tree)).replace(/\n+$/, '\n').replace(/^\n+$/, '');
|
|
323
336
|
}
|
|
@@ -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;AA4FhD,eAAO,MAAM,kBAAkB,EAAE,eAsDhC,CAAC"}
|
package/dist/plain-text/index.js
CHANGED
|
@@ -28,19 +28,15 @@ function nodeToText(node, order, listItemIndex = 0) {
|
|
|
28
28
|
return `[${order.get(id) ?? '?'}]`;
|
|
29
29
|
}
|
|
30
30
|
// Linearized per the fidelity matrix ("Poetry layout variants ⚠️ linearized"):
|
|
31
|
-
// one misra per line, regardless of single/two-column
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
// first couplet being read.
|
|
37
|
-
if (node.type === 'poetry-block' || node.type === 'poetry-couplet') {
|
|
31
|
+
// one misra per line, regardless of single/two-column layout and however
|
|
32
|
+
// many couplets the block holds — in two-column layout each couplet is
|
|
33
|
+
// its own layout-container row, so each contributes its two item lines
|
|
34
|
+
// in turn rather than only the block's first couplet being read.
|
|
35
|
+
if (node.type === 'poetry-couplet') {
|
|
38
36
|
const children = withChildren.children ?? [];
|
|
39
37
|
const lines = children.flatMap((child) => child.type === 'layout-container'
|
|
40
38
|
? (child.children ?? []).map((item) => nodeToText(item, order))
|
|
41
|
-
: child
|
|
42
|
-
? (child.children ?? []).map((item) => nodeToText(item, order))
|
|
43
|
-
: [nodeToText(child, order)]);
|
|
39
|
+
: [nodeToText(child, order)]);
|
|
44
40
|
return lines.join('\n');
|
|
45
41
|
}
|
|
46
42
|
if (node.type === 'list' && withChildren.children) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Poetry block settings shared by the HTML and Markdown converters. The CSS
|
|
3
|
+
* values mirror the editor's own (packages/react/src/theme/editor.css), so an
|
|
4
|
+
* exported page looks the same outside the editor.
|
|
5
|
+
*/
|
|
6
|
+
export declare const POETRY_LAYOUTS: readonly ["single", "two-column", "staggered"];
|
|
7
|
+
export type PoetryLayout = (typeof POETRY_LAYOUTS)[number];
|
|
8
|
+
export declare const POETRY_SCALE_NAMES: readonly ["compact", "normal", "relaxed", "loose"];
|
|
9
|
+
export type PoetryScaleName = (typeof POETRY_SCALE_NAMES)[number];
|
|
10
|
+
export declare const POETRY_COUPLET_GAP: Record<PoetryScaleName, string>;
|
|
11
|
+
export declare const POETRY_GUTTER_PX: Record<PoetryScaleName, number>;
|
|
12
|
+
export declare const POETRY_STAGGER_WIDTH: Record<PoetryScaleName, string>;
|
|
13
|
+
export declare const POETRY_CENTERED_WIDTH = "60%";
|
|
14
|
+
export declare const POETRY_RULE = "1px dashed #8a8880";
|
|
15
|
+
export declare function scaleOrDefault(value: unknown): PoetryScaleName;
|
|
16
|
+
export declare function layoutOrDefault(value: unknown): PoetryLayout;
|
|
17
|
+
//# sourceMappingURL=poetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poetry.d.ts","sourceRoot":"","sources":["../../src/shared/poetry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,cAAc,gDAAiD,CAAC;AAC7E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,eAAO,MAAM,kBAAkB,oDAAqD,CAAC;AACrF,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAK9D,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAK5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAKhE,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAC3C,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAEhD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAE9D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAE5D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Poetry block settings shared by the HTML and Markdown converters. The CSS
|
|
3
|
+
* values mirror the editor's own (packages/react/src/theme/editor.css), so an
|
|
4
|
+
* exported page looks the same outside the editor.
|
|
5
|
+
*/
|
|
6
|
+
export const POETRY_LAYOUTS = ['single', 'two-column', 'staggered'];
|
|
7
|
+
export const POETRY_SCALE_NAMES = ['compact', 'normal', 'relaxed', 'loose'];
|
|
8
|
+
export const POETRY_COUPLET_GAP = {
|
|
9
|
+
compact: '0.4em',
|
|
10
|
+
normal: '1em',
|
|
11
|
+
relaxed: '1.8em',
|
|
12
|
+
loose: '2.8em',
|
|
13
|
+
};
|
|
14
|
+
export const POETRY_GUTTER_PX = {
|
|
15
|
+
compact: 8,
|
|
16
|
+
normal: 20,
|
|
17
|
+
relaxed: 32,
|
|
18
|
+
loose: 48,
|
|
19
|
+
};
|
|
20
|
+
export const POETRY_STAGGER_WIDTH = {
|
|
21
|
+
compact: '50%',
|
|
22
|
+
normal: '65%',
|
|
23
|
+
relaxed: '75%',
|
|
24
|
+
loose: '85%',
|
|
25
|
+
};
|
|
26
|
+
export const POETRY_CENTERED_WIDTH = '60%';
|
|
27
|
+
export const POETRY_RULE = '1px dashed #8a8880';
|
|
28
|
+
export function scaleOrDefault(value) {
|
|
29
|
+
return POETRY_SCALE_NAMES.find((name) => name === value) ?? 'normal';
|
|
30
|
+
}
|
|
31
|
+
export function layoutOrDefault(value) {
|
|
32
|
+
return POETRY_LAYOUTS.find((name) => name === value) ?? 'single';
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inshapardaz/likhari-converters",
|
|
3
|
-
"version": "0.1.18-dev.
|
|
3
|
+
"version": "0.1.18-dev.162",
|
|
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": {
|