@meowdown/core 0.79.0 → 0.80.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +12 -28
  2. package/dist/index.js +126 -121
  3. package/package.json +4 -3
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ import { Command, EditorState, PluginKey } from "@prosekit/pm/state";
7
7
  import { EditorView } from "@prosekit/pm/view";
8
8
  import { EditorNode, Mark, ProseMirrorNode } from "@prosekit/pm/model";
9
9
  import { ListAttrs } from "@prosekit/extensions/list";
10
- import { HorizontalRuleExtension } from "@prosekit/extensions/horizontal-rule";
10
+ import { HorizontalRuleCommandsExtension, HorizontalRuleSpecExtension } from "@prosekit/extensions/horizontal-rule";
11
11
  import { Resolver, XPostMediaClickEvent } from "@meowdown/embed/x";
12
12
  import { YouTubeVideoClickEvent } from "@meowdown/embed/youtube";
13
13
  import "prosemirror-flat-list";
@@ -117,7 +117,7 @@ type HorizontalRuleMarkerExtension = Extension<{
117
117
  horizontalRule: MeowdownHorizontalRuleAttrs;
118
118
  };
119
119
  }>;
120
- type MeowdownHorizontalRuleExtension = Union<[HorizontalRuleExtension, HorizontalRuleMarkerExtension]>;
120
+ type MeowdownHorizontalRuleExtension = Union<[HorizontalRuleSpecExtension, HorizontalRuleCommandsExtension, HorizontalRuleMarkerExtension]>;
121
121
  //#endregion
122
122
  //#region src/extensions/html-comment.d.ts
123
123
  interface MeowdownHTMLCommentAttrs {
@@ -1101,6 +1101,13 @@ interface EditorConfig extends InlineMarkOptions, FollowLinkHandlers, FilePasteO
1101
1101
  bulletAfterHeading?: boolean;
1102
1102
  backspaceDeletesEmptyFirstBlock?: boolean;
1103
1103
  substitution?: boolean;
1104
+ /**
1105
+ * Keep the document to one paragraph of inline Markdown: typed block
1106
+ * prefixes (`# `, `> `, `- `, ```` ``` ````) stay literal and pasted blocks
1107
+ * flatten into soft lines. Seed the editor with `paragraphMarkdownToDoc` and
1108
+ * read it with `docToParagraphMarkdown`.
1109
+ */
1110
+ singleParagraph?: boolean;
1104
1111
  wikilinkEnabled?: boolean;
1105
1112
  placeholder?: PlaceholderOptions$1['placeholder'];
1106
1113
  readOnly?: boolean;
@@ -1123,7 +1130,7 @@ declare function defineEditorExtensionImpl(options: EditorExtensionOptions): imp
1123
1130
  frontmatter?: Frontmatter;
1124
1131
  };
1125
1132
  };
1126
- }>, import("@prosekit/extensions/text").TextExtension, import("@prosekit/extensions/blockquote").BlockquoteExtension, import("@prosekit/core").Union<readonly [import("@prosekit/extensions/list").ListSpecExtension, import("@prosekit/core").PlainExtension, import("@prosekit/extensions/list").ListCommandsExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Extension<{
1133
+ }>, import("@prosekit/extensions/text").TextExtension, import("@prosekit/core").Union<readonly [import("@prosekit/extensions/blockquote").BlockquoteSpecExtension, import("@prosekit/extensions/blockquote").BlockquoteCommandsExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension]>, import("@prosekit/core").Union<readonly [import("@prosekit/extensions/list").ListSpecExtension, import("@prosekit/core").PlainExtension, import("@prosekit/extensions/list").ListCommandsExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Extension<{
1127
1134
  Nodes: {
1128
1135
  list: {
1129
1136
  marker?: ListMarker;
@@ -1176,7 +1183,7 @@ declare function defineEditorExtensionImpl(options: EditorExtensionOptions): imp
1176
1183
  Nodes: {
1177
1184
  tableHeaderCell: import("@prosekit/pm/model").Attrs;
1178
1185
  };
1179
- }>]>, TableColumnAlignExtension, import("@prosekit/core").PlainExtension, import("@prosekit/extensions/table").TableCommandsExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension]>, import("@prosekit/core").Union<readonly [import("@prosekit/extensions/code-block").CodeBlockExtension, import("@prosekit/core").Extension<{
1186
+ }>]>, TableColumnAlignExtension, import("@prosekit/core").PlainExtension, import("@prosekit/extensions/table").TableCommandsExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension]>, import("@prosekit/core").Union<readonly [import("@prosekit/extensions/code-block").CodeBlockSpecExtension, import("@prosekit/core").PlainExtension, import("@prosekit/extensions/code-block").CodeBlockCommandsExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Extension<{
1180
1187
  Nodes: {
1181
1188
  codeBlock: {
1182
1189
  fenceStyle?: CodeBlockFenceStyle | null;
@@ -1256,7 +1263,7 @@ declare function defineEditorExtensionImpl(options: EditorExtensionOptions): imp
1256
1263
  Commands: {
1257
1264
  insertSoftBreak: [];
1258
1265
  };
1259
- }>, import("@prosekit/core").PlainExtension]>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Union<readonly [import("@prosekit/core").Extension<{
1266
+ }>, import("@prosekit/core").PlainExtension]>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Union<readonly [import("@prosekit/core").Extension<{
1260
1267
  Commands: {
1261
1268
  toggleEm: [];
1262
1269
  toggleStrong: [];
@@ -1681,27 +1688,4 @@ export declare function paragraphMarkdownToDoc(markdown: string, nodes?: TypedNo
1681
1688
  */
1682
1689
  export declare function docToParagraphMarkdown(doc: ProseMirrorNode): string;
1683
1690
  //#endregion
1684
- //#region src/extensions/single-paragraph.d.ts
1685
- /**
1686
- * Keep editing and pasted content within one paragraph.
1687
- * Opt into this extension for inline Markdown fields such as task paragraphs.
1688
- * Register it alongside `defineMeowdown`, seed content with
1689
- * `paragraphMarkdownToDoc`, and read edits with `docToParagraphMarkdown`.
1690
- * Ordinary note editors should keep their full document behavior.
1691
- *
1692
- * Text input stays literal, before block input rules can consume prefixes.
1693
- * Transactions and pasted blocks flatten to paragraph text separated by soft
1694
- * lines. Reference definitions come from `referenceDefinitions` in the editor
1695
- * config; definition-looking content in the field remains visible literal text.
1696
- * Enter/submit, focus, and persistence remain the caller's responsibility.
1697
- *
1698
- * @example
1699
- * const editor = createEditor({
1700
- * extension: union(defineMeowdown({ referenceDefinitions }), defineSingleParagraph()),
1701
- * defaultContent: paragraphMarkdownToDoc('Buy **milk**'),
1702
- * })
1703
- * const markdown = docToParagraphMarkdown(editor.state.doc)
1704
- */
1705
- export declare function defineSingleParagraph(): PlainExtension;
1706
- //#endregion
1707
1691
  export { type AcceptPendingReplacementOptions, type CheckRoundTripOptions, type CodeBlockAttrs, type CodeBlockFenceStyle, type CodeToken, type DocToMarkdownOptions, type EditorConfig, type EditorExtension, type EditorExtensionOptions, type ExitBoundaryHandler, type ExitBoundaryOptions, type FileClickHandler, type FileClickPayload, type FileInfo, type FileInfoResolver, type FileLinkOptions, type FileLinkPayload, type FileLinkResolver, type FilePasteHandler, type FilePasteOptions, type FileSaveErrorHandler, type FileViewOptions, type FollowLinkHandlers, type ImageClickHandler, type ImageClickPayload, type ImageOptions, type ImageUrlResolver, type InlineMarkContext, type InlineMarkOptions, type KaTeXRender, type LanguageItem, type LinkAttrs, type LinkClickHandler, type LinkClickPayload, type LinkCopyHandler, type LinkCopyPayload, type LinkEditHandler, type LinkEditOptions, type LinkHoverHandler, type LinkHoverOptions, type LinkPreview, type LinkPreviewResolver, type LinkUnit, type ListMarker, type MarkChunk, type MarkMode, type MarkName, type MarkdownToDocOptions, type MdFileAttrs, type MdImageAttrs, type MdLinkTextAttrs, type MdMathAttrs, type MdWikilinkAttrs, type MeowdownCodeBlockAttrs, type MeowdownHTMLCommentAttrs, type MeowdownListAttrs, type MeowdownTableCellAttrs, type NodeName, type ParsedWikiEmbed, type PendingReplacement, type PendingReplacementEvent, type PendingReplacementHandler, type PendingReplacementMode, type PendingReplacementOutcome, type PlaceholderOptions, type PositionRange, type PostEmbedSnapshot, Priority, type ReferenceDefinition, type ReferenceDefinitionIndex, type ReferenceDefinitions, type RoundTripFidelity, type SearchStatus, type SearchStatusHandler, type StartPendingReplacementOptions, type TableColumnAlign, type TagClickHandler, type TagClickPayload, type TypedEditor, type TypedMarkBuilders, type VirtualElement, type WikiEmbedOptions, type WikiEmbedResolution, type WikiEmbedResolver, type WikilinkClickHandler, type WikilinkClickPayload, type WikilinkHoverHandler, type WikilinkHoverHit, type WikilinkOptions, type WikilinkPayload, type WikilinkResolution, type WikilinkResolver, type XPostMediaClickHandler, type XPostResolver, type YouTubeVideoClickHandler, type YouTubeVideoResolver, checkRoundTrip, defineCodeBlockPreviewPlugin, definePlaceholder, defineSearchStatusHandler, getSearchStatus, isCodeBlockPreviewHiddenDecoration, withPriority };
package/dist/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import { LEZER_NODE_IDS, checkRoundTrip, collectInlineElements, getAutolinkHref, gfmParser, isSpaceChar, matchEmbed, parseInline, parseMarkdownAst, parseXPostId, serializeMarkdownAst } from "@meowdown/markdown";
2
2
  import { Priority, Priority as Priority$1, createMarkBuilders, createNodeBuilders, defaultBlockAt, defineBaseCommands, defineBaseKeymap, defineClipboardSerializer, defineCommands, defineHistory, defineKeymap, defineMarkSpec, defineMarkView, defineNodeAttr, defineNodeSpec, definePlugin, findMarkRange, getMarkRange, getMarkType, getNodeType, isAllSelection, isApple, isAtBlockStart, isNodeSelection, isTextSelection, setBlockType, toggleNode, union, unsetBlockType, withPriority, withPriority as withPriority$1, withSkipCodeBlock } from "@prosekit/core";
3
- import { defineCodeBlock, defineCodeBlockHighlight, defineCodeBlockPreviewPlugin, isCodeBlockPreviewHiddenDecoration } from "@prosekit/extensions/code-block";
3
+ import { codeBlockEnterRule, codeBlockInputRule, defineCodeBlockCommands, defineCodeBlockHighlight, defineCodeBlockKeymap, defineCodeBlockPreviewPlugin, defineCodeBlockSpec, isCodeBlockPreviewHiddenDecoration } from "@prosekit/extensions/code-block";
4
4
  import { definePlaceholder, definePlaceholder as definePlaceholder$1 } from "@prosekit/extensions/placeholder";
5
5
  import { defineSearchCommands, defineSearchQuery, defineSearchStatusHandler, getSearchStatus, getSearchStatus as getSearchStatus$1 } from "@prosekit/extensions/search";
6
6
  import { isElementLike, isHTMLElement, isObject, once } from "@ocavue/utils";
7
- import { defineBlockquote } from "@prosekit/extensions/blockquote";
8
7
  import { defineDoc } from "@prosekit/extensions/doc";
9
8
  import { defineGapCursor } from "@prosekit/extensions/gap-cursor";
10
9
  import { defineModClickPrevention } from "@prosekit/extensions/mod-click-prevention";
@@ -12,6 +11,11 @@ import { defineText } from "@prosekit/extensions/text";
12
11
  import { defineVirtualSelection } from "@prosekit/extensions/virtual-selection";
13
12
  import { NodeSelection, Plugin, PluginKey, Selection, TextSelection } from "@prosekit/pm/state";
14
13
  import { Decoration, DecorationSet } from "@prosekit/pm/view";
14
+ import { blockquoteInputRule, defineBlockquoteCommands, defineBlockquoteKeymap, defineBlockquoteSpec } from "@prosekit/extensions/blockquote";
15
+ import { createTextBlockInputRuleHandler, createWrappingInputRuleHandler, defineInputRule } from "@prosekit/extensions/input-rule";
16
+ import { defineEnterRule } from "@prosekit/extensions/enter-rule";
17
+ import { InputRule } from "@prosekit/pm/inputrules";
18
+ import { createTextBlockEnterRule } from "prosemirror-enter-rules";
15
19
  import { NO_BREAK_SPACE } from "unicode-by-name";
16
20
  import { defaultHandlers } from "hast-util-to-mdast";
17
21
  import { defaultHandlers as defaultHandlers$1 } from "mdast-util-to-markdown";
@@ -21,19 +25,17 @@ import remarkGfm from "remark-gfm";
21
25
  import remarkStringify from "remark-stringify";
22
26
  import { unified } from "unified";
23
27
  import { DOMParser, DOMSerializer, Fragment, Mark, Slice } from "@prosekit/pm/model";
24
- import { defineHeadingCommands, defineHeadingInputRule, defineHeadingSpec } from "@prosekit/extensions/heading";
28
+ import { defineHeadingCommands, defineHeadingSpec, headingInputRule } from "@prosekit/extensions/heading";
25
29
  import { defineParagraphCommands, defineParagraphKeymap } from "@prosekit/extensions/paragraph";
26
30
  import { LanguageDescription } from "@codemirror/language";
27
31
  import { languages } from "@codemirror/language-data";
28
32
  import { classHighlighter, highlightTree } from "@lezer/highlight";
29
33
  import { createParser } from "prosemirror-highlight/lezer";
30
- import { defineEnterRule, defineTextBlockEnterRule } from "@prosekit/extensions/enter-rule";
31
- import { defineInputRule, defineTextBlockInputRule } from "@prosekit/extensions/input-rule";
32
34
  import { triggerAutocomplete } from "@prosekit/extensions/autocomplete";
33
35
  import { defineListCommands, defineListDropIndicator, defineListSpec, moveList, toggleList, unwrapList, wrapInList } from "@prosekit/extensions/list";
34
36
  import { chainCommands, deleteSelection, lift, newlineInCode, splitBlock } from "@prosekit/pm/commands";
35
37
  import { closeHistory } from "@prosekit/pm/history";
36
- import { defineHorizontalRule } from "@prosekit/extensions/horizontal-rule";
38
+ import { defineHorizontalRuleCommands, defineHorizontalRuleSpec, horizontalRuleInputRule } from "@prosekit/extensions/horizontal-rule";
37
39
  import { X_POST_MEDIA_CLICK, registerXPost } from "@meowdown/embed/x";
38
40
  import { YOUTUBE_VIDEO_CLICK, registerYouTubeVideo } from "@meowdown/embed/youtube";
39
41
  import { registerResizableHandleElement, registerResizableRootElement } from "@prosekit/web/resizable";
@@ -44,9 +46,8 @@ import * as v from "valibot";
44
46
  import { AttrStep, ReplaceStep, Step, StepResult, Transform } from "@prosekit/pm/transform";
45
47
  import { decodeString } from "micromark-util-decode-string";
46
48
  import { normalizeIdentifier } from "micromark-util-normalize-identifier";
47
- import { createDedentListCommand, createIndentListCommand, createListRenderingPlugin, createSafariInputMethodWorkaroundPlugin, createSplitListCommand, createToggleCollapsedCommand, defaultAttributesGetter, deleteCommand, enterCommand, findCheckboxInListItem, handleListMarkerMouseDown, joinCollapsedListBackward, joinListElements, joinListUp, listToDOM, protectCollapsed, unwrapListSlice, wrappingListInputRule } from "prosemirror-flat-list";
49
+ import { bulletListInputRule, createDedentListCommand, createIndentListCommand, createListInputRuleHandler, createListRenderingPlugin, createSafariInputMethodWorkaroundPlugin, createSplitListCommand, createToggleCollapsedCommand, defaultAttributesGetter, deleteCommand, enterCommand, findCheckboxInListItem, handleListMarkerMouseDown, joinCollapsedListBackward, joinListElements, joinListUp, listToDOM, orderedListInputRule, protectCollapsed, taskListInputRule, unwrapListSlice } from "prosemirror-flat-list";
48
50
  import { defineTableCellSpec, defineTableCommands, defineTableDropIndicator, defineTableEditingPlugin, defineTableHeaderCellSpec, defineTableRowSpec, defineTableSpec, deleteTable, isCellSelection } from "@prosekit/extensions/table";
49
- import { InputRule } from "@prosekit/pm/inputrules";
50
51
 
51
52
  //#region src/utils/composition.ts
52
53
  const COMPOSITION_TAIL_MS = 50;
@@ -661,6 +662,97 @@ function defineBackspaceEmptyFirstBlock(enabled) {
661
662
  } }), Priority$1.lowest);
662
663
  }
663
664
 
665
+ //#endregion
666
+ //#region src/converters/paragraph.ts
667
+ /**
668
+ * Build one paragraph without interpreting block-opening Markdown.
669
+ */
670
+ function paragraphMarkdownToDoc(markdown, nodes = getNodeBuilders()) {
671
+ return astToDoc({
672
+ type: "document",
673
+ children: [{
674
+ type: "paragraph",
675
+ value: markdown.replaceAll(/\r\n?/g, "\n").replaceAll(/\n[ \t]*\n+/g, "\n")
676
+ }]
677
+ }, nodes);
678
+ }
679
+ /**
680
+ * Flatten textblocks into one paragraph's source content.
681
+ */
682
+ function docToParagraphMarkdown(doc) {
683
+ const paragraphs = [];
684
+ doc.descendants((node) => {
685
+ if (!node.isTextblock) return true;
686
+ paragraphs.push(node.textContent);
687
+ return false;
688
+ });
689
+ return paragraphs.join("\n").replaceAll(/\n[ \t]*\n+/g, "\n");
690
+ }
691
+
692
+ //#endregion
693
+ //#region src/extensions/single-paragraph.ts
694
+ const singleParagraphPluginKey = new PluginKey("single-paragraph");
695
+ /**
696
+ * Whether the editor holds one paragraph of inline Markdown (the
697
+ * `singleParagraph` editor config option).
698
+ */
699
+ function isSingleParagraph(state) {
700
+ return !!getEditorConfig(state).singleParagraph;
701
+ }
702
+ /**
703
+ * Keep the document to one paragraph while `singleParagraph` is set. Input
704
+ * rules and enter rules that open a block stay inert on their own (see
705
+ * `block-rule.ts`); this flattens what still arrives as blocks, such as a
706
+ * paste or a command, into paragraph text separated by soft lines.
707
+ */
708
+ function defineSingleParagraph() {
709
+ return withPriority$1(definePlugin(new Plugin({
710
+ key: singleParagraphPluginKey,
711
+ appendTransaction(transactions, _oldState, state) {
712
+ if (!isSingleParagraph(state)) return;
713
+ if (!transactions.some((transaction) => transaction.docChanged)) return;
714
+ const markdown = docToParagraphMarkdown(state.doc);
715
+ if (state.doc.childCount === 1 && isNodeOfType(state.doc.child(0), "paragraph") && state.doc.child(0).textContent === markdown) return;
716
+ const doc = paragraphMarkdownToDoc(markdown, getNodeBuildersForSchema(state.schema));
717
+ const position = Math.min(state.selection.head, doc.content.size - 1);
718
+ const transaction = state.tr.replaceWith(0, state.doc.content.size, doc.content);
719
+ return transaction.setSelection(TextSelection.create(transaction.doc, Math.max(1, position)));
720
+ }
721
+ })), Priority$1.highest);
722
+ }
723
+
724
+ //#endregion
725
+ //#region src/extensions/block-rule.ts
726
+ /**
727
+ * An input rule whose match turns the textblock into, or wraps it in, a block
728
+ * node. In a single paragraph the match is left as typed text: there is no
729
+ * block for it to open.
730
+ */
731
+ function defineBlockInputRule(regex, handler) {
732
+ return defineInputRule(new InputRule(regex, (state, match, start, end) => {
733
+ return isSingleParagraph(state) ? null : handler(state, match, start, end);
734
+ }));
735
+ }
736
+ /**
737
+ * An enter rule whose match turns the textblock into a block node. In a single
738
+ * paragraph the match is left as typed text, like {@link defineBlockInputRule}.
739
+ */
740
+ function defineBlockEnterRule(options) {
741
+ const rule = createTextBlockEnterRule(options);
742
+ return defineEnterRule({
743
+ ...rule,
744
+ handler: (handlerOptions) => {
745
+ return isSingleParagraph(handlerOptions.state) ? null : rule.handler(handlerOptions);
746
+ }
747
+ });
748
+ }
749
+
750
+ //#endregion
751
+ //#region src/extensions/blockquote.ts
752
+ function defineMeowdownBlockquote() {
753
+ return union(defineBlockquoteSpec(), defineBlockquoteCommands(), defineBlockquoteKeymap(), defineBlockInputRule(blockquoteInputRule.regex, createWrappingInputRuleHandler(blockquoteInputRule)));
754
+ }
755
+
664
756
  //#endregion
665
757
  //#region src/extensions/bullet-after-heading.ts
666
758
  /**
@@ -1124,7 +1216,7 @@ function defineHeadingKeymap() {
1124
1216
  });
1125
1217
  }
1126
1218
  function defineHeading() {
1127
- return union(defineHeadingSpec(), defineHeadingWhitespace(), defineSetextUnderlineAttr(), defineHeadingClosingHashesAttr(), defineHeadingInputRule(), defineHeadingCommands(), defineHeadingKeymap());
1219
+ return union(defineHeadingSpec(), defineHeadingWhitespace(), defineSetextUnderlineAttr(), defineHeadingClosingHashesAttr(), defineBlockInputRule(headingInputRule.regex, createTextBlockInputRuleHandler(headingInputRule)), defineHeadingCommands(), defineHeadingKeymap());
1128
1220
  }
1129
1221
 
1130
1222
  //#endregion
@@ -1845,22 +1937,20 @@ function getTildeFenceAttrs(match) {
1845
1937
  fenceStyle: "tilde"
1846
1938
  };
1847
1939
  }
1848
- function defineTildeFenceInputRule() {
1849
- return defineTextBlockInputRule({
1850
- regex: /^~~~(\S*)\s$/,
1851
- type: "codeBlock",
1852
- attrs: getTildeFenceAttrs
1853
- });
1854
- }
1940
+ const tildeFenceInputRule = {
1941
+ regex: /^~~~(\S*)\s$/,
1942
+ type: "codeBlock",
1943
+ attrs: getTildeFenceAttrs
1944
+ };
1855
1945
  function defineTildeFenceEnterRule() {
1856
- return defineTextBlockEnterRule({
1946
+ return defineBlockEnterRule({
1857
1947
  regex: /^~~~(\S*)$/,
1858
1948
  type: "codeBlock",
1859
1949
  attrs: getTildeFenceAttrs
1860
1950
  });
1861
1951
  }
1862
1952
  function defineDollarFenceEnterRule() {
1863
- return defineTextBlockEnterRule({
1953
+ return defineBlockEnterRule({
1864
1954
  regex: /^\$\$$/,
1865
1955
  type: "codeBlock",
1866
1956
  attrs: () => ({
@@ -1869,8 +1959,8 @@ function defineDollarFenceEnterRule() {
1869
1959
  })
1870
1960
  });
1871
1961
  }
1872
- function defineCodeBlock$1() {
1873
- return union(defineCodeBlock(), defineFenceStyleAttr(), defineFenceLengthAttr(), defineTildeFenceInputRule(), defineTildeFenceEnterRule(), defineDollarFenceEnterRule(), defineCodeBlockExitKeymap());
1962
+ function defineCodeBlock() {
1963
+ return union(defineCodeBlockSpec(), defineCodeBlockKeymap(), defineCodeBlockCommands(), defineBlockInputRule(codeBlockInputRule.regex, createTextBlockInputRuleHandler(codeBlockInputRule)), defineBlockEnterRule(codeBlockEnterRule), defineFenceStyleAttr(), defineFenceLengthAttr(), defineBlockInputRule(tildeFenceInputRule.regex, createTextBlockInputRuleHandler(tildeFenceInputRule)), defineTildeFenceEnterRule(), defineDollarFenceEnterRule(), defineCodeBlockExitKeymap());
1874
1964
  }
1875
1965
 
1876
1966
  //#endregion
@@ -3067,7 +3157,7 @@ function defineHorizontalRuleMarkerAttr() {
3067
3157
  });
3068
3158
  }
3069
3159
  function defineMeowdownHorizontalRule() {
3070
- return union(defineHorizontalRule(), defineHorizontalRuleMarkerAttr());
3160
+ return union(defineHorizontalRuleSpec(), defineHorizontalRuleCommands(), defineHorizontalRuleMarkerAttr(), defineBlockInputRule(horizontalRuleInputRule.regex, horizontalRuleInputRule.handler));
3071
3161
  }
3072
3162
 
3073
3163
  //#endregion
@@ -4550,75 +4640,6 @@ function emit(out, from, to, marks) {
4550
4640
  ]);
4551
4641
  }
4552
4642
 
4553
- //#endregion
4554
- //#region src/converters/paragraph.ts
4555
- /**
4556
- * Build one paragraph without interpreting block-opening Markdown.
4557
- */
4558
- function paragraphMarkdownToDoc(markdown, nodes = getNodeBuilders()) {
4559
- return astToDoc({
4560
- type: "document",
4561
- children: [{
4562
- type: "paragraph",
4563
- value: markdown.replaceAll(/\r\n?/g, "\n").replaceAll(/\n[ \t]*\n+/g, "\n")
4564
- }]
4565
- }, nodes);
4566
- }
4567
- /**
4568
- * Flatten textblocks into one paragraph's source content.
4569
- */
4570
- function docToParagraphMarkdown(doc) {
4571
- const paragraphs = [];
4572
- doc.descendants((node) => {
4573
- if (!node.isTextblock) return true;
4574
- paragraphs.push(node.textContent);
4575
- return false;
4576
- });
4577
- return paragraphs.join("\n").replaceAll(/\n[ \t]*\n+/g, "\n");
4578
- }
4579
-
4580
- //#endregion
4581
- //#region src/extensions/single-paragraph.ts
4582
- const singleParagraphPluginKey = new PluginKey("single-paragraph");
4583
- /**
4584
- * Keep editing and pasted content within one paragraph.
4585
- * Opt into this extension for inline Markdown fields such as task paragraphs.
4586
- * Register it alongside `defineMeowdown`, seed content with
4587
- * `paragraphMarkdownToDoc`, and read edits with `docToParagraphMarkdown`.
4588
- * Ordinary note editors should keep their full document behavior.
4589
- *
4590
- * Text input stays literal, before block input rules can consume prefixes.
4591
- * Transactions and pasted blocks flatten to paragraph text separated by soft
4592
- * lines. Reference definitions come from `referenceDefinitions` in the editor
4593
- * config; definition-looking content in the field remains visible literal text.
4594
- * Enter/submit, focus, and persistence remain the caller's responsibility.
4595
- *
4596
- * @example
4597
- * const editor = createEditor({
4598
- * extension: union(defineMeowdown({ referenceDefinitions }), defineSingleParagraph()),
4599
- * defaultContent: paragraphMarkdownToDoc('Buy **milk**'),
4600
- * })
4601
- * const markdown = docToParagraphMarkdown(editor.state.doc)
4602
- */
4603
- function defineSingleParagraph() {
4604
- return withPriority$1(definePlugin(new Plugin({
4605
- key: singleParagraphPluginKey,
4606
- props: { handleTextInput(view, from, to, text) {
4607
- view.dispatch(view.state.tr.insertText(text, from, to));
4608
- return true;
4609
- } },
4610
- appendTransaction(transactions, _oldState, state) {
4611
- if (!transactions.some((transaction) => transaction.docChanged)) return;
4612
- const markdown = docToParagraphMarkdown(state.doc);
4613
- if (state.doc.childCount === 1 && isNodeOfType(state.doc.child(0), "paragraph") && state.doc.child(0).textContent === markdown) return;
4614
- const doc = paragraphMarkdownToDoc(markdown, getNodeBuildersForSchema(state.schema));
4615
- const position = Math.min(state.selection.head, doc.content.size - 1);
4616
- const transaction = state.tr.replaceWith(0, state.doc.content.size, doc.content);
4617
- return transaction.setSelection(TextSelection.create(transaction.doc, Math.max(1, position)));
4618
- }
4619
- })), Priority$1.highest);
4620
- }
4621
-
4622
4643
  //#endregion
4623
4644
  //#region src/extensions/inline-mark-plugin.ts
4624
4645
  const META_KEY = "inline-marks-applied";
@@ -4715,7 +4736,7 @@ function createInlineMarkPlugin(getOptions) {
4715
4736
  const touchesRange = pos <= range.to && pos + node.nodeSize >= range.from;
4716
4737
  const dependsOnChange = cached == null || setsIntersect(cached.referencedKeys, changedKeys);
4717
4738
  if (!touchesRange && !dependsOnChange) return false;
4718
- const nodeChunks = chunksForTextblock(node, pos + 1, state.schema, options, references, changedKeys, !singleParagraphPluginKey.get(state) && isReferenceDefinitionNode(node, parent, index));
4739
+ const nodeChunks = chunksForTextblock(node, pos + 1, state.schema, options, references, changedKeys, !isSingleParagraph(state) && isReferenceDefinitionNode(node, parent, index));
4719
4740
  if (nodeChunks.length > 0) chunks.push(...nodeChunks);
4720
4741
  const updated = chunkCache.get(node);
4721
4742
  if (updated != null) processed.push({
@@ -4738,7 +4759,7 @@ function createInlineMarkPlugin(getOptions) {
4738
4759
  }
4739
4760
  }
4740
4761
  function collectReferences(doc, external, state) {
4741
- const local = singleParagraphPluginKey.get(state) ? {
4762
+ const local = isSingleParagraph(state) ? {
4742
4763
  nodes: /* @__PURE__ */ new Set(),
4743
4764
  definitions: /* @__PURE__ */ new Map()
4744
4765
  } : collectReferenceDefinitions(doc);
@@ -4764,7 +4785,7 @@ function createInlineMarkPlugin(getOptions) {
4764
4785
  };
4765
4786
  if (transaction.getMeta(META_KEY)) return value;
4766
4787
  const externalDefinitions = getOptions?.(oldState)?.referenceDefinitions;
4767
- const references = singleParagraphPluginKey.get(oldState) || externalDefinitions !== value.externalDefinitions || externalDefinitions && transaction.docChanged ? collectReferences(newState.doc, externalDefinitions, newState) : updateReferenceDefinitions(value.references, transaction, newState.doc);
4788
+ const references = isSingleParagraph(oldState) || externalDefinitions !== value.externalDefinitions || externalDefinitions && transaction.docChanged ? collectReferences(newState.doc, externalDefinitions, newState) : updateReferenceDefinitions(value.references, transaction, newState.doc);
4768
4789
  if (references === value.references) return value;
4769
4790
  const changedKeys = getChangedReferenceKeys(value.references.definitions, references.definitions);
4770
4791
  if (changedKeys.size === 0) return {
@@ -5818,36 +5839,20 @@ function normalizeTaskList(node) {
5818
5839
  node.replaceChildren(...content.children);
5819
5840
  textBlock.prepend(checkbox);
5820
5841
  }
5821
- const listInputRules = [
5822
- wrappingListInputRule(/^\s?([*-])\s$/, {
5823
- kind: "bullet",
5824
- collapsed: false
5825
- }),
5826
- wrappingListInputRule(/^\s?(\d+)\.\s$/, ({ match }) => {
5827
- const text = match[1];
5828
- const num = text ? parseInt(text, 10) : void 0;
5829
- return {
5830
- kind: "ordered",
5831
- collapsed: false,
5832
- order: num && num >= 2 && Number.isSafeInteger(num) ? num : null
5833
- };
5834
- }),
5835
- wrappingListInputRule(/^\s?\[([\sX]?)\]\s$/i, ({ match }) => {
5836
- return {
5837
- kind: "task",
5838
- checked: ["x", "X"].includes(match[1]),
5839
- collapsed: false
5840
- };
5841
- }),
5842
- wrappingListInputRule(/^\s?\+\s$/, {
5842
+ const circleTaskListInputRule = {
5843
+ regexp: /^\s?\+\s$/,
5844
+ getAttrs: {
5843
5845
  kind: "task",
5844
5846
  marker: "+",
5845
5847
  checked: false,
5846
5848
  collapsed: false
5847
- })
5848
- ];
5849
+ }
5850
+ };
5851
+ function defineListInputRule({ regexp, getAttrs }) {
5852
+ return defineBlockInputRule(regexp, createListInputRuleHandler(getAttrs));
5853
+ }
5849
5854
  function defineMeowdownListInputRules() {
5850
- return union(listInputRules.map(defineInputRule));
5855
+ return union(defineListInputRule(bulletListInputRule), defineListInputRule(orderedListInputRule), defineListInputRule(taskListInputRule), defineListInputRule(circleTaskListInputRule));
5851
5856
  }
5852
5857
  /**
5853
5858
  * Circle checkbox task: a `task` list item with a `+` marker.
@@ -7343,7 +7348,7 @@ function defineYouTubeVideoClickHandler(getOnClick) {
7343
7348
  //#endregion
7344
7349
  //#region src/extensions/extension.ts
7345
7350
  function defineEditorExtensionImpl(options) {
7346
- return union(defineMeowdownParagraph(), defineDoc(), defineDocFrontmatterAttr(), defineText(), defineBlockquote(), defineMeowdownList(), defineHeading(), defineTable(), defineCodeBlock$1(), defineMeowdownHorizontalRule(), defineHTMLComment(), defineInlineMarks(), defineEditorConfig(options), defineImage(getEditorConfig), defineFileView(getEditorConfig), defineModClickPrevention(), defineFileClickHandler((state) => getEditorConfig(state).onFileClick), defineImageClickHandler((state) => getEditorConfig(state).onImageClick), defineXPostMediaClickHandler((state) => getEditorConfig(state).onXPostMediaClick), defineYouTubeVideoClickHandler((state) => getEditorConfig(state).onYouTubeVideoClick), defineWikilinkClickHandler((state) => getEditorConfig(state).onWikilinkClick), defineTagClickHandler((state) => getEditorConfig(state).onTagClick), defineLinkClickHandler((state) => getEditorConfig(state).onLinkClick), defineFollowLinkHandler(getEditorConfig), defineExitBoundaryHandler((state) => getEditorConfig(state).onExitBoundary), defineFilePaste(getEditorConfig), defineEmbedPaste((state) => !!getEditorConfig(state).embedPaste), defineLinkPaste((state) => !!getEditorConfig(state).linkPaste), defineBulletAfterHeading((state) => !!getEditorConfig(state).bulletAfterHeading), defineBackspaceEmptyFirstBlock((state) => !!getEditorConfig(state).backspaceDeletesEmptyFirstBlock), defineSubstitution((state) => !!getEditorConfig(state).substitution), defineWikilinkTrigger((state) => !!getEditorConfig(state).wikilinkEnabled), definePlaceholder$1({
7351
+ return union(defineMeowdownParagraph(), defineDoc(), defineDocFrontmatterAttr(), defineText(), defineMeowdownBlockquote(), defineMeowdownList(), defineHeading(), defineTable(), defineCodeBlock(), defineMeowdownHorizontalRule(), defineHTMLComment(), defineInlineMarks(), defineEditorConfig(options), defineImage(getEditorConfig), defineFileView(getEditorConfig), defineModClickPrevention(), defineFileClickHandler((state) => getEditorConfig(state).onFileClick), defineImageClickHandler((state) => getEditorConfig(state).onImageClick), defineXPostMediaClickHandler((state) => getEditorConfig(state).onXPostMediaClick), defineYouTubeVideoClickHandler((state) => getEditorConfig(state).onYouTubeVideoClick), defineWikilinkClickHandler((state) => getEditorConfig(state).onWikilinkClick), defineTagClickHandler((state) => getEditorConfig(state).onTagClick), defineLinkClickHandler((state) => getEditorConfig(state).onLinkClick), defineFollowLinkHandler(getEditorConfig), defineExitBoundaryHandler((state) => getEditorConfig(state).onExitBoundary), defineFilePaste(getEditorConfig), defineEmbedPaste((state) => !!getEditorConfig(state).embedPaste), defineLinkPaste((state) => !!getEditorConfig(state).linkPaste), defineBulletAfterHeading((state) => !!getEditorConfig(state).bulletAfterHeading), defineBackspaceEmptyFirstBlock((state) => !!getEditorConfig(state).backspaceDeletesEmptyFirstBlock), defineSubstitution((state) => !!getEditorConfig(state).substitution), defineWikilinkTrigger((state) => !!getEditorConfig(state).wikilinkEnabled), definePlaceholder$1({
7347
7352
  placeholder: (state) => {
7348
7353
  const placeholder = getEditorConfig(state).placeholder;
7349
7354
  return typeof placeholder === "function" ? placeholder(state) : placeholder ?? "";
@@ -7355,7 +7360,7 @@ function defineEditorExtensionImpl(options) {
7355
7360
  if (editorClassName) attributes.class += ` ${editorClassName}`;
7356
7361
  if (spellCheck != null) attributes.spellcheck = String(spellCheck);
7357
7362
  return attributes;
7358
- }), defineCodeBlockSyntaxHighlight(), defineCrossEditorDrag(), defineClickBelow(), defineEscapeCollapse(), defineSoftBreak(), defineMoveBlock(), defineSelectDocBoundary(), defineInlineMarkPlugin(getEditorConfig), defineInlineToggle(), defineLinkCommands(), defineWikilink(), defineMath(), defineMarkMode(getMarkMode), defineClipboard(), defineScrollToSelection(), defineHiddenRunCaret(), defineSystemSubstitutionGuard(), defineAtomMarkNavigation({ marks: ATOM_SOURCE_MARK_NAMES.map((name) => ({
7363
+ }), defineCodeBlockSyntaxHighlight(), defineCrossEditorDrag(), defineClickBelow(), defineEscapeCollapse(), defineSoftBreak(), defineMoveBlock(), defineSelectDocBoundary(), defineSingleParagraph(), defineInlineMarkPlugin(getEditorConfig), defineInlineToggle(), defineLinkCommands(), defineWikilink(), defineMath(), defineMarkMode(getMarkMode), defineClipboard(), defineScrollToSelection(), defineHiddenRunCaret(), defineSystemSubstitutionGuard(), defineAtomMarkNavigation({ marks: ATOM_SOURCE_MARK_NAMES.map((name) => ({
7359
7364
  name,
7360
7365
  modes: [
7361
7366
  "hide",
@@ -8134,4 +8139,4 @@ function getVirtualElementFromRange(view, range) {
8134
8139
  }
8135
8140
 
8136
8141
  //#endregion
8137
- export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineBackspaceEmptyFirstBlock, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSingleParagraph, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, defineXPostMediaClickHandler, defineYouTubeVideoClickHandler, dismissLinkHover, docToMarkdown, docToParagraphMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getEditorConfig, getFileKind, getIsComposing, getLinkText, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isMarkOfType, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, paragraphMarkdownToDoc, parsePostEmbedSnapshot, parseWikiEmbed, removeLink, renderMathInto, updateEditorConfig, updateLink, wikiEmbedBasename, withPriority };
8142
+ export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineBackspaceEmptyFirstBlock, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, defineXPostMediaClickHandler, defineYouTubeVideoClickHandler, dismissLinkHover, docToMarkdown, docToParagraphMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getEditorConfig, getFileKind, getIsComposing, getLinkText, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isMarkOfType, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, paragraphMarkdownToDoc, parsePostEmbedSnapshot, parseWikiEmbed, removeLink, renderMathInto, updateEditorConfig, updateLink, wikiEmbedBasename, withPriority };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/core",
3
3
  "type": "module",
4
- "version": "0.79.0",
4
+ "version": "0.80.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -32,7 +32,7 @@
32
32
  "@post-embed/schema": "^0.5.2",
33
33
  "@post-embed/types": "^0.5.0",
34
34
  "@prosekit/core": "^0.13.3",
35
- "@prosekit/extensions": "^0.18.3",
35
+ "@prosekit/extensions": "^0.18.4",
36
36
  "@prosekit/pm": "^0.1.20",
37
37
  "@prosekit/web": "^0.9.3",
38
38
  "hast-util-to-mdast": "^10.1.2",
@@ -41,7 +41,8 @@
41
41
  "mdast-util-to-markdown": "^2.2.0",
42
42
  "micromark-util-decode-string": "^2.0.1",
43
43
  "micromark-util-normalize-identifier": "^2.0.1",
44
- "prosemirror-flat-list": "^0.7.2",
44
+ "prosemirror-enter-rules": "^0.1.6",
45
+ "prosemirror-flat-list": "^0.7.3",
45
46
  "prosemirror-highlight": "^0.16.0",
46
47
  "rehype-parse": "^9.0.0",
47
48
  "rehype-remark": "^10.0.1",