@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.
@@ -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;AAiVD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,qBAAqB,CAMvF"}
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"}
@@ -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 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';
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, { layout, align });
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()], { layout, align });
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
  });
@@ -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;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"}
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"}
@@ -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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
@@ -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
- return `<${tag}${span}>${content}</${tag}>`;
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
- return `<li id="fn-${escapeAttr(id)}" data-likhari-footnote-item="${escapeAttr(id)}">${body} <a href="#fnref-${escapeAttr(id)}">↩</a></li>`;
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 === '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>`;
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
- footnoteOrder = collectFootnoteOrder(blocks);
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;AAwTjD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,qBAAqB,CAO3F"}
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"}
@@ -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 layout = attrs.layout === 'two-column' ? 'two-column' : 'single';
243
- const align = ALIGNMENTS.has(attrs.align ?? '') ? attrs.align : 'justify';
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()], { layout, align }));
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;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"}
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 layout = node.layout === 'two-column' ? 'two-column' : 'single';
251
- const align = typeof node.align === 'string' ? node.align : 'justify';
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', { layout, align }, inner);
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 tree = { type: 'root', children: rootChildren(state).flatMap((n) => blockToMd(n, ctx)) };
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.158",
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": {