@lofcz/platejs-markdown 53.1.1 → 53.1.4
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 +192 -52
- 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
|
@@ -115,25 +115,50 @@ const getCustomMark = (options) => {
|
|
|
115
115
|
//#region src/lib/rules/utils/parseAttributes.ts
|
|
116
116
|
function parseAttributes(attributes) {
|
|
117
117
|
const props = {};
|
|
118
|
-
if (attributes
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
if (!attributes || attributes.length === 0) return props;
|
|
119
|
+
for (const attr of attributes) {
|
|
120
|
+
if (!attr?.name) continue;
|
|
121
|
+
const raw = attr.value;
|
|
122
|
+
if (raw === void 0) continue;
|
|
123
|
+
if (raw === null) {
|
|
124
|
+
props[attr.name] = true;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (typeof raw === "object") {
|
|
128
|
+
props[attr.name] = raw;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (typeof raw === "string") {
|
|
121
132
|
try {
|
|
122
|
-
|
|
123
|
-
} catch
|
|
124
|
-
|
|
133
|
+
props[attr.name] = JSON.parse(raw);
|
|
134
|
+
} catch {
|
|
135
|
+
props[attr.name] = raw;
|
|
125
136
|
}
|
|
126
|
-
|
|
137
|
+
continue;
|
|
127
138
|
}
|
|
128
|
-
|
|
139
|
+
props[attr.name] = raw;
|
|
140
|
+
}
|
|
129
141
|
return props;
|
|
130
142
|
}
|
|
131
143
|
function propsToAttributes(props) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
144
|
+
const out = [];
|
|
145
|
+
for (const [name, value] of Object.entries(props)) {
|
|
146
|
+
if (value === void 0 || value === null) continue;
|
|
147
|
+
if (value === true) {
|
|
148
|
+
out.push({
|
|
149
|
+
name,
|
|
150
|
+
type: "mdxJsxAttribute",
|
|
151
|
+
value: null
|
|
152
|
+
});
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
out.push({
|
|
156
|
+
name,
|
|
157
|
+
type: "mdxJsxAttribute",
|
|
158
|
+
value: typeof value === "string" || value?.type === "mdxJsxAttributeValueExpression" ? value : JSON.stringify(value)
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
137
162
|
}
|
|
138
163
|
|
|
139
164
|
//#endregion
|
|
@@ -519,12 +544,14 @@ function listToMdastTree(nodes, options, isBlock = false) {
|
|
|
519
544
|
};
|
|
520
545
|
stackTop = indentStack.at(-1);
|
|
521
546
|
}
|
|
547
|
+
const listItemParagraph = {
|
|
548
|
+
children: convertNodesSerialize(node.children, options),
|
|
549
|
+
type: "paragraph"
|
|
550
|
+
};
|
|
551
|
+
if (Array.isArray(node.__sourceMapPath)) listItemParagraph.__sourceMapSlatePath = node.__sourceMapPath;
|
|
522
552
|
const listItem = {
|
|
523
553
|
checked: null,
|
|
524
|
-
children: [
|
|
525
|
-
children: convertNodesSerialize(node.children, options),
|
|
526
|
-
type: "paragraph"
|
|
527
|
-
}],
|
|
554
|
+
children: [listItemParagraph],
|
|
528
555
|
spread: options.spread ?? false,
|
|
529
556
|
type: "listItem"
|
|
530
557
|
};
|
|
@@ -565,12 +592,14 @@ function processListWithBlockIds(nodes, options) {
|
|
|
565
592
|
start: node.listStyleType === "decimal" ? i + 1 : void 0,
|
|
566
593
|
type: "list"
|
|
567
594
|
};
|
|
595
|
+
const singleParagraph = {
|
|
596
|
+
children: convertNodesSerialize(node.children, options),
|
|
597
|
+
type: "paragraph"
|
|
598
|
+
};
|
|
599
|
+
if (Array.isArray(node.__sourceMapPath)) singleParagraph.__sourceMapSlatePath = node.__sourceMapPath;
|
|
568
600
|
const listItem = {
|
|
569
601
|
checked: null,
|
|
570
|
-
children: [
|
|
571
|
-
children: convertNodesSerialize(node.children, options),
|
|
572
|
-
type: "paragraph"
|
|
573
|
-
}],
|
|
602
|
+
children: [singleParagraph],
|
|
574
603
|
spread: options.spread ?? false,
|
|
575
604
|
type: "listItem"
|
|
576
605
|
};
|
|
@@ -621,6 +650,23 @@ const convertNodesSerialize = (nodes, options, isBlock = false) => {
|
|
|
621
650
|
}
|
|
622
651
|
return mdastNodes;
|
|
623
652
|
};
|
|
653
|
+
/**
|
|
654
|
+
* Mirror the Slate `__sourceMapPath` annotation onto the produced mdast node.
|
|
655
|
+
*
|
|
656
|
+
* This breadcrumb lets `attachDescendantSources` (in
|
|
657
|
+
* `serializeMdWithSourceMap`) match mdast children back to their originating
|
|
658
|
+
* Slate child by *path* instead of by array position. Without it, any custom
|
|
659
|
+
* MDX serializer that injects, drops, or reorders mdast children would
|
|
660
|
+
* silently shift the source-map zipper and assign each Slate child the line
|
|
661
|
+
* range of a different mdast node.
|
|
662
|
+
*
|
|
663
|
+
* The property is a plain own-property on the mdast object — invisible to
|
|
664
|
+
* `mdast-util-to-markdown`, which only consumes the documented mdast fields.
|
|
665
|
+
*/
|
|
666
|
+
const tagWithSlatePath = (mdastNode, slateNode) => {
|
|
667
|
+
if (mdastNode && typeof mdastNode === "object" && Array.isArray(slateNode?.__sourceMapPath)) mdastNode.__sourceMapSlatePath = slateNode.__sourceMapPath;
|
|
668
|
+
return mdastNode;
|
|
669
|
+
};
|
|
624
670
|
const buildMdastNode = (node, options, isBlock = false) => {
|
|
625
671
|
const editor = options.editor;
|
|
626
672
|
let key = getPluginKey(editor, node.type) ?? node.type;
|
|
@@ -629,8 +675,8 @@ const buildMdastNode = (node, options, isBlock = false) => {
|
|
|
629
675
|
const nodeParser = getSerializerByKey(key, options);
|
|
630
676
|
if (nodeParser) {
|
|
631
677
|
const mdastNode = nodeParser(node, options);
|
|
632
|
-
if (options.withBlockId && node.id && isBlock) return wrapWithBlockId(mdastNode, node.id);
|
|
633
|
-
return mdastNode;
|
|
678
|
+
if (options.withBlockId && node.id && isBlock) return tagWithSlatePath(wrapWithBlockId(mdastNode, node.id), node);
|
|
679
|
+
return tagWithSlatePath(mdastNode, node);
|
|
634
680
|
}
|
|
635
681
|
unreachable(node);
|
|
636
682
|
};
|
|
@@ -723,6 +769,9 @@ const slateNodeKind = (node, editor) => {
|
|
|
723
769
|
const pType = getPluginType(editor, KEYS.p) ?? KEYS.p;
|
|
724
770
|
if (node.type === pType) return "paragraph";
|
|
725
771
|
if (SEGMENT_SKIP_TYPES.has(key)) return null;
|
|
772
|
+
try {
|
|
773
|
+
if (editor?.api?.isInline?.(node)) return null;
|
|
774
|
+
} catch {}
|
|
726
775
|
if (Array.isArray(node.children)) return "block";
|
|
727
776
|
return null;
|
|
728
777
|
};
|
|
@@ -805,14 +854,23 @@ const attachTableCellSources = (mdastTable, slateTable, editor) => {
|
|
|
805
854
|
* to zip them correctly.
|
|
806
855
|
*/
|
|
807
856
|
const attachListSources = (mdastList, slateListItems, editor, insideMdx) => {
|
|
857
|
+
const slateByPath = /* @__PURE__ */ new Map();
|
|
858
|
+
for (const item of slateListItems) if (Array.isArray(item?.__sourceMapPath)) slateByPath.set(toPathKey(item.__sourceMapPath), item);
|
|
808
859
|
let slateIdx = 0;
|
|
809
860
|
const walkList = (list) => {
|
|
810
861
|
if (!Array.isArray(list.children)) return;
|
|
811
862
|
for (const listItem of list.children) {
|
|
812
863
|
if (listItem.type !== "listItem" || !Array.isArray(listItem.children)) continue;
|
|
813
|
-
for (const child of listItem.children)
|
|
814
|
-
|
|
815
|
-
|
|
864
|
+
for (const child of listItem.children) {
|
|
865
|
+
if (child.type === "list") {
|
|
866
|
+
walkList(child);
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const taggedPath = child.__sourceMapSlatePath;
|
|
870
|
+
let slateChild = null;
|
|
871
|
+
if (Array.isArray(taggedPath)) slateChild = slateByPath.get(toPathKey(taggedPath)) ?? null;
|
|
872
|
+
if (!slateChild && slateIdx < slateListItems.length) slateChild = slateListItems[slateIdx++];
|
|
873
|
+
if (!slateChild) continue;
|
|
816
874
|
const source = buildSource(slateChild, editor);
|
|
817
875
|
if (source) {
|
|
818
876
|
if (insideMdx) source.containsMdx = true;
|
|
@@ -823,35 +881,78 @@ const attachListSources = (mdastList, slateListItems, editor, insideMdx) => {
|
|
|
823
881
|
};
|
|
824
882
|
walkList(mdastList);
|
|
825
883
|
};
|
|
884
|
+
/**
|
|
885
|
+
* Count the number of Slate list-paragraph slots a mdast list tree needs.
|
|
886
|
+
*
|
|
887
|
+
* Each non-`list` child of a `listItem` corresponds to exactly one Slate
|
|
888
|
+
* list paragraph; nested `list` nodes recurse. This lets us consume exactly
|
|
889
|
+
* the right slice of consecutive Slate list paragraphs for a given mdast
|
|
890
|
+
* list, instead of greedily swallowing every list-styled Slate sibling.
|
|
891
|
+
*/
|
|
892
|
+
const countMdastListSlots = (list) => {
|
|
893
|
+
if (!list || !Array.isArray(list.children)) return 0;
|
|
894
|
+
let count = 0;
|
|
895
|
+
for (const listItem of list.children) {
|
|
896
|
+
if (listItem.type !== "listItem" || !Array.isArray(listItem.children)) continue;
|
|
897
|
+
for (const child of listItem.children) if (child?.type === "list") count += countMdastListSlots(child);
|
|
898
|
+
else count += 1;
|
|
899
|
+
}
|
|
900
|
+
return count;
|
|
901
|
+
};
|
|
902
|
+
const pathsEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
826
903
|
const attachDescendantSources = (mdastNode, slateNode, editor, insideMdx) => {
|
|
827
904
|
if (!insideMdx && !MDX_MDAST_TYPES.has(mdastNode?.type ?? "")) return;
|
|
828
905
|
const slateChildren = slateNode?.children;
|
|
829
906
|
const mdastChildren = mdastNode?.children;
|
|
830
907
|
if (!Array.isArray(slateChildren) || !Array.isArray(mdastChildren)) return;
|
|
831
908
|
const slateElements = slateChildren.filter((c) => c?.type && Array.isArray(c.children));
|
|
832
|
-
const
|
|
909
|
+
const consumedSlateIndices = /* @__PURE__ */ new Set();
|
|
910
|
+
const attachSingleByMdast = (mdastChild, slateChild) => {
|
|
911
|
+
const isMdx = insideMdx || MDX_MDAST_TYPES.has(mdastChild.type ?? "");
|
|
912
|
+
const source = buildSource(slateChild, editor);
|
|
913
|
+
if (source) {
|
|
914
|
+
if (isMdx) source.containsMdx = true;
|
|
915
|
+
attachSource(mdastChild, source);
|
|
916
|
+
}
|
|
917
|
+
if (mdastChild.type === "table") attachTableCellSources(mdastChild, slateChild, editor);
|
|
918
|
+
else if (insideMdx || MDX_MDAST_TYPES.has(mdastChild.type ?? "")) attachDescendantSources(mdastChild, slateChild, editor, isMdx);
|
|
919
|
+
};
|
|
920
|
+
for (const mdastChild of mdastChildren) {
|
|
921
|
+
if (!mdastChild || !mdastChild.type || mdastChild.type === "text") continue;
|
|
922
|
+
const taggedPath = mdastChild.__sourceMapSlatePath;
|
|
923
|
+
if (!Array.isArray(taggedPath)) continue;
|
|
924
|
+
const slateIdx = slateElements.findIndex((c) => Array.isArray(c.__sourceMapPath) && pathsEqual(c.__sourceMapPath, taggedPath));
|
|
925
|
+
if (slateIdx === -1) continue;
|
|
926
|
+
consumedSlateIndices.add(slateIdx);
|
|
927
|
+
attachSingleByMdast(mdastChild, slateElements[slateIdx]);
|
|
928
|
+
}
|
|
833
929
|
let si = 0;
|
|
834
|
-
|
|
930
|
+
const advanceToUnconsumed = () => {
|
|
931
|
+
while (si < slateElements.length && consumedSlateIndices.has(si)) si++;
|
|
932
|
+
};
|
|
933
|
+
for (const mdastChild of mdastChildren) {
|
|
934
|
+
if (!mdastChild || !mdastChild.type || mdastChild.type === "text") continue;
|
|
935
|
+
if (Array.isArray(mdastChild.__sourceMapSlatePath)) continue;
|
|
835
936
|
if (mdastChild.type === "list") {
|
|
937
|
+
const slotsNeeded = countMdastListSlots(mdastChild);
|
|
836
938
|
const listRun = [];
|
|
837
|
-
|
|
939
|
+
for (let k = 0; k < slotsNeeded; k++) {
|
|
940
|
+
advanceToUnconsumed();
|
|
941
|
+
if (si >= slateElements.length) break;
|
|
942
|
+
if (!slateElements[si].listStyleType) break;
|
|
838
943
|
listRun.push(slateElements[si]);
|
|
944
|
+
consumedSlateIndices.add(si);
|
|
839
945
|
si++;
|
|
840
946
|
}
|
|
841
947
|
attachListSources(mdastChild, listRun, editor, insideMdx);
|
|
842
948
|
continue;
|
|
843
949
|
}
|
|
950
|
+
advanceToUnconsumed();
|
|
844
951
|
if (si >= slateElements.length) break;
|
|
845
952
|
const slateChild = slateElements[si];
|
|
953
|
+
consumedSlateIndices.add(si);
|
|
846
954
|
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);
|
|
955
|
+
attachSingleByMdast(mdastChild, slateChild);
|
|
855
956
|
}
|
|
856
957
|
};
|
|
857
958
|
const buildMdastNodeWithSource = (node, options, isBlock = false) => {
|
|
@@ -1048,7 +1149,7 @@ const serializeMdWithSourceMap = (editor, options) => {
|
|
|
1048
1149
|
const startLine = info.now.line;
|
|
1049
1150
|
const emitted = handler(node, parent, state, info);
|
|
1050
1151
|
const source = node?.data?.sourceMap;
|
|
1051
|
-
if (source && emitted.trim()) pushSegment(source, emitted, startLine, startLine + (emitted.split(/\r?\n|\r/g).length - 1));
|
|
1152
|
+
if (source && emitted.trim()) pushSegment(source, emitted, startLine, startLine + (emitted.replace(/(?:\r?\n|\r)+$/g, "").split(/\r?\n|\r/g).length - 1));
|
|
1052
1153
|
if (type === "table" && Array.isArray(node?.children)) for (let r = 0; r < node.children.length; r++) {
|
|
1053
1154
|
const line = r === 0 ? startLine : startLine + 1 + r;
|
|
1054
1155
|
const cells = node.children[r]?.children;
|
|
@@ -1086,14 +1187,44 @@ const serializeMdWithSourceMap = (editor, options) => {
|
|
|
1086
1187
|
const MDX_TAG_RE = /<[a-zA-Z_][\w.-]*/;
|
|
1087
1188
|
/** `a` is an ancestor-or-self of `b`: a's path is a prefix of (or equal to) b's. */
|
|
1088
1189
|
const isAncestorOrSelf = (a, b) => a.length <= b.length && a.every((v, i) => b[i] === v);
|
|
1190
|
+
/**
|
|
1191
|
+
* Order two Slate selection points in document order.
|
|
1192
|
+
*
|
|
1193
|
+
* Slate selection points come in two flavours that previously got compared
|
|
1194
|
+
* incompatibly:
|
|
1195
|
+
*
|
|
1196
|
+
* - *Element-level*: `path` ends at an element, `offset` is the boundary
|
|
1197
|
+
* index between its children (0 = before the first child, N = after the
|
|
1198
|
+
* N-th child).
|
|
1199
|
+
* - *Leaf-level*: `path` descends into a text leaf, `offset` is a character
|
|
1200
|
+
* index within that text.
|
|
1201
|
+
*
|
|
1202
|
+
* The previous implementation padded the shorter path with `0`s and then
|
|
1203
|
+
* fell back to comparing offsets — which only works when both points are
|
|
1204
|
+
* at the same depth. For mixed-depth points like `[3] offset 2`
|
|
1205
|
+
* (≈ "before child 2 of [3]") vs `[3, 1] offset 5` (≈ "inside child 1 of
|
|
1206
|
+
* [3]"), it returned the wrong order silently because the offsets are not
|
|
1207
|
+
* commensurate.
|
|
1208
|
+
*
|
|
1209
|
+
* Fix: when the shorter point is element-level, expand it to the leaf-level
|
|
1210
|
+
* coordinate space by treating its offset as the next path step ("before
|
|
1211
|
+
* child `offset`"), then continue path comparison. Offsets are only
|
|
1212
|
+
* compared when both points are at identical depth.
|
|
1213
|
+
*/
|
|
1089
1214
|
const comparePoints = (a, b) => {
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1215
|
+
const aPath = a.path;
|
|
1216
|
+
const bPath = b.path;
|
|
1217
|
+
const aLen = aPath.length;
|
|
1218
|
+
const bLen = bPath.length;
|
|
1219
|
+
const common = Math.min(aLen, bLen);
|
|
1220
|
+
for (let i = 0; i < common; i++) if (aPath[i] !== bPath[i]) return aPath[i] - bPath[i];
|
|
1221
|
+
if (aLen === bLen) return a.offset - b.offset;
|
|
1222
|
+
if (aLen < bLen) {
|
|
1223
|
+
if (a.offset !== bPath[common]) return a.offset - bPath[common];
|
|
1224
|
+
return -1;
|
|
1095
1225
|
}
|
|
1096
|
-
|
|
1226
|
+
if (aPath[common] !== b.offset) return aPath[common] - b.offset;
|
|
1227
|
+
return 1;
|
|
1097
1228
|
};
|
|
1098
1229
|
/**
|
|
1099
1230
|
* Find the segment covering the start of a selection at `path`.
|
|
@@ -1115,8 +1246,12 @@ const findStartSegment = (segments, path) => {
|
|
|
1115
1246
|
/**
|
|
1116
1247
|
* Find the segment covering the end of a selection at `path`.
|
|
1117
1248
|
*
|
|
1118
|
-
* Same as findStartSegment but for descendant matches picks the
|
|
1119
|
-
* (the container's
|
|
1249
|
+
* Same as findStartSegment but for descendant matches picks the segment
|
|
1250
|
+
* with the LARGEST `endLine` (the container's deepest/latest child
|
|
1251
|
+
* segment). The previous implementation returned the last segment by
|
|
1252
|
+
* iteration order, which only happened to work because `allSegments` is
|
|
1253
|
+
* sorted by path today — any caller passing an unsorted array would
|
|
1254
|
+
* silently get the wrong range end.
|
|
1120
1255
|
*/
|
|
1121
1256
|
const findEndSegment = (segments, path) => {
|
|
1122
1257
|
let best = null;
|
|
@@ -1126,9 +1261,12 @@ const findEndSegment = (segments, path) => {
|
|
|
1126
1261
|
bestLen = seg.path.length;
|
|
1127
1262
|
}
|
|
1128
1263
|
if (best) return best;
|
|
1129
|
-
let
|
|
1130
|
-
for (const seg of segments)
|
|
1131
|
-
|
|
1264
|
+
let latest = null;
|
|
1265
|
+
for (const seg of segments) {
|
|
1266
|
+
if (!isAncestorOrSelf(path, seg.path)) continue;
|
|
1267
|
+
if (!latest || seg.endLine > latest.endLine) latest = seg;
|
|
1268
|
+
}
|
|
1269
|
+
return latest;
|
|
1132
1270
|
};
|
|
1133
1271
|
/** Recursively compute the total text length of a Slate node. */
|
|
1134
1272
|
const getTextLength = (node) => {
|
|
@@ -1228,13 +1366,14 @@ const tryContainerExpand = ({ allSegments, leafSeg, selPath, currentLine, direct
|
|
|
1228
1366
|
const resolveSelectionByPath = (editor, selection, options) => {
|
|
1229
1367
|
const { allSegments, markdown, segments } = serializeMdWithSourceMap(editor, options);
|
|
1230
1368
|
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);
|
|
1369
|
+
const startSeg = findStartSegment(segments, start.path) ?? findStartSegment(allSegments, start.path);
|
|
1370
|
+
const endSeg = findEndSegment(segments, end.path) ?? findEndSegment(allSegments, end.path);
|
|
1233
1371
|
if (!startSeg && !endSeg) return {
|
|
1234
1372
|
containsMdx: false,
|
|
1235
1373
|
endLine: 1,
|
|
1236
1374
|
extractedMarkdown: "",
|
|
1237
1375
|
markdown,
|
|
1376
|
+
resolved: false,
|
|
1238
1377
|
segments,
|
|
1239
1378
|
startLine: 1
|
|
1240
1379
|
};
|
|
@@ -1269,6 +1408,7 @@ const resolveSelectionByPath = (editor, selection, options) => {
|
|
|
1269
1408
|
endLine,
|
|
1270
1409
|
extractedMarkdown,
|
|
1271
1410
|
markdown,
|
|
1411
|
+
resolved: true,
|
|
1272
1412
|
segments,
|
|
1273
1413
|
startLine
|
|
1274
1414
|
};
|
|
@@ -2618,4 +2758,4 @@ const remarkMention = () => (tree) => {
|
|
|
2618
2758
|
};
|
|
2619
2759
|
|
|
2620
2760
|
//#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 };
|
|
2761
|
+
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.1.
|
|
3
|
+
"version": "53.1.4",
|
|
4
4
|
"description": "Markdown serializer plugin for Plate",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"remark-gfm": "4.0.1",
|
|
53
53
|
"remark-math": "6.0.0",
|
|
54
54
|
"@plate/scripts": "1.0.0",
|
|
55
|
-
"
|
|
56
|
-
"platejs": "npm:@lofcz/platejs@53.
|
|
55
|
+
"platejs": "npm:@lofcz/platejs@53.1.1",
|
|
56
|
+
"@platejs/footnote": "npm:@lofcz/platejs-footnote@53.0.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"platejs": ">=53.0.0",
|