@lofcz/platejs-markdown 53.0.0 → 53.1.3
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 +52 -1
- package/dist/index.js +155 -40
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -77,9 +77,60 @@ type ResolveSelectionByPathResult = {
|
|
|
77
77
|
endLine: number;
|
|
78
78
|
extractedMarkdown: string;
|
|
79
79
|
markdown: string;
|
|
80
|
+
/**
|
|
81
|
+
* `false` when neither endpoint of the selection mapped to any segment
|
|
82
|
+
* (e.g. stale paths after a concurrent edit). The line numbers in this
|
|
83
|
+
* case are placeholders (1, 1) and the extracted markdown is empty.
|
|
84
|
+
* Callers must check this flag before using the result; the previous
|
|
85
|
+
* silent default produced invalid line-1 references.
|
|
86
|
+
*/
|
|
87
|
+
resolved: boolean;
|
|
80
88
|
segments: MarkdownSourceMapSegment[];
|
|
81
89
|
startLine: number;
|
|
82
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Order two Slate selection points in document order.
|
|
93
|
+
*
|
|
94
|
+
* Slate selection points come in two flavours that previously got compared
|
|
95
|
+
* incompatibly:
|
|
96
|
+
*
|
|
97
|
+
* - *Element-level*: `path` ends at an element, `offset` is the boundary
|
|
98
|
+
* index between its children (0 = before the first child, N = after the
|
|
99
|
+
* N-th child).
|
|
100
|
+
* - *Leaf-level*: `path` descends into a text leaf, `offset` is a character
|
|
101
|
+
* index within that text.
|
|
102
|
+
*
|
|
103
|
+
* The previous implementation padded the shorter path with `0`s and then
|
|
104
|
+
* fell back to comparing offsets — which only works when both points are
|
|
105
|
+
* at the same depth. For mixed-depth points like `[3] offset 2`
|
|
106
|
+
* (≈ "before child 2 of [3]") vs `[3, 1] offset 5` (≈ "inside child 1 of
|
|
107
|
+
* [3]"), it returned the wrong order silently because the offsets are not
|
|
108
|
+
* commensurate.
|
|
109
|
+
*
|
|
110
|
+
* Fix: when the shorter point is element-level, expand it to the leaf-level
|
|
111
|
+
* coordinate space by treating its offset as the next path step ("before
|
|
112
|
+
* child `offset`"), then continue path comparison. Offsets are only
|
|
113
|
+
* compared when both points are at identical depth.
|
|
114
|
+
*/
|
|
115
|
+
declare const comparePoints: (a: SelectionPoint, b: SelectionPoint) => number;
|
|
116
|
+
/**
|
|
117
|
+
* Find the segment covering the start of a selection at `path`.
|
|
118
|
+
*
|
|
119
|
+
* 1. Ancestor-or-self: segment whose path is a prefix of `path` (most specific wins)
|
|
120
|
+
* 2. First descendant: `path` is a prefix of segment's path (container selection)
|
|
121
|
+
*/
|
|
122
|
+
declare const findStartSegment: (segments: MarkdownSourceMapSegment[], path: number[]) => MarkdownSourceMapSegment | null;
|
|
123
|
+
/**
|
|
124
|
+
* Find the segment covering the end of a selection at `path`.
|
|
125
|
+
*
|
|
126
|
+
* Same as findStartSegment but for descendant matches picks the segment
|
|
127
|
+
* with the LARGEST `endLine` (the container's deepest/latest child
|
|
128
|
+
* segment). The previous implementation returned the last segment by
|
|
129
|
+
* iteration order, which only happened to work because `allSegments` is
|
|
130
|
+
* sorted by path today — any caller passing an unsorted array would
|
|
131
|
+
* silently get the wrong range end.
|
|
132
|
+
*/
|
|
133
|
+
declare const findEndSegment: (segments: MarkdownSourceMapSegment[], path: number[]) => MarkdownSourceMapSegment | null;
|
|
83
134
|
declare const resolveSelectionByPath: (editor: SlateEditor, selection: SelectionRange, options?: Omit<SerializeMdOptions, "editor">) => ResolveSelectionByPathResult;
|
|
84
135
|
//#endregion
|
|
85
136
|
//#region src/lib/serializer/serializeInlineMd.d.ts
|
|
@@ -536,4 +587,4 @@ declare const tagRemarkPlugin: (pluginFn: any, tag: string) => {
|
|
|
536
587
|
};
|
|
537
588
|
declare const getRemarkPluginsWithoutMdx: (plugins: Plugin[]) => Plugin[];
|
|
538
589
|
//#endregion
|
|
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 };
|
|
590
|
+
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, comparePoints, convertChildrenDeserialize, convertNodesDeserialize, convertNodesSerialize, convertTextsDeserialize, convertTextsSerialize, customMdxDeserialize, defaultRules, deserializeInlineMd, deserializeMd, findEndSegment, findStartSegment, 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
|
@@ -519,12 +519,14 @@ function listToMdastTree(nodes, options, isBlock = false) {
|
|
|
519
519
|
};
|
|
520
520
|
stackTop = indentStack.at(-1);
|
|
521
521
|
}
|
|
522
|
+
const listItemParagraph = {
|
|
523
|
+
children: convertNodesSerialize(node.children, options),
|
|
524
|
+
type: "paragraph"
|
|
525
|
+
};
|
|
526
|
+
if (Array.isArray(node.__sourceMapPath)) listItemParagraph.__sourceMapSlatePath = node.__sourceMapPath;
|
|
522
527
|
const listItem = {
|
|
523
528
|
checked: null,
|
|
524
|
-
children: [
|
|
525
|
-
children: convertNodesSerialize(node.children, options),
|
|
526
|
-
type: "paragraph"
|
|
527
|
-
}],
|
|
529
|
+
children: [listItemParagraph],
|
|
528
530
|
spread: options.spread ?? false,
|
|
529
531
|
type: "listItem"
|
|
530
532
|
};
|
|
@@ -565,12 +567,14 @@ function processListWithBlockIds(nodes, options) {
|
|
|
565
567
|
start: node.listStyleType === "decimal" ? i + 1 : void 0,
|
|
566
568
|
type: "list"
|
|
567
569
|
};
|
|
570
|
+
const singleParagraph = {
|
|
571
|
+
children: convertNodesSerialize(node.children, options),
|
|
572
|
+
type: "paragraph"
|
|
573
|
+
};
|
|
574
|
+
if (Array.isArray(node.__sourceMapPath)) singleParagraph.__sourceMapSlatePath = node.__sourceMapPath;
|
|
568
575
|
const listItem = {
|
|
569
576
|
checked: null,
|
|
570
|
-
children: [
|
|
571
|
-
children: convertNodesSerialize(node.children, options),
|
|
572
|
-
type: "paragraph"
|
|
573
|
-
}],
|
|
577
|
+
children: [singleParagraph],
|
|
574
578
|
spread: options.spread ?? false,
|
|
575
579
|
type: "listItem"
|
|
576
580
|
};
|
|
@@ -621,6 +625,23 @@ const convertNodesSerialize = (nodes, options, isBlock = false) => {
|
|
|
621
625
|
}
|
|
622
626
|
return mdastNodes;
|
|
623
627
|
};
|
|
628
|
+
/**
|
|
629
|
+
* Mirror the Slate `__sourceMapPath` annotation onto the produced mdast node.
|
|
630
|
+
*
|
|
631
|
+
* This breadcrumb lets `attachDescendantSources` (in
|
|
632
|
+
* `serializeMdWithSourceMap`) match mdast children back to their originating
|
|
633
|
+
* Slate child by *path* instead of by array position. Without it, any custom
|
|
634
|
+
* MDX serializer that injects, drops, or reorders mdast children would
|
|
635
|
+
* silently shift the source-map zipper and assign each Slate child the line
|
|
636
|
+
* range of a different mdast node.
|
|
637
|
+
*
|
|
638
|
+
* The property is a plain own-property on the mdast object — invisible to
|
|
639
|
+
* `mdast-util-to-markdown`, which only consumes the documented mdast fields.
|
|
640
|
+
*/
|
|
641
|
+
const tagWithSlatePath = (mdastNode, slateNode) => {
|
|
642
|
+
if (mdastNode && typeof mdastNode === "object" && Array.isArray(slateNode?.__sourceMapPath)) mdastNode.__sourceMapSlatePath = slateNode.__sourceMapPath;
|
|
643
|
+
return mdastNode;
|
|
644
|
+
};
|
|
624
645
|
const buildMdastNode = (node, options, isBlock = false) => {
|
|
625
646
|
const editor = options.editor;
|
|
626
647
|
let key = getPluginKey(editor, node.type) ?? node.type;
|
|
@@ -629,8 +650,8 @@ const buildMdastNode = (node, options, isBlock = false) => {
|
|
|
629
650
|
const nodeParser = getSerializerByKey(key, options);
|
|
630
651
|
if (nodeParser) {
|
|
631
652
|
const mdastNode = nodeParser(node, options);
|
|
632
|
-
if (options.withBlockId && node.id && isBlock) return wrapWithBlockId(mdastNode, node.id);
|
|
633
|
-
return mdastNode;
|
|
653
|
+
if (options.withBlockId && node.id && isBlock) return tagWithSlatePath(wrapWithBlockId(mdastNode, node.id), node);
|
|
654
|
+
return tagWithSlatePath(mdastNode, node);
|
|
634
655
|
}
|
|
635
656
|
unreachable(node);
|
|
636
657
|
};
|
|
@@ -723,6 +744,9 @@ const slateNodeKind = (node, editor) => {
|
|
|
723
744
|
const pType = getPluginType(editor, KEYS.p) ?? KEYS.p;
|
|
724
745
|
if (node.type === pType) return "paragraph";
|
|
725
746
|
if (SEGMENT_SKIP_TYPES.has(key)) return null;
|
|
747
|
+
try {
|
|
748
|
+
if (editor?.api?.isInline?.(node)) return null;
|
|
749
|
+
} catch {}
|
|
726
750
|
if (Array.isArray(node.children)) return "block";
|
|
727
751
|
return null;
|
|
728
752
|
};
|
|
@@ -805,14 +829,23 @@ const attachTableCellSources = (mdastTable, slateTable, editor) => {
|
|
|
805
829
|
* to zip them correctly.
|
|
806
830
|
*/
|
|
807
831
|
const attachListSources = (mdastList, slateListItems, editor, insideMdx) => {
|
|
832
|
+
const slateByPath = /* @__PURE__ */ new Map();
|
|
833
|
+
for (const item of slateListItems) if (Array.isArray(item?.__sourceMapPath)) slateByPath.set(toPathKey(item.__sourceMapPath), item);
|
|
808
834
|
let slateIdx = 0;
|
|
809
835
|
const walkList = (list) => {
|
|
810
836
|
if (!Array.isArray(list.children)) return;
|
|
811
837
|
for (const listItem of list.children) {
|
|
812
838
|
if (listItem.type !== "listItem" || !Array.isArray(listItem.children)) continue;
|
|
813
|
-
for (const child of listItem.children)
|
|
814
|
-
|
|
815
|
-
|
|
839
|
+
for (const child of listItem.children) {
|
|
840
|
+
if (child.type === "list") {
|
|
841
|
+
walkList(child);
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
const taggedPath = child.__sourceMapSlatePath;
|
|
845
|
+
let slateChild = null;
|
|
846
|
+
if (Array.isArray(taggedPath)) slateChild = slateByPath.get(toPathKey(taggedPath)) ?? null;
|
|
847
|
+
if (!slateChild && slateIdx < slateListItems.length) slateChild = slateListItems[slateIdx++];
|
|
848
|
+
if (!slateChild) continue;
|
|
816
849
|
const source = buildSource(slateChild, editor);
|
|
817
850
|
if (source) {
|
|
818
851
|
if (insideMdx) source.containsMdx = true;
|
|
@@ -823,35 +856,78 @@ const attachListSources = (mdastList, slateListItems, editor, insideMdx) => {
|
|
|
823
856
|
};
|
|
824
857
|
walkList(mdastList);
|
|
825
858
|
};
|
|
859
|
+
/**
|
|
860
|
+
* Count the number of Slate list-paragraph slots a mdast list tree needs.
|
|
861
|
+
*
|
|
862
|
+
* Each non-`list` child of a `listItem` corresponds to exactly one Slate
|
|
863
|
+
* list paragraph; nested `list` nodes recurse. This lets us consume exactly
|
|
864
|
+
* the right slice of consecutive Slate list paragraphs for a given mdast
|
|
865
|
+
* list, instead of greedily swallowing every list-styled Slate sibling.
|
|
866
|
+
*/
|
|
867
|
+
const countMdastListSlots = (list) => {
|
|
868
|
+
if (!list || !Array.isArray(list.children)) return 0;
|
|
869
|
+
let count = 0;
|
|
870
|
+
for (const listItem of list.children) {
|
|
871
|
+
if (listItem.type !== "listItem" || !Array.isArray(listItem.children)) continue;
|
|
872
|
+
for (const child of listItem.children) if (child?.type === "list") count += countMdastListSlots(child);
|
|
873
|
+
else count += 1;
|
|
874
|
+
}
|
|
875
|
+
return count;
|
|
876
|
+
};
|
|
877
|
+
const pathsEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
826
878
|
const attachDescendantSources = (mdastNode, slateNode, editor, insideMdx) => {
|
|
827
879
|
if (!insideMdx && !MDX_MDAST_TYPES.has(mdastNode?.type ?? "")) return;
|
|
828
880
|
const slateChildren = slateNode?.children;
|
|
829
881
|
const mdastChildren = mdastNode?.children;
|
|
830
882
|
if (!Array.isArray(slateChildren) || !Array.isArray(mdastChildren)) return;
|
|
831
883
|
const slateElements = slateChildren.filter((c) => c?.type && Array.isArray(c.children));
|
|
832
|
-
const
|
|
884
|
+
const consumedSlateIndices = /* @__PURE__ */ new Set();
|
|
885
|
+
const attachSingleByMdast = (mdastChild, slateChild) => {
|
|
886
|
+
const isMdx = insideMdx || MDX_MDAST_TYPES.has(mdastChild.type ?? "");
|
|
887
|
+
const source = buildSource(slateChild, editor);
|
|
888
|
+
if (source) {
|
|
889
|
+
if (isMdx) source.containsMdx = true;
|
|
890
|
+
attachSource(mdastChild, source);
|
|
891
|
+
}
|
|
892
|
+
if (mdastChild.type === "table") attachTableCellSources(mdastChild, slateChild, editor);
|
|
893
|
+
else if (insideMdx || MDX_MDAST_TYPES.has(mdastChild.type ?? "")) attachDescendantSources(mdastChild, slateChild, editor, isMdx);
|
|
894
|
+
};
|
|
895
|
+
for (const mdastChild of mdastChildren) {
|
|
896
|
+
if (!mdastChild || !mdastChild.type || mdastChild.type === "text") continue;
|
|
897
|
+
const taggedPath = mdastChild.__sourceMapSlatePath;
|
|
898
|
+
if (!Array.isArray(taggedPath)) continue;
|
|
899
|
+
const slateIdx = slateElements.findIndex((c) => Array.isArray(c.__sourceMapPath) && pathsEqual(c.__sourceMapPath, taggedPath));
|
|
900
|
+
if (slateIdx === -1) continue;
|
|
901
|
+
consumedSlateIndices.add(slateIdx);
|
|
902
|
+
attachSingleByMdast(mdastChild, slateElements[slateIdx]);
|
|
903
|
+
}
|
|
833
904
|
let si = 0;
|
|
834
|
-
|
|
905
|
+
const advanceToUnconsumed = () => {
|
|
906
|
+
while (si < slateElements.length && consumedSlateIndices.has(si)) si++;
|
|
907
|
+
};
|
|
908
|
+
for (const mdastChild of mdastChildren) {
|
|
909
|
+
if (!mdastChild || !mdastChild.type || mdastChild.type === "text") continue;
|
|
910
|
+
if (Array.isArray(mdastChild.__sourceMapSlatePath)) continue;
|
|
835
911
|
if (mdastChild.type === "list") {
|
|
912
|
+
const slotsNeeded = countMdastListSlots(mdastChild);
|
|
836
913
|
const listRun = [];
|
|
837
|
-
|
|
914
|
+
for (let k = 0; k < slotsNeeded; k++) {
|
|
915
|
+
advanceToUnconsumed();
|
|
916
|
+
if (si >= slateElements.length) break;
|
|
917
|
+
if (!slateElements[si].listStyleType) break;
|
|
838
918
|
listRun.push(slateElements[si]);
|
|
919
|
+
consumedSlateIndices.add(si);
|
|
839
920
|
si++;
|
|
840
921
|
}
|
|
841
922
|
attachListSources(mdastChild, listRun, editor, insideMdx);
|
|
842
923
|
continue;
|
|
843
924
|
}
|
|
925
|
+
advanceToUnconsumed();
|
|
844
926
|
if (si >= slateElements.length) break;
|
|
845
927
|
const slateChild = slateElements[si];
|
|
928
|
+
consumedSlateIndices.add(si);
|
|
846
929
|
si++;
|
|
847
|
-
|
|
848
|
-
const source = buildSource(slateChild, editor);
|
|
849
|
-
if (source) {
|
|
850
|
-
if (isMdx) source.containsMdx = true;
|
|
851
|
-
attachSource(mdastChild, source);
|
|
852
|
-
}
|
|
853
|
-
if (mdastChild.type === "table") attachTableCellSources(mdastChild, slateChild, editor);
|
|
854
|
-
else if (insideMdx || MDX_MDAST_TYPES.has(mdastChild.type ?? "")) attachDescendantSources(mdastChild, slateChild, editor, isMdx);
|
|
930
|
+
attachSingleByMdast(mdastChild, slateChild);
|
|
855
931
|
}
|
|
856
932
|
};
|
|
857
933
|
const buildMdastNodeWithSource = (node, options, isBlock = false) => {
|
|
@@ -1048,7 +1124,7 @@ const serializeMdWithSourceMap = (editor, options) => {
|
|
|
1048
1124
|
const startLine = info.now.line;
|
|
1049
1125
|
const emitted = handler(node, parent, state, info);
|
|
1050
1126
|
const source = node?.data?.sourceMap;
|
|
1051
|
-
if (source && emitted.trim()) pushSegment(source, emitted, startLine, startLine + (emitted.split(/\r?\n|\r/g).length - 1));
|
|
1127
|
+
if (source && emitted.trim()) pushSegment(source, emitted, startLine, startLine + (emitted.replace(/(?:\r?\n|\r)+$/g, "").split(/\r?\n|\r/g).length - 1));
|
|
1052
1128
|
if (type === "table" && Array.isArray(node?.children)) for (let r = 0; r < node.children.length; r++) {
|
|
1053
1129
|
const line = r === 0 ? startLine : startLine + 1 + r;
|
|
1054
1130
|
const cells = node.children[r]?.children;
|
|
@@ -1086,14 +1162,44 @@ const serializeMdWithSourceMap = (editor, options) => {
|
|
|
1086
1162
|
const MDX_TAG_RE = /<[a-zA-Z_][\w.-]*/;
|
|
1087
1163
|
/** `a` is an ancestor-or-self of `b`: a's path is a prefix of (or equal to) b's. */
|
|
1088
1164
|
const isAncestorOrSelf = (a, b) => a.length <= b.length && a.every((v, i) => b[i] === v);
|
|
1165
|
+
/**
|
|
1166
|
+
* Order two Slate selection points in document order.
|
|
1167
|
+
*
|
|
1168
|
+
* Slate selection points come in two flavours that previously got compared
|
|
1169
|
+
* incompatibly:
|
|
1170
|
+
*
|
|
1171
|
+
* - *Element-level*: `path` ends at an element, `offset` is the boundary
|
|
1172
|
+
* index between its children (0 = before the first child, N = after the
|
|
1173
|
+
* N-th child).
|
|
1174
|
+
* - *Leaf-level*: `path` descends into a text leaf, `offset` is a character
|
|
1175
|
+
* index within that text.
|
|
1176
|
+
*
|
|
1177
|
+
* The previous implementation padded the shorter path with `0`s and then
|
|
1178
|
+
* fell back to comparing offsets — which only works when both points are
|
|
1179
|
+
* at the same depth. For mixed-depth points like `[3] offset 2`
|
|
1180
|
+
* (≈ "before child 2 of [3]") vs `[3, 1] offset 5` (≈ "inside child 1 of
|
|
1181
|
+
* [3]"), it returned the wrong order silently because the offsets are not
|
|
1182
|
+
* commensurate.
|
|
1183
|
+
*
|
|
1184
|
+
* Fix: when the shorter point is element-level, expand it to the leaf-level
|
|
1185
|
+
* coordinate space by treating its offset as the next path step ("before
|
|
1186
|
+
* child `offset`"), then continue path comparison. Offsets are only
|
|
1187
|
+
* compared when both points are at identical depth.
|
|
1188
|
+
*/
|
|
1089
1189
|
const comparePoints = (a, b) => {
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1190
|
+
const aPath = a.path;
|
|
1191
|
+
const bPath = b.path;
|
|
1192
|
+
const aLen = aPath.length;
|
|
1193
|
+
const bLen = bPath.length;
|
|
1194
|
+
const common = Math.min(aLen, bLen);
|
|
1195
|
+
for (let i = 0; i < common; i++) if (aPath[i] !== bPath[i]) return aPath[i] - bPath[i];
|
|
1196
|
+
if (aLen === bLen) return a.offset - b.offset;
|
|
1197
|
+
if (aLen < bLen) {
|
|
1198
|
+
if (a.offset !== bPath[common]) return a.offset - bPath[common];
|
|
1199
|
+
return -1;
|
|
1095
1200
|
}
|
|
1096
|
-
|
|
1201
|
+
if (aPath[common] !== b.offset) return aPath[common] - b.offset;
|
|
1202
|
+
return 1;
|
|
1097
1203
|
};
|
|
1098
1204
|
/**
|
|
1099
1205
|
* Find the segment covering the start of a selection at `path`.
|
|
@@ -1115,8 +1221,12 @@ const findStartSegment = (segments, path) => {
|
|
|
1115
1221
|
/**
|
|
1116
1222
|
* Find the segment covering the end of a selection at `path`.
|
|
1117
1223
|
*
|
|
1118
|
-
* Same as findStartSegment but for descendant matches picks the
|
|
1119
|
-
* (the container's
|
|
1224
|
+
* Same as findStartSegment but for descendant matches picks the segment
|
|
1225
|
+
* with the LARGEST `endLine` (the container's deepest/latest child
|
|
1226
|
+
* segment). The previous implementation returned the last segment by
|
|
1227
|
+
* iteration order, which only happened to work because `allSegments` is
|
|
1228
|
+
* sorted by path today — any caller passing an unsorted array would
|
|
1229
|
+
* silently get the wrong range end.
|
|
1120
1230
|
*/
|
|
1121
1231
|
const findEndSegment = (segments, path) => {
|
|
1122
1232
|
let best = null;
|
|
@@ -1126,9 +1236,12 @@ const findEndSegment = (segments, path) => {
|
|
|
1126
1236
|
bestLen = seg.path.length;
|
|
1127
1237
|
}
|
|
1128
1238
|
if (best) return best;
|
|
1129
|
-
let
|
|
1130
|
-
for (const seg of segments)
|
|
1131
|
-
|
|
1239
|
+
let latest = null;
|
|
1240
|
+
for (const seg of segments) {
|
|
1241
|
+
if (!isAncestorOrSelf(path, seg.path)) continue;
|
|
1242
|
+
if (!latest || seg.endLine > latest.endLine) latest = seg;
|
|
1243
|
+
}
|
|
1244
|
+
return latest;
|
|
1132
1245
|
};
|
|
1133
1246
|
/** Recursively compute the total text length of a Slate node. */
|
|
1134
1247
|
const getTextLength = (node) => {
|
|
@@ -1228,13 +1341,14 @@ const tryContainerExpand = ({ allSegments, leafSeg, selPath, currentLine, direct
|
|
|
1228
1341
|
const resolveSelectionByPath = (editor, selection, options) => {
|
|
1229
1342
|
const { allSegments, markdown, segments } = serializeMdWithSourceMap(editor, options);
|
|
1230
1343
|
const [start, end] = comparePoints(selection.anchor, selection.focus) <= 0 ? [selection.anchor, selection.focus] : [selection.focus, selection.anchor];
|
|
1231
|
-
const startSeg = findStartSegment(segments, start.path);
|
|
1232
|
-
const endSeg = findEndSegment(segments, end.path);
|
|
1344
|
+
const startSeg = findStartSegment(segments, start.path) ?? findStartSegment(allSegments, start.path);
|
|
1345
|
+
const endSeg = findEndSegment(segments, end.path) ?? findEndSegment(allSegments, end.path);
|
|
1233
1346
|
if (!startSeg && !endSeg) return {
|
|
1234
1347
|
containsMdx: false,
|
|
1235
1348
|
endLine: 1,
|
|
1236
1349
|
extractedMarkdown: "",
|
|
1237
1350
|
markdown,
|
|
1351
|
+
resolved: false,
|
|
1238
1352
|
segments,
|
|
1239
1353
|
startLine: 1
|
|
1240
1354
|
};
|
|
@@ -1269,6 +1383,7 @@ const resolveSelectionByPath = (editor, selection, options) => {
|
|
|
1269
1383
|
endLine,
|
|
1270
1384
|
extractedMarkdown,
|
|
1271
1385
|
markdown,
|
|
1386
|
+
resolved: true,
|
|
1272
1387
|
segments,
|
|
1273
1388
|
startLine
|
|
1274
1389
|
};
|
|
@@ -1370,7 +1485,7 @@ const defaultRules = {
|
|
|
1370
1485
|
}),
|
|
1371
1486
|
serialize: (node, options) => {
|
|
1372
1487
|
const children = convertNodesSerialize(node.children, options);
|
|
1373
|
-
if (children.length === 1 && children[0]?.type === "text" && children[0].value === node.url && BARE_AUTOLINK_PROTOCOL_REGEX.test(node.url ?? "")) return {
|
|
1488
|
+
if (children.length === 1 && children[0]?.type === "text" && children[0].value === node.url && options.remarkStringifyOptions?.resourceLink !== true && BARE_AUTOLINK_PROTOCOL_REGEX.test(node.url ?? "")) return {
|
|
1374
1489
|
type: "html",
|
|
1375
1490
|
value: node.url
|
|
1376
1491
|
};
|
|
@@ -2618,4 +2733,4 @@ const remarkMention = () => (tree) => {
|
|
|
2618
2733
|
};
|
|
2619
2734
|
|
|
2620
2735
|
//#endregion
|
|
2621
|
-
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 };
|
|
2736
|
+
export { MarkdownPlugin, REMARK_MDX_TAG, basicMarkdownMarks, buildMdastNode, buildRules, buildSlateNode, columnRules, comparePoints, convertChildrenDeserialize, convertNodesDeserialize, convertNodesSerialize, convertTextsDeserialize, convertTextsSerialize, customMdxDeserialize, defaultRules, deserializeInlineMd, deserializeMd, findEndSegment, findStartSegment, 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": "53.
|
|
3
|
+
"version": "53.1.3",
|
|
4
4
|
"description": "Markdown serializer plugin for Plate",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"remark-emoji": "5.0.1",
|
|
52
52
|
"remark-gfm": "4.0.1",
|
|
53
53
|
"remark-math": "6.0.0",
|
|
54
|
-
"@plate/scripts": "1.0.0",
|
|
55
54
|
"@platejs/footnote": "npm:@lofcz/platejs-footnote@53.0.0",
|
|
56
|
-
"
|
|
55
|
+
"@plate/scripts": "1.0.0",
|
|
56
|
+
"platejs": "npm:@lofcz/platejs@53.1.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"platejs": ">=53.0.0",
|