@jacobbubu/md-to-lark 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import { toString } from 'hast-util-to-string';
2
2
  import { createDefaultImagePayload } from '../last/image-defaults.js';
3
3
  import { LAST_TEXTUAL_BLOCK_TYPE_SET } from '../last/textual-block-types.js';
4
+ import { parseDirectiveWidth } from './markdown/md-to-semantic-hast.js';
4
5
  const BLOCK_CONTAINER_TAGS = new Set([
5
6
  'article',
6
7
  'section',
@@ -11,11 +12,15 @@ const BLOCK_CONTAINER_TAGS = new Set([
11
12
  'footer',
12
13
  ]);
13
14
  const DEFAULT_ALIGN = 'left';
14
- function createContext() {
15
+ function createContext(options = {}) {
15
16
  return {
16
17
  blocks: {},
17
18
  blockCounter: 1,
18
19
  inlineCounter: 1,
20
+ imageSizeContext: options.imageSizeContext ?? {},
21
+ warnedImageSizeKeys: new Set(),
22
+ semanticTarget: options.semanticTarget ?? {},
23
+ ...(options.imageSizeResolver ? { imageSizeResolver: options.imageSizeResolver } : {}),
19
24
  };
20
25
  }
21
26
  function nextBlockId(ctx) {
@@ -145,9 +150,86 @@ function createDividerBlock(ctx, parentId) {
145
150
  });
146
151
  return blockId;
147
152
  }
148
- function createImageBlock(ctx, parentId, sourceUrl, alt = null) {
153
+ function warnInvalidImageDisplaySize(ctx, key, message) {
154
+ if (ctx.warnedImageSizeKeys.has(key))
155
+ return;
156
+ ctx.warnedImageSizeKeys.add(key);
157
+ console.warn(`[md-to-lark] ${message}`);
158
+ }
159
+ function buildImageSizeResolverContext(ctx, image) {
160
+ const context = {};
161
+ if (ctx.imageSizeContext.inputPath) {
162
+ context.inputPath = ctx.imageSizeContext.inputPath;
163
+ }
164
+ if (ctx.imageSizeContext.resourceBaseDir) {
165
+ context.resourceBaseDir = ctx.imageSizeContext.resourceBaseDir;
166
+ }
167
+ if (image.alt) {
168
+ context.alt = image.alt;
169
+ }
170
+ if (image.title) {
171
+ context.title = image.title;
172
+ }
173
+ return context;
174
+ }
175
+ function toPositiveRoundedWidth(value, maximum = 1000) {
176
+ if (!Number.isFinite(value) || value <= 0)
177
+ return undefined;
178
+ return Math.min(maximum, Math.max(1, Math.round(value)));
179
+ }
180
+ function applyImageDisplaySize(ctx, image, block) {
181
+ if (!ctx.imageSizeResolver || !image.sourceUrl) {
182
+ return false;
183
+ }
184
+ const size = ctx.imageSizeResolver(image.sourceUrl, buildImageSizeResolverContext(ctx, image));
185
+ if (!size) {
186
+ return false;
187
+ }
188
+ const applyAspectRatio = () => {
189
+ if (typeof size.aspectRatio === 'number' &&
190
+ Number.isFinite(size.aspectRatio) &&
191
+ size.aspectRatio > 0 &&
192
+ typeof block.payload.width === 'number') {
193
+ block.payload.height = Math.max(1, Math.round(block.payload.width / size.aspectRatio));
194
+ }
195
+ };
196
+ const hasWidthRatio = typeof size.widthRatio === 'number';
197
+ if (hasWidthRatio) {
198
+ const ratio = size.widthRatio;
199
+ if (Number.isFinite(ratio) && ratio > 0 && ratio <= 1) {
200
+ const baseWidth = block.payload.width ??
201
+ createDefaultImagePayload(block.parentId ? ctx.blocks[block.parentId]?.type : undefined).width ??
202
+ 0;
203
+ const width = toPositiveRoundedWidth(baseWidth * ratio);
204
+ if (width !== undefined) {
205
+ block.payload.width = width;
206
+ applyAspectRatio();
207
+ }
208
+ return true;
209
+ }
210
+ warnInvalidImageDisplaySize(ctx, `widthRatio:${image.sourceUrl}:${String(size.widthRatio)}`, `Ignoring invalid image widthRatio for ${image.sourceUrl}: ${String(size.widthRatio)}. Expected 0 < widthRatio <= 1.`);
211
+ }
212
+ if (typeof size.widthPx === 'number') {
213
+ const maximum = createDefaultImagePayload(block.parentId ? ctx.blocks[block.parentId]?.type : undefined).width ?? 1000;
214
+ const width = toPositiveRoundedWidth(size.widthPx, maximum);
215
+ if (width !== undefined) {
216
+ block.payload.width = width;
217
+ applyAspectRatio();
218
+ return true;
219
+ }
220
+ warnInvalidImageDisplaySize(ctx, `widthPx:${image.sourceUrl}:${String(size.widthPx)}`, `Ignoring invalid image widthPx for ${image.sourceUrl}: ${String(size.widthPx)}. Expected widthPx > 0.`);
221
+ }
222
+ return false;
223
+ }
224
+ function createImageBlock(ctx, parentId, sourceUrl, alt = null, title = null, linkHref = null, displayOverride) {
149
225
  const blockId = nextBlockId(ctx);
150
226
  const parentBlock = ctx.blocks[parentId];
227
+ const image = {
228
+ sourceUrl,
229
+ alt,
230
+ title,
231
+ linkHref,
232
+ };
151
233
  const blockBase = {
152
234
  id: blockId,
153
235
  type: 'image',
@@ -162,12 +244,90 @@ function createImageBlock(ctx, parentId, sourceUrl, alt = null) {
162
244
  if (alt) {
163
245
  selectorAttrs.alt = alt;
164
246
  }
247
+ if (title) {
248
+ selectorAttrs.title = title;
249
+ }
250
+ if (linkHref) {
251
+ selectorAttrs.linkHref = linkHref;
252
+ }
165
253
  if (Object.keys(selectorAttrs).length > 0) {
166
254
  blockBase.selector = { attrs: selectorAttrs };
167
255
  }
256
+ const resolverMatched = applyImageDisplaySize(ctx, image, blockBase);
257
+ const shouldApplyOverride = Boolean(displayOverride && (!displayOverride.fallback || !resolverMatched));
258
+ if (shouldApplyOverride && displayOverride?.widthRatio !== undefined) {
259
+ const ratio = displayOverride.widthRatio;
260
+ if (Number.isFinite(ratio) && ratio > 0 && ratio <= 1) {
261
+ const baseWidth = createDefaultImagePayload(parentBlock?.type).width ?? blockBase.payload.width ?? 0;
262
+ blockBase.payload.width = Math.max(1, Math.round(baseWidth * ratio));
263
+ }
264
+ }
265
+ else if (shouldApplyOverride && displayOverride?.widthPx !== undefined && displayOverride.widthPx > 0) {
266
+ const maximum = createDefaultImagePayload(parentBlock?.type).width ?? 1000;
267
+ blockBase.payload.width = Math.min(maximum, Math.max(1, Math.round(displayOverride.widthPx)));
268
+ }
269
+ if (shouldApplyOverride && displayOverride?.aspectRatio && blockBase.payload.width) {
270
+ blockBase.payload.height = Math.max(1, Math.round(blockBase.payload.width / displayOverride.aspectRatio));
271
+ }
272
+ if (displayOverride?.align)
273
+ blockBase.payload.align = displayOverride.align;
168
274
  addBlock(ctx, blockBase);
169
275
  return blockId;
170
276
  }
277
+ function attachSemanticMeta(ctx, blockId, role, semanticId) {
278
+ const block = ctx.blocks[blockId];
279
+ if (!block)
280
+ return;
281
+ block.selector = {
282
+ ...(block.selector ?? {}),
283
+ labels: [...(block.selector?.labels ?? []), role],
284
+ attrs: {
285
+ ...(block.selector?.attrs ?? {}),
286
+ semanticRole: role,
287
+ ...(semanticId ? { semanticId } : {}),
288
+ },
289
+ };
290
+ }
291
+ function normalizeCalloutColor(value) {
292
+ return value?.trim().toLowerCase().replaceAll('-', '_') || undefined;
293
+ }
294
+ function calloutStyleForElement(element, footnote) {
295
+ const target = footnote ? undefined : getStringProp(element, 'type');
296
+ const defaults = {
297
+ note: { backgroundColor: 'light_gray', borderColor: 'gray' },
298
+ info: { backgroundColor: 'light_blue', borderColor: 'blue' },
299
+ tip: { backgroundColor: 'light_green', borderColor: 'green' },
300
+ warning: { backgroundColor: 'light_yellow', borderColor: 'yellow' },
301
+ danger: { backgroundColor: 'light_red', borderColor: 'red' },
302
+ important: { backgroundColor: 'light_purple', borderColor: 'purple' },
303
+ };
304
+ const style = defaults[target ?? 'note'] ?? defaults.note;
305
+ return {
306
+ backgroundColor: style.backgroundColor,
307
+ borderColor: style.borderColor,
308
+ };
309
+ }
310
+ function createCalloutBlock(ctx, parentId, element, footnote) {
311
+ const blockId = nextBlockId(ctx);
312
+ const base = calloutStyleForElement(element, footnote);
313
+ const footnoteTarget = ctx.semanticTarget.footnotes;
314
+ const backgroundColor = footnote ? normalizeCalloutColor(footnoteTarget?.background_color ?? null) : undefined;
315
+ const borderColor = footnote ? normalizeCalloutColor(footnoteTarget?.border_color ?? null) : undefined;
316
+ addBlock(ctx, {
317
+ id: blockId,
318
+ type: 'callout',
319
+ parentId,
320
+ children: [],
321
+ payload: {
322
+ ...base,
323
+ ...(backgroundColor ? { backgroundColor: backgroundColor } : {}),
324
+ ...(borderColor ? { borderColor: borderColor } : {}),
325
+ ...(footnoteTarget?.icon ? { emojiId: footnoteTarget.icon } : {}),
326
+ },
327
+ });
328
+ attachSemanticMeta(ctx, blockId, footnote ? 'footnote' : 'callout', getStringProp(element, 'semanticId') ?? undefined);
329
+ return blockId;
330
+ }
171
331
  function createIframeBlock(ctx, parentId, url, iframeType) {
172
332
  const blockId = nextBlockId(ctx);
173
333
  addBlock(ctx, {
@@ -351,7 +511,7 @@ function parseInlineNodes(ctx, nodes, marks = createDefaultMarks()) {
351
511
  if (node.tagName === 'u') {
352
512
  nextMarks.underline = true;
353
513
  }
354
- if (node.tagName === 'a') {
514
+ if (node.tagName === 'a' || node.tagName === 'm2l-footnote-reference') {
355
515
  const href = getStringProp(node, 'href');
356
516
  nextMarks.link = href ? { url: href } : null;
357
517
  }
@@ -370,17 +530,21 @@ function extractImageSourceFromElement(element) {
370
530
  return {
371
531
  sourceUrl: getStringProp(element, 'src'),
372
532
  alt: getStringProp(element, 'alt'),
533
+ title: getStringProp(element, 'title'),
534
+ linkHref: null,
373
535
  };
374
536
  }
375
537
  if (element.tagName !== 'a')
376
538
  return null;
539
+ const href = getStringProp(element, 'href');
377
540
  const meaningfulChildren = getMeaningfulChildren(getChildren(element));
378
541
  if (meaningfulChildren.length !== 1)
379
542
  return null;
380
543
  const only = meaningfulChildren[0];
381
544
  if (!only || !isElement(only))
382
545
  return null;
383
- return extractImageSourceFromElement(only);
546
+ const imageSource = extractImageSourceFromElement(only);
547
+ return imageSource ? { ...imageSource, linkHref: href } : null;
384
548
  }
385
549
  function findStandaloneImageInParagraph(paragraph) {
386
550
  const meaningfulChildren = getMeaningfulChildren(getChildren(paragraph));
@@ -692,7 +856,7 @@ function convertTable(ctx, table, parentId) {
692
856
  const cellBlock = ctx.blocks[cellId];
693
857
  const richItem = cell ? findStandaloneRichItemInTableCell(cell) : null;
694
858
  if (cellBlock?.type === 'table_cell' && richItem?.kind === 'image') {
695
- const imageId = createImageBlock(ctx, cellId, richItem.sourceUrl, richItem.alt);
859
+ const imageId = createImageBlock(ctx, cellId, richItem.sourceUrl, richItem.alt, richItem.title, richItem.linkHref);
696
860
  cellBlock.children = [imageId];
697
861
  cells.push(cellId);
698
862
  continue;
@@ -764,7 +928,9 @@ function hasNonWhitespaceInline(inlines) {
764
928
  function convertParagraph(ctx, paragraph, parentId) {
765
929
  const standaloneImage = findStandaloneImageInParagraph(paragraph);
766
930
  if (standaloneImage?.sourceUrl) {
767
- return [createImageBlock(ctx, parentId, standaloneImage.sourceUrl, standaloneImage.alt)];
931
+ return [
932
+ createImageBlock(ctx, parentId, standaloneImage.sourceUrl, standaloneImage.alt, standaloneImage.title, standaloneImage.linkHref),
933
+ ];
768
934
  }
769
935
  const standaloneIframe = findStandaloneIframePayloadInParagraph(paragraph);
770
936
  if (standaloneIframe) {
@@ -785,7 +951,7 @@ function convertParagraph(ctx, paragraph, parentId) {
785
951
  const image = isElement(child) ? extractImageSourceFromElement(child) : null;
786
952
  if (image?.sourceUrl) {
787
953
  flushText();
788
- ids.push(createImageBlock(ctx, parentId, image.sourceUrl, image.alt));
954
+ ids.push(createImageBlock(ctx, parentId, image.sourceUrl, image.alt, image.title, image.linkHref));
789
955
  continue;
790
956
  }
791
957
  pendingInlineNodes.push(child);
@@ -796,6 +962,189 @@ function convertParagraph(ctx, paragraph, parentId) {
796
962
  }
797
963
  return [createTextualBlock(ctx, 'text', parentId, parseInlineNodes(ctx, getChildren(paragraph)))];
798
964
  }
965
+ function collectSemanticImages(element) {
966
+ const images = [];
967
+ const visit = (node) => {
968
+ if (!isElement(node))
969
+ return;
970
+ const image = extractImageSourceFromElement(node);
971
+ if (image?.sourceUrl) {
972
+ images.push(image);
973
+ return;
974
+ }
975
+ for (const child of getChildren(node))
976
+ visit(child);
977
+ };
978
+ for (const child of getChildren(element))
979
+ visit(child);
980
+ return images;
981
+ }
982
+ function findDirectSemanticChild(element, tagName) {
983
+ return getChildren(element).find((child) => isElement(child) && child.tagName === tagName);
984
+ }
985
+ function createSemanticTextBlock(ctx, element, parentId, role, semanticId) {
986
+ const blockId = createTextualBlock(ctx, 'text', parentId, parseInlineNodes(ctx, getChildren(element)));
987
+ attachSemanticMeta(ctx, blockId, role, semanticId);
988
+ return blockId;
989
+ }
990
+ function parseSemanticAlign(element) {
991
+ return parseAlignValue(getStringProp(element, 'align'));
992
+ }
993
+ function semanticAnnotationIds(ctx, wrapper, parentId, semanticId) {
994
+ const result = {};
995
+ for (const role of ['caption', 'note', 'source']) {
996
+ const element = findDirectSemanticChild(wrapper, `m2l-${role}`);
997
+ if (element)
998
+ result[role] = createSemanticTextBlock(ctx, element, parentId, role, semanticId);
999
+ }
1000
+ return result;
1001
+ }
1002
+ function convertSemanticFigure(ctx, figure, parentId) {
1003
+ const semanticId = getStringProp(figure, 'semanticId') ?? undefined;
1004
+ const display = parseDirectiveWidth(getStringProp(figure, 'width') ?? '');
1005
+ const fallbackRatio = ctx.semanticTarget.figures?.default_width_ratio;
1006
+ const effectiveDisplay = display ??
1007
+ (typeof fallbackRatio === 'number' && fallbackRatio > 0 && fallbackRatio <= 1
1008
+ ? { widthRatio: fallbackRatio, fallback: true }
1009
+ : undefined);
1010
+ const align = parseSemanticAlign(figure);
1011
+ const imageIds = collectSemanticImages(figure).map((image) => {
1012
+ const imageId = createImageBlock(ctx, parentId, image.sourceUrl, image.alt, image.title, image.linkHref, {
1013
+ ...(effectiveDisplay ?? {}),
1014
+ ...(align ? { align } : {}),
1015
+ });
1016
+ attachSemanticMeta(ctx, imageId, 'figure-image', semanticId);
1017
+ return imageId;
1018
+ });
1019
+ const annotations = semanticAnnotationIds(ctx, figure, parentId, semanticId);
1020
+ const target = ctx.semanticTarget.figures;
1021
+ const ids = [];
1022
+ if (target?.caption_position === 'above' && annotations.caption)
1023
+ ids.push(annotations.caption);
1024
+ if (target?.source_position === 'above' && annotations.source)
1025
+ ids.push(annotations.source);
1026
+ if (target?.note_position === 'above' && annotations.note)
1027
+ ids.push(annotations.note);
1028
+ ids.push(...imageIds);
1029
+ if (target?.caption_position !== 'above' && annotations.caption)
1030
+ ids.push(annotations.caption);
1031
+ if (target?.source_position === 'below-caption' && annotations.source)
1032
+ ids.push(annotations.source);
1033
+ if (target?.note_position !== 'above' && annotations.note)
1034
+ ids.push(annotations.note);
1035
+ if (target?.source_position !== 'above' && target?.source_position !== 'below-caption' && annotations.source) {
1036
+ ids.push(annotations.source);
1037
+ }
1038
+ return ids;
1039
+ }
1040
+ function convertSemanticTable(ctx, wrapper, parentId) {
1041
+ const semanticId = getStringProp(wrapper, 'semanticId') ?? undefined;
1042
+ const table = getChildren(wrapper).find((child) => isElement(child) && child.tagName === 'table');
1043
+ const annotations = semanticAnnotationIds(ctx, wrapper, parentId, semanticId);
1044
+ const tableIds = table ? convertTable(ctx, table, parentId) : [];
1045
+ for (const tableId of tableIds)
1046
+ attachSemanticMeta(ctx, tableId, 'semantic-table', semanticId);
1047
+ const target = ctx.semanticTarget.tables;
1048
+ const ids = [];
1049
+ if (target?.caption_position !== 'below' && annotations.caption)
1050
+ ids.push(annotations.caption);
1051
+ if (target?.source_position === 'above' && annotations.source)
1052
+ ids.push(annotations.source);
1053
+ ids.push(...tableIds);
1054
+ if (target?.caption_position === 'below' && annotations.caption)
1055
+ ids.push(annotations.caption);
1056
+ if (annotations.note)
1057
+ ids.push(annotations.note);
1058
+ if (target?.source_position !== 'above' && annotations.source)
1059
+ ids.push(annotations.source);
1060
+ return ids;
1061
+ }
1062
+ function convertSemanticEquation(ctx, wrapper, parentId) {
1063
+ const semanticId = getStringProp(wrapper, 'semanticId') ?? undefined;
1064
+ const ids = [];
1065
+ const equationNumber = getStringProp(wrapper, 'equationNumber');
1066
+ let numberAttached = false;
1067
+ for (const child of getChildren(wrapper)) {
1068
+ if (isElement(child) && ['m2l-caption', 'm2l-note', 'm2l-source'].includes(child.tagName))
1069
+ continue;
1070
+ for (const blockId of convertBlock(ctx, child, parentId)) {
1071
+ attachSemanticMeta(ctx, blockId, 'equation', semanticId);
1072
+ const block = ctx.blocks[blockId];
1073
+ if (equationNumber && !numberAttached && block && isTextualBlockNode(block)) {
1074
+ block.payload.inlines.push({
1075
+ id: nextInlineId(ctx),
1076
+ kind: 'text_run',
1077
+ marks: createDefaultMarks(),
1078
+ text: ` (${equationNumber})`,
1079
+ });
1080
+ numberAttached = true;
1081
+ }
1082
+ ids.push(blockId);
1083
+ }
1084
+ }
1085
+ const annotations = semanticAnnotationIds(ctx, wrapper, parentId, semanticId);
1086
+ if (annotations.caption)
1087
+ ids.push(annotations.caption);
1088
+ if (annotations.note)
1089
+ ids.push(annotations.note);
1090
+ if (annotations.source)
1091
+ ids.push(annotations.source);
1092
+ return ids;
1093
+ }
1094
+ function prependFootnoteLabel(ctx, blockId, label) {
1095
+ const block = ctx.blocks[blockId];
1096
+ if (!block || !isTextualBlockNode(block))
1097
+ return;
1098
+ block.payload.inlines.unshift({
1099
+ id: nextInlineId(ctx),
1100
+ kind: 'text_run',
1101
+ marks: { ...createDefaultMarks(), bold: true },
1102
+ text: `[${label}] `,
1103
+ });
1104
+ }
1105
+ function convertSemanticCallout(ctx, element, parentId, footnote) {
1106
+ const output = [];
1107
+ let calloutId = createCalloutBlock(ctx, parentId, element, footnote);
1108
+ output.push(calloutId);
1109
+ let firstText = true;
1110
+ const appendConverted = (blockId) => {
1111
+ const block = ctx.blocks[blockId];
1112
+ if (!block)
1113
+ return;
1114
+ if (block.type === 'image' || block.type === 'table' || block.type === 'file' || block.type === 'board') {
1115
+ block.parentId = parentId;
1116
+ attachSemanticMeta(ctx, blockId, footnote ? 'footnote-media-sibling' : 'callout-media-sibling', getStringProp(element, 'semanticId') ?? undefined);
1117
+ output.push(blockId);
1118
+ calloutId = createCalloutBlock(ctx, parentId, element, footnote);
1119
+ output.push(calloutId);
1120
+ return;
1121
+ }
1122
+ const callout = ctx.blocks[calloutId];
1123
+ if (!callout || callout.type !== 'callout')
1124
+ return;
1125
+ block.parentId = calloutId;
1126
+ appendChild(callout, blockId);
1127
+ if (footnote && firstText && isTextualBlockNode(block)) {
1128
+ prependFootnoteLabel(ctx, blockId, getStringProp(element, 'semanticId') ?? '?');
1129
+ firstText = false;
1130
+ }
1131
+ };
1132
+ for (const child of getChildren(element)) {
1133
+ const current = ctx.blocks[calloutId];
1134
+ if (!current || current.type !== 'callout')
1135
+ continue;
1136
+ for (const blockId of convertBlock(ctx, child, calloutId))
1137
+ appendConverted(blockId);
1138
+ }
1139
+ for (const blockId of [...output]) {
1140
+ const block = ctx.blocks[blockId];
1141
+ if (block?.type === 'callout' && block.children.length === 0) {
1142
+ delete ctx.blocks[blockId];
1143
+ output.splice(output.indexOf(blockId), 1);
1144
+ }
1145
+ }
1146
+ return output;
1147
+ }
799
1148
  function convertBlock(ctx, node, parentId) {
800
1149
  if (isWhitespaceTextNode(node)) {
801
1150
  return [];
@@ -842,8 +1191,25 @@ function convertBlock(ctx, node, parentId) {
842
1191
  return [createDividerBlock(ctx, parentId)];
843
1192
  case 'table':
844
1193
  return convertTable(ctx, node, parentId);
1194
+ case 'm2l-figure':
1195
+ return convertSemanticFigure(ctx, node, parentId);
1196
+ case 'm2l-table':
1197
+ return convertSemanticTable(ctx, node, parentId);
1198
+ case 'm2l-equation':
1199
+ return convertSemanticEquation(ctx, node, parentId);
1200
+ case 'm2l-callout':
1201
+ return convertSemanticCallout(ctx, node, parentId, false);
1202
+ case 'm2l-footnote':
1203
+ return convertSemanticCallout(ctx, node, parentId, true);
1204
+ case 'm2l-unknown-directive':
1205
+ case 'm2l-caption':
1206
+ case 'm2l-note':
1207
+ case 'm2l-source':
1208
+ return convertUnknownElement(ctx, node, parentId);
845
1209
  case 'img':
846
- return [createImageBlock(ctx, parentId, getStringProp(node, 'src'), getStringProp(node, 'alt'))];
1210
+ return [
1211
+ createImageBlock(ctx, parentId, getStringProp(node, 'src'), getStringProp(node, 'alt'), getStringProp(node, 'title')),
1212
+ ];
847
1213
  case 'br':
848
1214
  return [
849
1215
  createTextualBlock(ctx, 'text', parentId, [
@@ -956,7 +1322,7 @@ function deepCloneBlock(value) {
956
1322
  return JSON.parse(JSON.stringify(value));
957
1323
  }
958
1324
  export function hastToLAST(hast, options) {
959
- const ctx = createContext();
1325
+ const ctx = createContext(options);
960
1326
  const mode = options?.mode ?? 'fragment';
961
1327
  const rootId = createTextualBlock(ctx, 'page', null, []);
962
1328
  const root = ctx.blocks[rootId];
@@ -1,3 +1,4 @@
1
1
  export { markdownToHast } from './markdown/md-to-hast.js';
2
+ export { markdownToSemanticHast, parseDirectiveWidth } from './markdown/md-to-semantic-hast.js';
2
3
  export { prepareMarkdownBeforePublish } from './markdown/prepare-markdown.js';
3
4
  export { hastToLAST } from './hast-to-last.js';