@lofcz/platejs-markdown 52.3.8 → 52.3.9

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/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as platejs2 from "platejs";
2
2
  import { Descendant, NodeKey, Nullable, OmitFirst, PluginConfig, SlateEditor, TElement, TListElement, TNodeMap, TText, Value } from "platejs";
3
3
  import { MdxJsxFlowElement, MdxJsxFlowElement as MdMdxJsxFlowElement, MdxJsxTextElement, MdxJsxTextElement as MdMdxJsxTextElement } from "mdast-util-mdx";
4
- import { Options } from "remark-stringify";
5
4
  import { Plugin } from "unified";
5
+ import { Options } from "remark-stringify";
6
6
  import { Token } from "marked";
7
7
  import baseRemarkMdx from "remark-mdx";
8
8
  import { Blockquote as MdBlockquote, Break as MdBreak, Code as MdCode, Content as MdContent, Definition as MdDefinition, Delete as MdDelete, Emphasis as MdEmphasis, FootnoteDefinition as MdFootnoteDefinition, FootnoteReference as MdFootnoteReference, Heading as MdHeading, Html as MdHtml, Image as MdImage, ImageReference as MdImageReference, InlineCode as MdInlineCode, Link as MdLink, LinkReference as MdLinkReference, List as MdList, ListItem as MdListItem, Paragraph as MdParagraph, PhrasingContent as MDPhrasingContent, Root, Root as MdRoot, RootContent as MdRootContent, Strong as MdStrong, Table as MdTable, TableCell as MdTableCell, TableRow as MdTableRow, Text as MdText, ThematicBreak as MdThematicBreak, Yaml as MdYaml } from "mdast";
@@ -40,9 +40,6 @@ declare const convertTextsSerialize: (slateTexts: readonly TText[], options: Ser
40
40
  //#region src/lib/serializer/listToMdastTree.d.ts
41
41
  declare function listToMdastTree(nodes: TListElement[], options: SerializeMdOptions, isBlock?: boolean): any;
42
42
  //#endregion
43
- //#region src/lib/serializer/serializeInlineMd.d.ts
44
- declare const serializeInlineMd: (editor: SlateEditor, options?: SerializeMdOptions) => string;
45
- //#endregion
46
43
  //#region src/lib/serializer/serializeMdWithSourceMap.d.ts
47
44
  type SelectionPath = number[];
48
45
  type MarkdownSourceMapKind = 'block' | 'blockquote' | 'code_block' | 'heading' | 'list_item' | 'media' | 'paragraph' | 'table_cell';
@@ -58,11 +55,36 @@ type MarkdownSourceMapSegment = {
58
55
  text: string;
59
56
  };
60
57
  type SerializeMdSourceMapResult = {
58
+ /** All segments including container-level (parent MDX) segments, sorted by path. */
59
+ allSegments: MarkdownSourceMapSegment[];
61
60
  markdown: string;
61
+ /** Leaf-level segments only (most precise per path). */
62
62
  segments: MarkdownSourceMapSegment[];
63
63
  };
64
64
  declare const serializeMdWithSourceMap: (editor: SlateEditor, options?: Omit<SerializeMdOptions, "editor">) => SerializeMdSourceMapResult;
65
65
  //#endregion
66
+ //#region src/lib/serializer/resolveSelectionLines.d.ts
67
+ type SelectionPoint = {
68
+ offset: number;
69
+ path: number[];
70
+ };
71
+ type SelectionRange = {
72
+ anchor: SelectionPoint;
73
+ focus: SelectionPoint;
74
+ };
75
+ type ResolveSelectionByPathResult = {
76
+ containsMdx: boolean;
77
+ endLine: number;
78
+ extractedMarkdown: string;
79
+ markdown: string;
80
+ segments: MarkdownSourceMapSegment[];
81
+ startLine: number;
82
+ };
83
+ declare const resolveSelectionByPath: (editor: SlateEditor, selection: SelectionRange, options?: Omit<SerializeMdOptions, "editor">) => ResolveSelectionByPathResult;
84
+ //#endregion
85
+ //#region src/lib/serializer/serializeInlineMd.d.ts
86
+ declare const serializeInlineMd: (editor: SlateEditor, options?: SerializeMdOptions) => string;
87
+ //#endregion
66
88
  //#region src/lib/serializer/wrapWithBlockId.d.ts
67
89
  /**
68
90
  * Wraps an mdast node with a block element containing an ID attribute. Used for
@@ -514,4 +536,4 @@ declare const tagRemarkPlugin: (pluginFn: any, tag: string) => {
514
536
  };
515
537
  declare const getRemarkPluginsWithoutMdx: (plugins: Plugin[]) => Plugin[];
516
538
  //#endregion
517
- export { AllowNodeConfig, DeserializeMdOptions, type MDPhrasingContent, MarkdownConfig, MarkdownPlugin, MarkdownSourceMapKind, MarkdownSourceMapSegment, type MdBlockquote, type MdBreak, type MdCode, type MdContent, MdDecoration, type MdDefinition, type MdDelete, type MdEmphasis, type MdFootnoteDefinition, type MdFootnoteReference, type MdHeading, type MdHtml, type MdImage, type MdImageReference, type MdInlineCode, type MdInlineMath, type MdLink, type MdLinkReference, type MdList, type MdListItem, MdMark, type MdMath, type MdMdxJsxFlowElement, type MdMdxJsxTextElement, MdNodeParser, type MdParagraph, type MdRoot, type MdRootContent, MdRules, type MdStrong, type MdTable, type MdTableCell, type MdTableRow, type MdText, type MdThematicBreak, MdType, type MdYaml, MentionNode, ParseMarkdownBlocksOptions, PlateType, REMARK_MDX_TAG, SerializeMdOptions, SerializeMdSourceMapResult, StrictPlateType, basicMarkdownMarks, buildMdastNode, buildRules, buildSlateNode, columnRules, convertChildrenDeserialize, convertNodesDeserialize, convertNodesSerialize, convertTextsDeserialize, convertTextsSerialize, customMdxDeserialize, defaultRules, deserializeInlineMd, deserializeMd, fontRules, getCustomMark, getDeserializerByKey, getMergedOptionsDeserialize, getMergedOptionsSerialize, getRemarkPluginsWithoutMdx, getSerializerByKey, getStyleValue, htmlToJsx, listToMdastTree, markdownToAstProcessor, markdownToSlateNodes, markdownToSlateNodesSafely, mdastToPlate, mdastToSlate, mediaRules, parseAttributes, parseMarkdownBlocks, plateToMdast, propsToAttributes, remarkMdx, remarkMention, serializeInlineMd, serializeMd, serializeMdWithSourceMap, splitIncompleteMdx, stripMarkdown, stripMarkdownBlocks, stripMarkdownInline, tagRemarkPlugin, type unistLib, unreachable, wrapWithBlockId };
539
+ export { AllowNodeConfig, DeserializeMdOptions, type MDPhrasingContent, MarkdownConfig, MarkdownPlugin, MarkdownSourceMapKind, MarkdownSourceMapSegment, type MdBlockquote, type MdBreak, type MdCode, type MdContent, MdDecoration, type MdDefinition, type MdDelete, type MdEmphasis, type MdFootnoteDefinition, type MdFootnoteReference, type MdHeading, type MdHtml, type MdImage, type MdImageReference, type MdInlineCode, type MdInlineMath, type MdLink, type MdLinkReference, type MdList, type MdListItem, MdMark, type MdMath, type MdMdxJsxFlowElement, type MdMdxJsxTextElement, MdNodeParser, type MdParagraph, type MdRoot, type MdRootContent, MdRules, type MdStrong, type MdTable, type MdTableCell, type MdTableRow, type MdText, type MdThematicBreak, MdType, type MdYaml, MentionNode, ParseMarkdownBlocksOptions, PlateType, REMARK_MDX_TAG, ResolveSelectionByPathResult, SelectionRange, SerializeMdOptions, SerializeMdSourceMapResult, StrictPlateType, basicMarkdownMarks, buildMdastNode, buildRules, buildSlateNode, columnRules, convertChildrenDeserialize, convertNodesDeserialize, convertNodesSerialize, convertTextsDeserialize, convertTextsSerialize, customMdxDeserialize, defaultRules, deserializeInlineMd, deserializeMd, fontRules, getCustomMark, getDeserializerByKey, getMergedOptionsDeserialize, getMergedOptionsSerialize, getRemarkPluginsWithoutMdx, getSerializerByKey, getStyleValue, htmlToJsx, listToMdastTree, markdownToAstProcessor, markdownToSlateNodes, markdownToSlateNodesSafely, mdastToPlate, mdastToSlate, mediaRules, parseAttributes, parseMarkdownBlocks, plateToMdast, propsToAttributes, remarkMdx, remarkMention, resolveSelectionByPath, serializeInlineMd, serializeMd, serializeMdWithSourceMap, splitIncompleteMdx, stripMarkdown, stripMarkdownBlocks, stripMarkdownInline, tagRemarkPlugin, type unistLib, unreachable, wrapWithBlockId };
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ElementApi, KEYS, TextApi, bindFirst, createTSlatePlugin, getPluginKey, getPluginType, isUrl } from "platejs";
2
2
  import "mdast-util-mdx";
3
3
  import kebabCase from "lodash/kebabCase.js";
4
- import remarkStringify from "remark-stringify";
5
- import { unified } from "unified";
6
4
  import { defaultHandlers, toMarkdown } from "mdast-util-to-markdown";
5
+ import { unified } from "unified";
6
+ import remarkStringify from "remark-stringify";
7
7
  import remarkParse from "remark-parse";
8
8
  import { marked } from "marked";
9
9
  import baseRemarkMdx from "remark-mdx";
@@ -655,45 +655,6 @@ const shouldIncludeNode$2 = (node, options) => {
655
655
  return true;
656
656
  };
657
657
 
658
- //#endregion
659
- //#region src/lib/serializer/serializeInlineMd.ts
660
- const serializeInlineMd = (editor, options) => {
661
- const mergedOptions = getMergedOptionsSerialize(editor, options);
662
- const toRemarkProcessor = unified().use(mergedOptions.remarkPlugins ?? []).use(remarkStringify, {
663
- emphasis: "_",
664
- ...mergedOptions?.remarkStringifyOptions
665
- });
666
- if (options?.value?.length === 0) return "";
667
- const convertedTexts = convertTextsSerialize(mergedOptions.value, { editor });
668
- return toRemarkProcessor.stringify({
669
- children: convertedTexts,
670
- type: "root"
671
- });
672
- };
673
-
674
- //#endregion
675
- //#region src/lib/serializer/serializeMd.ts
676
- /** Serialize the editor value to Markdown. */
677
- const serializeMd = (editor, options) => {
678
- const mergedOptions = getMergedOptionsSerialize(editor, options);
679
- const { remarkPlugins, value } = mergedOptions;
680
- const toRemarkProcessor = unified().use(remarkPlugins ?? []).use(remarkStringify, {
681
- emphasis: "_",
682
- ...mergedOptions?.remarkStringifyOptions
683
- });
684
- const mdast = slateToMdast({
685
- children: value,
686
- options: mergedOptions
687
- });
688
- return toRemarkProcessor.stringify(mdast);
689
- };
690
- const slateToMdast = ({ children, options }) => {
691
- return {
692
- children: convertNodesSerialize(children, options, true),
693
- type: "root"
694
- };
695
- };
696
-
697
658
  //#endregion
698
659
  //#region src/lib/serializer/serializeMdWithSourceMap.ts
699
660
  const collectSlateText = (node) => {
@@ -770,7 +731,6 @@ const buildSource = (node, editor) => {
770
731
  const path = Array.isArray(node.__sourceMapPath) ? node.__sourceMapPath : null;
771
732
  if (!path) return null;
772
733
  const text = collectSlateText(node);
773
- if (!text.trim() && kind !== "media" && kind !== "block") return null;
774
734
  return {
775
735
  containsMdx: false,
776
736
  kind,
@@ -826,6 +786,72 @@ const attachTableCellSources = (mdastTable, slateTable, editor) => {
826
786
  }
827
787
  }
828
788
  };
789
+ /**
790
+ * Custom serializers (e.g. MDX components) build their own mdast subtree,
791
+ * bypassing convertNodesWithSource. Walk Slate and mdast element children
792
+ * in parallel (by position) and attach sourceMap data so the handler
793
+ * wrappers capture per-child segments during serialization.
794
+ *
795
+ * Position-based matching works because custom serializers preserve child
796
+ * order. We filter to element children only (skip text/leaf nodes) in both
797
+ * trees before zipping.
798
+ */
799
+ /**
800
+ * Walk the mdast list tree and attach sources from the flat Slate list
801
+ * paragraphs. Slate uses flat paragraphs with `listStyleType` while mdast
802
+ * nests them as list > listItem > paragraph. We walk both in document order
803
+ * to zip them correctly.
804
+ */
805
+ const attachListSources = (mdastList, slateListItems, editor, insideMdx) => {
806
+ let slateIdx = 0;
807
+ const walkList = (list) => {
808
+ if (!Array.isArray(list.children)) return;
809
+ for (const listItem of list.children) {
810
+ if (listItem.type !== "listItem" || !Array.isArray(listItem.children)) continue;
811
+ for (const child of listItem.children) if (child.type === "list") walkList(child);
812
+ else if (slateIdx < slateListItems.length) {
813
+ const slateChild = slateListItems[slateIdx++];
814
+ const source = buildSource(slateChild, editor);
815
+ if (source) {
816
+ if (insideMdx) source.containsMdx = true;
817
+ attachSource(child, source);
818
+ }
819
+ }
820
+ }
821
+ };
822
+ walkList(mdastList);
823
+ };
824
+ const attachDescendantSources = (mdastNode, slateNode, editor, insideMdx) => {
825
+ if (!insideMdx && !MDX_MDAST_TYPES.has(mdastNode?.type ?? "")) return;
826
+ const slateChildren = slateNode?.children;
827
+ const mdastChildren = mdastNode?.children;
828
+ if (!Array.isArray(slateChildren) || !Array.isArray(mdastChildren)) return;
829
+ const slateElements = slateChildren.filter((c) => c?.type && Array.isArray(c.children));
830
+ const mdastElements = mdastChildren.filter((c) => c.type && c.type !== "text");
831
+ let si = 0;
832
+ for (const mdastChild of mdastElements) {
833
+ if (mdastChild.type === "list") {
834
+ const listRun = [];
835
+ while (si < slateElements.length && slateElements[si].listStyleType) {
836
+ listRun.push(slateElements[si]);
837
+ si++;
838
+ }
839
+ attachListSources(mdastChild, listRun, editor, insideMdx);
840
+ continue;
841
+ }
842
+ if (si >= slateElements.length) break;
843
+ const slateChild = slateElements[si];
844
+ si++;
845
+ const isMdx = insideMdx || MDX_MDAST_TYPES.has(mdastChild.type ?? "");
846
+ const source = buildSource(slateChild, editor);
847
+ if (source) {
848
+ if (isMdx) source.containsMdx = true;
849
+ attachSource(mdastChild, source);
850
+ }
851
+ if (mdastChild.type === "table") attachTableCellSources(mdastChild, slateChild, editor);
852
+ else if (insideMdx || MDX_MDAST_TYPES.has(mdastChild.type ?? "")) attachDescendantSources(mdastChild, slateChild, editor, isMdx);
853
+ }
854
+ };
829
855
  const buildMdastNodeWithSource = (node, options, isBlock = false) => {
830
856
  let mdastNode = buildMdastNode(node, options, isBlock);
831
857
  if (!mdastNode) {
@@ -847,19 +873,20 @@ const buildMdastNodeWithSource = (node, options, isBlock = false) => {
847
873
  return mdastNode;
848
874
  }
849
875
  const source = buildSource(node, options.editor);
876
+ const isMdxOutput = MDX_MDAST_TYPES.has(mdastNode?.type ?? "");
850
877
  if (!source) {
851
- if (mdastNode && MDX_MDAST_TYPES.has(mdastNode.type ?? "")) propagateMdxFlag(mdastNode);
878
+ if (isMdxOutput) propagateMdxFlag(mdastNode);
879
+ attachDescendantSources(mdastNode, node, options.editor, isMdxOutput);
852
880
  return mdastNode;
853
881
  }
854
- if (MDX_MDAST_TYPES.has(mdastNode?.type ?? "")) {
855
- source.containsMdx = true;
856
- if (Array.isArray(mdastNode?.children)) for (const child of mdastNode.children) propagateMdxFlag(child);
857
- }
882
+ if (isMdxOutput) source.containsMdx = true;
858
883
  if (source.kind === "blockquote" && Array.isArray(mdastNode?.children) && mdastNode.children[0]) {
859
884
  attachSource(mdastNode.children[0], source);
860
885
  return mdastNode;
861
886
  }
862
- return attachSource(mdastNode, source);
887
+ attachSource(mdastNode, source);
888
+ attachDescendantSources(mdastNode, node, options.editor, isMdxOutput);
889
+ return mdastNode;
863
890
  };
864
891
  const convertNodesWithSource = (nodes, options, isBlock = false) => {
865
892
  const mdastNodes = [];
@@ -1046,11 +1073,244 @@ const serializeMdWithSourceMap = (editor, options) => {
1046
1073
  }
1047
1074
  const ordered = Array.from(deduped.values()).sort((a, b) => comparePaths(a.path, b.path));
1048
1075
  return {
1076
+ allSegments: ordered,
1049
1077
  markdown,
1050
1078
  segments: ordered.filter((seg) => !ordered.some((other) => other !== seg && isPathPrefix(seg.path, other.path)))
1051
1079
  };
1052
1080
  };
1053
1081
 
1082
+ //#endregion
1083
+ //#region src/lib/serializer/resolveSelectionLines.ts
1084
+ const MDX_TAG_RE = /<[a-zA-Z_][\w.-]*/;
1085
+ /** `a` is an ancestor-or-self of `b`: a's path is a prefix of (or equal to) b's. */
1086
+ const isAncestorOrSelf = (a, b) => a.length <= b.length && a.every((v, i) => b[i] === v);
1087
+ const comparePoints = (a, b) => {
1088
+ const len = Math.max(a.path.length, b.path.length);
1089
+ for (let i = 0; i < len; i++) {
1090
+ const av = a.path[i] ?? 0;
1091
+ const bv = b.path[i] ?? 0;
1092
+ if (av !== bv) return av - bv;
1093
+ }
1094
+ return a.offset - b.offset;
1095
+ };
1096
+ /**
1097
+ * Find the segment covering the start of a selection at `path`.
1098
+ *
1099
+ * 1. Ancestor-or-self: segment whose path is a prefix of `path` (most specific wins)
1100
+ * 2. First descendant: `path` is a prefix of segment's path (container selection)
1101
+ */
1102
+ const findStartSegment = (segments, path) => {
1103
+ let best = null;
1104
+ let bestLen = -1;
1105
+ for (const seg of segments) if (isAncestorOrSelf(seg.path, path) && seg.path.length > bestLen) {
1106
+ best = seg;
1107
+ bestLen = seg.path.length;
1108
+ }
1109
+ if (best) return best;
1110
+ for (const seg of segments) if (isAncestorOrSelf(path, seg.path)) return seg;
1111
+ return null;
1112
+ };
1113
+ /**
1114
+ * Find the segment covering the end of a selection at `path`.
1115
+ *
1116
+ * Same as findStartSegment but for descendant matches picks the LAST one
1117
+ * (the container's last child segment).
1118
+ */
1119
+ const findEndSegment = (segments, path) => {
1120
+ let best = null;
1121
+ let bestLen = -1;
1122
+ for (const seg of segments) if (isAncestorOrSelf(seg.path, path) && seg.path.length > bestLen) {
1123
+ best = seg;
1124
+ bestLen = seg.path.length;
1125
+ }
1126
+ if (best) return best;
1127
+ let last = null;
1128
+ for (const seg of segments) if (isAncestorOrSelf(path, seg.path)) last = seg;
1129
+ return last;
1130
+ };
1131
+ /** Recursively compute the total text length of a Slate node. */
1132
+ const getTextLength = (node) => {
1133
+ if (typeof node?.text === "string") return node.text.length;
1134
+ if (Array.isArray(node?.children)) return node.children.reduce((sum, child) => sum + getTextLength(child), 0);
1135
+ return 0;
1136
+ };
1137
+ /** Walk the editor tree to reach the node at `path`. */
1138
+ const getNodeAtPath = (editor, path) => {
1139
+ let node = { children: editor.children };
1140
+ for (const idx of path) {
1141
+ if (!node?.children?.[idx]) return null;
1142
+ node = node.children[idx];
1143
+ }
1144
+ return node;
1145
+ };
1146
+ /**
1147
+ * Compute the absolute character offset within a segment's text.
1148
+ *
1149
+ * Given a segment at `segPath` (e.g. [1,3,0] = a paragraph) and a selection
1150
+ * point at `selPath` (e.g. [1,3,0,1] = second text node) with `selOffset`,
1151
+ * sum up text lengths of all Slate nodes preceding the selection point within
1152
+ * the segment, then add `selOffset`.
1153
+ */
1154
+ const computeAbsoluteOffset = (editor, segPath, selPath, selOffset) => {
1155
+ const segNode = getNodeAtPath(editor, segPath);
1156
+ if (!segNode) return null;
1157
+ const remainingPath = selPath.slice(segPath.length);
1158
+ if (remainingPath.length === 0) {
1159
+ let textOffset = 0;
1160
+ const children = segNode.children ?? [];
1161
+ for (let i = 0; i < Math.min(selOffset, children.length); i++) textOffset += getTextLength(children[i]);
1162
+ return textOffset;
1163
+ }
1164
+ let current = segNode;
1165
+ let accumulated = 0;
1166
+ for (const childIdx of remainingPath) {
1167
+ const children = current.children ?? [];
1168
+ for (let i = 0; i < childIdx && i < children.length; i++) accumulated += getTextLength(children[i]);
1169
+ if (!children[childIdx]) return null;
1170
+ current = children[childIdx];
1171
+ }
1172
+ return accumulated + selOffset;
1173
+ };
1174
+ /**
1175
+ * Given a segment and an absolute text offset, determine which line within
1176
+ * the segment the offset falls on.
1177
+ */
1178
+ const offsetToLine = (segText, segStartLine, absOffset) => {
1179
+ const clamped = Math.max(0, Math.min(absOffset, segText.length));
1180
+ return segStartLine + (segText.slice(0, clamped).match(/\n/g) ?? []).length;
1181
+ };
1182
+ /**
1183
+ * Whether a segment supports sub-line narrowing. Only for kinds where text
1184
+ * content lines map 1:1 to markdown output lines (no structural wrappers
1185
+ * like code fences, MDX tags, or table delimiters).
1186
+ */
1187
+ const NARROWABLE_KINDS = new Set([
1188
+ "paragraph",
1189
+ "heading",
1190
+ "list_item",
1191
+ "blockquote"
1192
+ ]);
1193
+ const canNarrow = (seg) => NARROWABLE_KINDS.has(seg.kind);
1194
+ const extractLines = (md, start, end) => md.split("\n").slice(start - 1, end).join("\n");
1195
+ /**
1196
+ * Resolve which markdown lines correspond to a Plate editor selection
1197
+ * using the source-map path mapping (no text matching).
1198
+ *
1199
+ * 1. Serializes editor to markdown with source-map segments
1200
+ * 2. Finds the segment matching the anchor path (longest prefix)
1201
+ * 3. Finds the segment matching the focus path
1202
+ * 4. Narrows to sub-line precision using selection offsets
1203
+ * 5. Returns the line range and extracted markdown
1204
+ */
1205
+ /**
1206
+ * When the selection lands on a container path (e.g. clicking an MDX label),
1207
+ * look up the container segment in `allSegments` to include the MDX wrapper
1208
+ * lines (opening/closing tags) rather than just the inner content.
1209
+ */
1210
+ const tryContainerExpand = ({ allSegments, leafSeg, selPath, currentLine, direction }) => {
1211
+ if (!leafSeg) return currentLine;
1212
+ if (selPath.length >= leafSeg.path.length) return currentLine;
1213
+ let containerSeg = null;
1214
+ let containerLen = -1;
1215
+ for (const seg of allSegments) if (isAncestorOrSelf(seg.path, selPath) && seg.path.length > containerLen && seg !== leafSeg) {
1216
+ containerSeg = seg;
1217
+ containerLen = seg.path.length;
1218
+ }
1219
+ for (const seg of allSegments) if (seg.path.length === selPath.length && seg.path.every((v, i) => selPath[i] === v)) {
1220
+ containerSeg = seg;
1221
+ break;
1222
+ }
1223
+ if (!containerSeg) return currentLine;
1224
+ return direction === "start" ? Math.min(currentLine, containerSeg.startLine) : Math.max(currentLine, containerSeg.endLine);
1225
+ };
1226
+ const resolveSelectionByPath = (editor, selection, options) => {
1227
+ const { allSegments, markdown, segments } = serializeMdWithSourceMap(editor, options);
1228
+ const [start, end] = comparePoints(selection.anchor, selection.focus) <= 0 ? [selection.anchor, selection.focus] : [selection.focus, selection.anchor];
1229
+ const startSeg = findStartSegment(segments, start.path);
1230
+ const endSeg = findEndSegment(segments, end.path);
1231
+ if (!startSeg && !endSeg) return {
1232
+ containsMdx: false,
1233
+ endLine: 1,
1234
+ extractedMarkdown: "",
1235
+ markdown,
1236
+ segments,
1237
+ startLine: 1
1238
+ };
1239
+ let startLine = startSeg?.startLine ?? endSeg.startLine;
1240
+ let endLine = endSeg?.endLine ?? startSeg.endLine;
1241
+ if (startSeg && startSeg.endLine > startSeg.startLine && canNarrow(startSeg)) {
1242
+ const absOffset = computeAbsoluteOffset(editor, startSeg.path, start.path, start.offset);
1243
+ if (absOffset !== null) startLine = offsetToLine(startSeg.text, startSeg.startLine, absOffset);
1244
+ }
1245
+ if (endSeg && endSeg.endLine > endSeg.startLine && canNarrow(endSeg)) {
1246
+ const absOffset = computeAbsoluteOffset(editor, endSeg.path, end.path, end.offset);
1247
+ if (absOffset !== null) endLine = offsetToLine(endSeg.text, endSeg.startLine, absOffset);
1248
+ }
1249
+ startLine = tryContainerExpand({
1250
+ allSegments,
1251
+ currentLine: startLine,
1252
+ direction: "start",
1253
+ leafSeg: startSeg,
1254
+ selPath: start.path
1255
+ });
1256
+ endLine = tryContainerExpand({
1257
+ allSegments,
1258
+ currentLine: endLine,
1259
+ direction: "end",
1260
+ leafSeg: endSeg,
1261
+ selPath: end.path
1262
+ });
1263
+ if (startLine > endLine) [startLine, endLine] = [endLine, startLine];
1264
+ const extractedMarkdown = extractLines(markdown, startLine, endLine);
1265
+ return {
1266
+ containsMdx: extractedMarkdown.includes("<") && MDX_TAG_RE.test(extractedMarkdown),
1267
+ endLine,
1268
+ extractedMarkdown,
1269
+ markdown,
1270
+ segments,
1271
+ startLine
1272
+ };
1273
+ };
1274
+
1275
+ //#endregion
1276
+ //#region src/lib/serializer/serializeInlineMd.ts
1277
+ const serializeInlineMd = (editor, options) => {
1278
+ const mergedOptions = getMergedOptionsSerialize(editor, options);
1279
+ const toRemarkProcessor = unified().use(mergedOptions.remarkPlugins ?? []).use(remarkStringify, {
1280
+ emphasis: "_",
1281
+ ...mergedOptions?.remarkStringifyOptions
1282
+ });
1283
+ if (options?.value?.length === 0) return "";
1284
+ const convertedTexts = convertTextsSerialize(mergedOptions.value, { editor });
1285
+ return toRemarkProcessor.stringify({
1286
+ children: convertedTexts,
1287
+ type: "root"
1288
+ });
1289
+ };
1290
+
1291
+ //#endregion
1292
+ //#region src/lib/serializer/serializeMd.ts
1293
+ /** Serialize the editor value to Markdown. */
1294
+ const serializeMd = (editor, options) => {
1295
+ const mergedOptions = getMergedOptionsSerialize(editor, options);
1296
+ const { remarkPlugins, value } = mergedOptions;
1297
+ const toRemarkProcessor = unified().use(remarkPlugins ?? []).use(remarkStringify, {
1298
+ emphasis: "_",
1299
+ ...mergedOptions?.remarkStringifyOptions
1300
+ });
1301
+ const mdast = slateToMdast({
1302
+ children: value,
1303
+ options: mergedOptions
1304
+ });
1305
+ return toRemarkProcessor.stringify(mdast);
1306
+ };
1307
+ const slateToMdast = ({ children, options }) => {
1308
+ return {
1309
+ children: convertNodesSerialize(children, options, true),
1310
+ type: "root"
1311
+ };
1312
+ };
1313
+
1054
1314
  //#endregion
1055
1315
  //#region src/lib/rules/defaultRules.ts
1056
1316
  const LEADING_NEWLINE_REGEX = /^\n/;
@@ -2292,4 +2552,4 @@ const remarkMention = () => (tree) => {
2292
2552
  };
2293
2553
 
2294
2554
  //#endregion
2295
- export { MarkdownPlugin, REMARK_MDX_TAG, basicMarkdownMarks, buildMdastNode, buildRules, buildSlateNode, columnRules, convertChildrenDeserialize, convertNodesDeserialize, convertNodesSerialize, convertTextsDeserialize, convertTextsSerialize, customMdxDeserialize, defaultRules, deserializeInlineMd, deserializeMd, fontRules, getCustomMark, getDeserializerByKey, getMergedOptionsDeserialize, getMergedOptionsSerialize, getRemarkPluginsWithoutMdx, getSerializerByKey, getStyleValue, htmlToJsx, listToMdastTree, markdownToAstProcessor, markdownToSlateNodes, markdownToSlateNodesSafely, mdastToPlate, mdastToSlate, mediaRules, parseAttributes, parseMarkdownBlocks, plateToMdast, propsToAttributes, remarkMdx, remarkMention, serializeInlineMd, serializeMd, serializeMdWithSourceMap, splitIncompleteMdx, stripMarkdown, stripMarkdownBlocks, stripMarkdownInline, tagRemarkPlugin, unreachable, wrapWithBlockId };
2555
+ export { MarkdownPlugin, REMARK_MDX_TAG, basicMarkdownMarks, buildMdastNode, buildRules, buildSlateNode, columnRules, convertChildrenDeserialize, convertNodesDeserialize, convertNodesSerialize, convertTextsDeserialize, convertTextsSerialize, customMdxDeserialize, defaultRules, deserializeInlineMd, deserializeMd, fontRules, getCustomMark, getDeserializerByKey, getMergedOptionsDeserialize, getMergedOptionsSerialize, getRemarkPluginsWithoutMdx, getSerializerByKey, getStyleValue, htmlToJsx, listToMdastTree, markdownToAstProcessor, markdownToSlateNodes, markdownToSlateNodesSafely, mdastToPlate, mdastToSlate, mediaRules, parseAttributes, parseMarkdownBlocks, plateToMdast, propsToAttributes, remarkMdx, remarkMention, resolveSelectionByPath, serializeInlineMd, serializeMd, serializeMdWithSourceMap, splitIncompleteMdx, stripMarkdown, stripMarkdownBlocks, stripMarkdownInline, tagRemarkPlugin, unreachable, wrapWithBlockId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lofcz/platejs-markdown",
3
- "version": "52.3.8",
3
+ "version": "52.3.9",
4
4
  "description": "Markdown serializer plugin for Plate",
5
5
  "keywords": [
6
6
  "markdown",
@@ -49,8 +49,8 @@
49
49
  "@types/unist": "^3.0.3",
50
50
  "remark-gfm": "4.0.1",
51
51
  "remark-math": "6.0.0",
52
- "@plate/scripts": "1.0.0",
53
- "platejs": "npm:@lofcz/platejs@52.3.6"
52
+ "platejs": "npm:@lofcz/platejs@52.3.6",
53
+ "@plate/scripts": "1.0.0"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "platejs": ">=52.0.11",