@inshapardaz/likhari-converters 0.1.18-dev.158 → 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 +13 -8
- package/dist/html/serialize.d.ts +2 -0
- package/dist/html/serialize.d.ts.map +1 -1
- package/dist/html/serialize.js +103 -9
- package/dist/markdown/parse.d.ts.map +1 -1
- package/dist/markdown/parse.js +14 -5
- package/dist/markdown/serialize.d.ts.map +1 -1
- package/dist/markdown/serialize.js +24 -4
- 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,13 +252,15 @@ 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']);
|
|
255
|
-
const POETRY_ALIGNS = new Set(['justify', 'left', 'right', 'start']);
|
|
256
255
|
function convertPoetry(el) {
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
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
|
+
};
|
|
261
264
|
if (layout === 'two-column') {
|
|
262
265
|
// One LayoutContainerNode row per couplet — grab all of them, not just
|
|
263
266
|
// the first, or every couplet past the first silently disappears.
|
|
@@ -269,10 +272,10 @@ function convertPoetry(el) {
|
|
|
269
272
|
templateColumns: 'repeat(2, 1fr)',
|
|
270
273
|
}),
|
|
271
274
|
];
|
|
272
|
-
return elementBase('poetry-couplet', containers,
|
|
275
|
+
return elementBase('poetry-couplet', containers, settings);
|
|
273
276
|
}
|
|
274
277
|
const children = flowBlocks(el);
|
|
275
|
-
return elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()],
|
|
278
|
+
return elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], settings);
|
|
276
279
|
}
|
|
277
280
|
function convertFootnoteList(el) {
|
|
278
281
|
const items = [];
|
|
@@ -289,6 +292,8 @@ function convertFootnoteList(el) {
|
|
|
289
292
|
Array.from(clone.querySelectorAll('a'))
|
|
290
293
|
.filter((a) => a.getAttribute('href') === `#fnref-${footnoteId}`)
|
|
291
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());
|
|
292
297
|
const children = flowBlocks(clone);
|
|
293
298
|
items.push(elementBase('footnote-item', children.length > 0 ? children : [paragraph()], { footnoteId }));
|
|
294
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,11 +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
|
}
|
|
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';
|
|
137
|
+
}
|
|
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
|
+
}
|
|
118
144
|
function poetryToHtml(node, ctx) {
|
|
119
|
-
const layout = node.layout
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
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>`;
|
|
123
213
|
}
|
|
124
214
|
function imageToHtml(node, ctx) {
|
|
125
215
|
const rawSrc = String(node.src ?? '');
|
|
@@ -180,8 +270,12 @@ export function nodeToHtml(node, ctx) {
|
|
|
180
270
|
return inlineChildren(node, ctx);
|
|
181
271
|
}
|
|
182
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
|
+
}
|
|
183
277
|
export function serializeHtml(state, ctx) {
|
|
184
278
|
const blocks = rootChildren(state);
|
|
185
|
-
|
|
279
|
+
setFootnoteOrder(blocks);
|
|
186
280
|
return blocks.map((node) => nodeToHtml(node, ctx)).join('\n');
|
|
187
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,10 +243,15 @@ function blocks(nodes, extra = {}) {
|
|
|
239
243
|
}
|
|
240
244
|
else if (node.name === 'poetry') {
|
|
241
245
|
const attrs = node.attributes ?? {};
|
|
242
|
-
const
|
|
243
|
-
|
|
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
|
+
};
|
|
244
253
|
const children = blocks(node.children ?? []);
|
|
245
|
-
out.push(elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()],
|
|
254
|
+
out.push(elementBase('poetry-couplet', children.length > 0 ? children : [paragraph(), paragraph()], settings));
|
|
246
255
|
}
|
|
247
256
|
else {
|
|
248
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,12 +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);
|
|
262
|
+
}
|
|
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'));
|
|
254
270
|
}
|
|
255
271
|
function tableToMd(node, ctx) {
|
|
272
|
+
if (needsHtmlTable(node))
|
|
273
|
+
return { type: 'html', value: nodeToHtml(node, ctx) };
|
|
256
274
|
const rows = (node.children ?? []).map((row) => ({
|
|
257
275
|
type: 'tableRow',
|
|
258
276
|
children: (row.children ?? []).map((cell) => {
|
|
@@ -310,7 +328,9 @@ function blockToMd(node, ctx) {
|
|
|
310
328
|
}
|
|
311
329
|
}
|
|
312
330
|
export function serializeMarkdown(state, ctx) {
|
|
313
|
-
const
|
|
331
|
+
const blocks = rootChildren(state);
|
|
332
|
+
setFootnoteOrder(blocks);
|
|
333
|
+
const tree = { type: 'root', children: blocks.flatMap((n) => blockToMd(n, ctx)) };
|
|
314
334
|
// remark-stringify consumes mdast; our MdNode is a structural subset of it.
|
|
315
335
|
return String(processor.stringify(tree)).replace(/\n+$/, '\n').replace(/^\n+$/, '');
|
|
316
336
|
}
|
|
@@ -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": {
|