@meowdown/core 0.71.2 → 0.72.1
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 +13 -16
- package/dist/index.js +162 -112
- package/package.json +9 -10
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { SearchStatus, SearchStatusHandler, defineSearchStatusHandler, getSearch
|
|
|
6
6
|
import { EditorView } from "@prosekit/pm/view";
|
|
7
7
|
import { EditorNode, Mark, ProseMirrorNode } from "@prosekit/pm/model";
|
|
8
8
|
import { ListAttrs } from "@prosekit/extensions/list";
|
|
9
|
-
import { parseXPostId } from "@post-embed/schema";
|
|
10
9
|
import { HorizontalRuleExtension } from "@prosekit/extensions/horizontal-rule";
|
|
11
10
|
import { Resolver } from "@post-embed/elements/x";
|
|
12
11
|
import { render } from "katex";
|
|
@@ -781,11 +780,6 @@ export declare function defineFollowLinkHandler(getHandlers?: (state: EditorStat
|
|
|
781
780
|
//#region src/extensions/post-embed.d.ts
|
|
782
781
|
type XPostResolver = Resolver<XPost>;
|
|
783
782
|
type YouTubeVideoResolver = Resolver<YouTubeVideo>;
|
|
784
|
-
type PostEmbedKind = 'x-post' | 'youtube-video';
|
|
785
|
-
/**
|
|
786
|
-
* The post-embed card an image `src` renders as, or `undefined` for a plain image.
|
|
787
|
-
*/
|
|
788
|
-
export declare function matchPostEmbed(src: string): PostEmbedKind | undefined;
|
|
789
783
|
export declare const defaultResolveXPost: XPostResolver;
|
|
790
784
|
export declare const defaultResolveYouTubeVideo: YouTubeVideoResolver;
|
|
791
785
|
/**
|
|
@@ -1168,7 +1162,7 @@ declare function defineEditorExtensionImpl(options: EditorExtensionOptions): imp
|
|
|
1168
1162
|
fenceLength?: number | null;
|
|
1169
1163
|
};
|
|
1170
1164
|
};
|
|
1171
|
-
}>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension]>, MeowdownHorizontalRuleExtension, import("@prosekit/core").Extension<{
|
|
1165
|
+
}>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension]>, MeowdownHorizontalRuleExtension, import("@prosekit/core").Extension<{
|
|
1172
1166
|
Nodes: {
|
|
1173
1167
|
htmlComment: MeowdownHTMLCommentAttrs;
|
|
1174
1168
|
};
|
|
@@ -1232,7 +1226,7 @@ declare function defineEditorExtensionImpl(options: EditorExtensionOptions): imp
|
|
|
1232
1226
|
Marks: {
|
|
1233
1227
|
mdPack: MdPackAttrs;
|
|
1234
1228
|
};
|
|
1235
|
-
}>]>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Extension<import("@prosekit/core").ExtensionTyping<any, any, any>>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Union<readonly [import("@prosekit/core").Extension<{
|
|
1229
|
+
}>]>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Extension<import("@prosekit/core").ExtensionTyping<any, any, any>>, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").PlainExtension, import("@prosekit/core").Union<readonly [import("@prosekit/core").Extension<{
|
|
1236
1230
|
Commands: {
|
|
1237
1231
|
insertSoftBreak: [];
|
|
1238
1232
|
};
|
|
@@ -1415,7 +1409,7 @@ export declare const EDITOR_KEY_BINDINGS: {
|
|
|
1415
1409
|
readonly 'Mod-5': "Heading 5";
|
|
1416
1410
|
readonly 'Mod-6': "Heading 6";
|
|
1417
1411
|
readonly 'Mod-.': "Fold or unfold a bullet";
|
|
1418
|
-
readonly 'Shift-Enter': "Insert a line break";
|
|
1412
|
+
readonly 'Shift-Enter': "Insert a line break, or leave a code block from its end";
|
|
1419
1413
|
readonly 'Mod-Enter': "Follow the link under the caret, or cycle a checkbox task";
|
|
1420
1414
|
readonly 'Mod-Shift-Enter': "Cycle a circle checkbox task";
|
|
1421
1415
|
readonly 'Mod-Shift-7': "Ordered list";
|
|
@@ -1447,7 +1441,7 @@ interface LinkHoverOptions {
|
|
|
1447
1441
|
}
|
|
1448
1442
|
/**
|
|
1449
1443
|
* Track the link under the user's attention: a mouse hover after a short
|
|
1450
|
-
*
|
|
1444
|
+
* delay, or a touch tap immediately, since touch has no hover. Without the
|
|
1451
1445
|
* tap entry, the popup's preview and actions would stay unreachable on
|
|
1452
1446
|
* phones, where a tap on a link only places the caret and raises the
|
|
1453
1447
|
* software keyboard.
|
|
@@ -1564,13 +1558,16 @@ interface WikilinkHoverHit extends WikilinkHit {
|
|
|
1564
1558
|
*/
|
|
1565
1559
|
type WikilinkHoverHandler = (hit: WikilinkHoverHit | undefined) => void;
|
|
1566
1560
|
/**
|
|
1567
|
-
* Track the wikilink
|
|
1561
|
+
* Track the wikilink the pointer rests on without attaching per-link
|
|
1562
|
+
* listeners.
|
|
1568
1563
|
*
|
|
1569
|
-
*
|
|
1570
|
-
*
|
|
1571
|
-
*
|
|
1564
|
+
* A cold pointer must rest on a link for `openDelay` ms before enter fires.
|
|
1565
|
+
* Moving to an adjacent link restarts the delay, or switches at once when a
|
|
1566
|
+
* link is already entered. Leave fires `closeDelay` ms after the pointer
|
|
1567
|
+
* leaves, and immediately when the hovered link is deleted, replaced, or
|
|
1568
|
+
* changes target. Moving among descendants of one label is de-duplicated.
|
|
1572
1569
|
*/
|
|
1573
|
-
export declare function defineWikilinkHoverHandler(onHoverChange: WikilinkHoverHandler): PlainExtension;
|
|
1570
|
+
export declare function defineWikilinkHoverHandler(onHoverChange: WikilinkHoverHandler, openDelay?: number, closeDelay?: number): PlainExtension;
|
|
1574
1571
|
//#endregion
|
|
1575
1572
|
//#region src/extensions/wikilink-trigger.d.ts
|
|
1576
1573
|
/**
|
|
@@ -1642,4 +1639,4 @@ export declare function getSelectedText(state: EditorState): string;
|
|
|
1642
1639
|
*/
|
|
1643
1640
|
export declare function getVirtualElementFromRange(view: EditorView, range: PositionRange): VirtualElement;
|
|
1644
1641
|
//#endregion
|
|
1645
|
-
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 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
|
|
1642
|
+
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 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 XPostResolver, type YouTubeVideoResolver, defineCodeBlockPreviewPlugin, definePlaceholder, defineSearchStatusHandler, getSearchStatus, isCodeBlockPreviewHiddenDecoration, withPriority };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Priority, Priority as Priority$1, createMarkBuilders, createNodeBuilders, 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";
|
|
1
|
+
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";
|
|
2
2
|
import { defineCodeBlock, defineCodeBlockHighlight, defineCodeBlockPreviewPlugin, isCodeBlockPreviewHiddenDecoration } from "@prosekit/extensions/code-block";
|
|
3
3
|
import { definePlaceholder, definePlaceholder as definePlaceholder$1 } from "@prosekit/extensions/placeholder";
|
|
4
4
|
import { NodeSelection, Plugin, PluginKey, Selection, TextSelection } from "@prosekit/pm/state";
|
|
5
5
|
import { defineSearchCommands, defineSearchQuery, defineSearchStatusHandler, getSearchStatus, getSearchStatus as getSearchStatus$1 } from "@prosekit/extensions/search";
|
|
6
|
-
import { LEZER_NODE_IDS, collectInlineElements, getAutolinkHref, gfmBlockOnlyParser, gfmParser, isSpaceChar, parseInline } from "@meowdown/markdown";
|
|
6
|
+
import { LEZER_NODE_IDS, collectInlineElements, getAutolinkHref, gfmBlockOnlyParser, gfmParser, isSpaceChar, matchEmbed, parseInline, parseXPostId } from "@meowdown/markdown";
|
|
7
7
|
import { isElementLike, isHTMLElement, isObject, once } from "@ocavue/utils";
|
|
8
8
|
import { defineBlockquote } from "@prosekit/extensions/blockquote";
|
|
9
9
|
import { defineDoc } from "@prosekit/extensions/doc";
|
|
@@ -33,14 +33,14 @@ import { triggerAutocomplete } from "@prosekit/extensions/autocomplete";
|
|
|
33
33
|
import { defineListCommands, defineListDropIndicator, defineListKeymap, defineListSpec, moveList, toggleList, unwrapList, wrapInList } from "@prosekit/extensions/list";
|
|
34
34
|
import { chainCommands, lift, newlineInCode, splitBlock } from "@prosekit/pm/commands";
|
|
35
35
|
import { closeHistory } from "@prosekit/pm/history";
|
|
36
|
-
import { fromSyndication } from "@post-embed/exporter/x/syndication";
|
|
37
|
-
import { XPostSchema, YouTubeVideoSchema, parseXPostId } from "@post-embed/schema";
|
|
38
|
-
import { createLRU } from "lru.min";
|
|
39
|
-
import * as v from "valibot";
|
|
40
36
|
import { defineHorizontalRule } from "@prosekit/extensions/horizontal-rule";
|
|
41
37
|
import { registerXPost } from "@post-embed/elements/x";
|
|
42
38
|
import { registerYouTubeVideo } from "@post-embed/elements/youtube";
|
|
43
39
|
import { registerResizableHandleElement, registerResizableRootElement } from "@prosekit/web/resizable";
|
|
40
|
+
import { fromSyndication } from "@post-embed/exporter/x/syndication";
|
|
41
|
+
import { XPostSchema, YouTubeVideoSchema } from "@post-embed/schema";
|
|
42
|
+
import { createLRU } from "lru.min";
|
|
43
|
+
import * as v from "valibot";
|
|
44
44
|
import { AttrStep, ReplaceStep, Step, StepResult, Transform } from "@prosekit/pm/transform";
|
|
45
45
|
import { decodeString } from "micromark-util-decode-string";
|
|
46
46
|
import { normalizeIdentifier } from "micromark-util-normalize-identifier";
|
|
@@ -602,6 +602,41 @@ function defineBulletAfterHeading(enabled) {
|
|
|
602
602
|
} }), Priority$1.high);
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
+
//#endregion
|
|
606
|
+
//#region src/extensions/click-below.ts
|
|
607
|
+
const clickBelowKey = new PluginKey("meowdown-click-below");
|
|
608
|
+
function acceptsCaretAtEnd(node) {
|
|
609
|
+
return node.isTextblock && !node.type.spec.code;
|
|
610
|
+
}
|
|
611
|
+
function handleMouseDown(view, event) {
|
|
612
|
+
if (event.target !== view.dom) return false;
|
|
613
|
+
if (event.button !== 0 || !view.editable) return false;
|
|
614
|
+
const { doc } = view.state;
|
|
615
|
+
const lastBlock = doc.lastChild;
|
|
616
|
+
if (!lastBlock || acceptsCaretAtEnd(lastBlock)) return false;
|
|
617
|
+
const lastBlockDOM = view.nodeDOM(doc.content.size - lastBlock.nodeSize);
|
|
618
|
+
if (!(lastBlockDOM instanceof HTMLElement)) return false;
|
|
619
|
+
if (event.clientY <= lastBlockDOM.getBoundingClientRect().bottom) return false;
|
|
620
|
+
const block = defaultBlockAt(doc.contentMatchAt(doc.childCount))?.createAndFill();
|
|
621
|
+
if (!block) return false;
|
|
622
|
+
event.preventDefault();
|
|
623
|
+
const end = doc.content.size;
|
|
624
|
+
const tr = view.state.tr.insert(end, block);
|
|
625
|
+
view.dispatch(tr.setSelection(TextSelection.create(tr.doc, end + 1)).scrollIntoView());
|
|
626
|
+
view.focus();
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* A press below the last block appends an empty paragraph and puts the caret
|
|
631
|
+
* in it, when that block would otherwise keep the caret inside itself.
|
|
632
|
+
*/
|
|
633
|
+
function defineClickBelow() {
|
|
634
|
+
return definePlugin(new Plugin({
|
|
635
|
+
key: clickBelowKey,
|
|
636
|
+
props: { handleDOMEvents: { mousedown: handleMouseDown } }
|
|
637
|
+
}));
|
|
638
|
+
}
|
|
639
|
+
|
|
605
640
|
//#endregion
|
|
606
641
|
//#region src/converters/html-to-md.ts
|
|
607
642
|
/**
|
|
@@ -2222,6 +2257,38 @@ function getCodeTokens(code, language) {
|
|
|
2222
2257
|
return loadLanguageSupport(trimmed).then((loaded) => loaded ? tokenize(code, loaded) : []);
|
|
2223
2258
|
}
|
|
2224
2259
|
|
|
2260
|
+
//#endregion
|
|
2261
|
+
//#region src/extensions/code-block-exit.ts
|
|
2262
|
+
/**
|
|
2263
|
+
* With the caret at the end of a code block, move it into the block below when
|
|
2264
|
+
* that block is an empty paragraph, and into a new default block otherwise.
|
|
2265
|
+
*/
|
|
2266
|
+
const exitCodeBlockAtEnd = (state, dispatch) => {
|
|
2267
|
+
const { $head, empty } = state.selection;
|
|
2268
|
+
if (!empty) return false;
|
|
2269
|
+
const codeBlock = $head.parent;
|
|
2270
|
+
if (!codeBlock.type.spec.code || $head.parentOffset !== codeBlock.content.size) return false;
|
|
2271
|
+
const container = $head.node(-1);
|
|
2272
|
+
const indexAfter = $head.indexAfter(-1);
|
|
2273
|
+
const after = $head.after();
|
|
2274
|
+
const next = container.maybeChild(indexAfter);
|
|
2275
|
+
if (next && isNodeOfType(next, "paragraph") && next.content.size === 0) {
|
|
2276
|
+
dispatch?.(state.tr.setSelection(TextSelection.create(state.doc, after + 1)).scrollIntoView());
|
|
2277
|
+
return true;
|
|
2278
|
+
}
|
|
2279
|
+
const type = defaultBlockAt(container.contentMatchAt(indexAfter));
|
|
2280
|
+
const block = type?.createAndFill();
|
|
2281
|
+
if (!type || !block || !container.canReplaceWith(indexAfter, indexAfter, type)) return false;
|
|
2282
|
+
if (dispatch) {
|
|
2283
|
+
const tr = state.tr.insert(after, block);
|
|
2284
|
+
dispatch(tr.setSelection(TextSelection.create(tr.doc, after + 1)).scrollIntoView());
|
|
2285
|
+
}
|
|
2286
|
+
return true;
|
|
2287
|
+
};
|
|
2288
|
+
function defineCodeBlockExitKeymap() {
|
|
2289
|
+
return defineKeymap({ "Mod-Enter": exitCodeBlockAtEnd });
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2225
2292
|
//#endregion
|
|
2226
2293
|
//#region src/extensions/code-block.ts
|
|
2227
2294
|
function defineFenceStyleAttr() {
|
|
@@ -2279,7 +2346,7 @@ function defineDollarFenceEnterRule() {
|
|
|
2279
2346
|
});
|
|
2280
2347
|
}
|
|
2281
2348
|
function defineCodeBlock$1() {
|
|
2282
|
-
return union(defineCodeBlock(), defineFenceStyleAttr(), defineFenceLengthAttr(), defineTildeFenceInputRule(), defineTildeFenceEnterRule(), defineDollarFenceEnterRule());
|
|
2349
|
+
return union(defineCodeBlock(), defineFenceStyleAttr(), defineFenceLengthAttr(), defineTildeFenceInputRule(), defineTildeFenceEnterRule(), defineDollarFenceEnterRule(), defineCodeBlockExitKeymap());
|
|
2283
2350
|
}
|
|
2284
2351
|
|
|
2285
2352
|
//#endregion
|
|
@@ -2478,99 +2545,13 @@ function getPastedText(event, slice) {
|
|
|
2478
2545
|
return slice.content.textBetween(0, slice.content.size, "\n");
|
|
2479
2546
|
}
|
|
2480
2547
|
|
|
2481
|
-
//#endregion
|
|
2482
|
-
//#region src/extensions/post-embed.ts
|
|
2483
|
-
const YOUTUBE_HOSTS = /^(?:www\.|m\.)?(?:youtube\.com|youtube-nocookie\.com)$/i;
|
|
2484
|
-
const YOUTU_BE_HOST = /^(?:www\.)?youtu\.be$/i;
|
|
2485
|
-
const VIDEO_ID = /^[\w-]{11}$/;
|
|
2486
|
-
function parseURL(src) {
|
|
2487
|
-
try {
|
|
2488
|
-
return new URL(src);
|
|
2489
|
-
} catch {
|
|
2490
|
-
return;
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
function isYouTubeVideo(src) {
|
|
2494
|
-
const url = parseURL(src);
|
|
2495
|
-
if (!url) return false;
|
|
2496
|
-
let videoId = null;
|
|
2497
|
-
if (YOUTU_BE_HOST.test(url.hostname)) videoId = url.pathname.slice(1);
|
|
2498
|
-
else if (YOUTUBE_HOSTS.test(url.hostname)) {
|
|
2499
|
-
const [, firstSegment, secondSegment] = url.pathname.split("/");
|
|
2500
|
-
if (url.pathname === "/watch") videoId = url.searchParams.get("v");
|
|
2501
|
-
else if (firstSegment === "shorts" || firstSegment === "embed" || firstSegment === "live") videoId = secondSegment ?? null;
|
|
2502
|
-
}
|
|
2503
|
-
return videoId !== null && VIDEO_ID.test(videoId);
|
|
2504
|
-
}
|
|
2505
|
-
/**
|
|
2506
|
-
* The post-embed card an image `src` renders as, or `undefined` for a plain image.
|
|
2507
|
-
*/
|
|
2508
|
-
function matchPostEmbed(src) {
|
|
2509
|
-
if (parseXPostId(src) !== void 0) return "x-post";
|
|
2510
|
-
if (isYouTubeVideo(src)) return "youtube-video";
|
|
2511
|
-
}
|
|
2512
|
-
/**
|
|
2513
|
-
* Wrap a loader in a 64-entry LRU keyed by URL. A settled URL answers
|
|
2514
|
-
* synchronously, so a revisited document renders its cards in the first
|
|
2515
|
-
* frame; the first visit returns the in-flight load.
|
|
2516
|
-
*/
|
|
2517
|
-
function cached(load) {
|
|
2518
|
-
const cache = createLRU({ max: 64 });
|
|
2519
|
-
return (url) => {
|
|
2520
|
-
if (cache.has(url)) return cache.get(url);
|
|
2521
|
-
const pending = load(url).then((value) => {
|
|
2522
|
-
cache.set(url, value);
|
|
2523
|
-
return value;
|
|
2524
|
-
}, (error) => {
|
|
2525
|
-
cache.delete(url);
|
|
2526
|
-
throw error;
|
|
2527
|
-
});
|
|
2528
|
-
cache.set(url, pending);
|
|
2529
|
-
return pending;
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
const X_POST_API = "https://react-tweet.vercel.app/api/tweet/";
|
|
2533
|
-
const defaultResolveXPost = cached(async (url) => {
|
|
2534
|
-
const id = parseXPostId(url);
|
|
2535
|
-
if (id === void 0) return;
|
|
2536
|
-
const response = await fetch(X_POST_API + id);
|
|
2537
|
-
if (!response.ok) return;
|
|
2538
|
-
const json = await response.json();
|
|
2539
|
-
return fromSyndication(json.data);
|
|
2540
|
-
});
|
|
2541
|
-
const YOUTUBE_OEMBED_API = "https://www.youtube.com/oembed?format=json&url=";
|
|
2542
|
-
const defaultResolveYouTubeVideo = cached(async (url) => {
|
|
2543
|
-
const response = await fetch(YOUTUBE_OEMBED_API + encodeURIComponent(url));
|
|
2544
|
-
if (!response.ok) return;
|
|
2545
|
-
return {
|
|
2546
|
-
url,
|
|
2547
|
-
...await response.json()
|
|
2548
|
-
};
|
|
2549
|
-
});
|
|
2550
|
-
const PostEmbedSnapshotSchema = v.variant("kind", [v.object({
|
|
2551
|
-
kind: v.literal("x-post"),
|
|
2552
|
-
data: XPostSchema
|
|
2553
|
-
}), v.object({
|
|
2554
|
-
kind: v.literal("youtube-video"),
|
|
2555
|
-
data: YouTubeVideoSchema
|
|
2556
|
-
})]);
|
|
2557
|
-
/**
|
|
2558
|
-
* The snapshot a card renders from a saved JSON object, or `undefined` when
|
|
2559
|
-
* the object does not validate. Unknown fields are dropped, so a persisted
|
|
2560
|
-
* snapshot is exactly what the card needs.
|
|
2561
|
-
*/
|
|
2562
|
-
function parsePostEmbedSnapshot(value) {
|
|
2563
|
-
const result = v.safeParse(PostEmbedSnapshotSchema, value);
|
|
2564
|
-
return result.success ? result.output : void 0;
|
|
2565
|
-
}
|
|
2566
|
-
|
|
2567
2548
|
//#endregion
|
|
2568
2549
|
//#region src/extensions/embed-paste.ts
|
|
2569
2550
|
const embedPasteKey = new PluginKey("meowdown-embed-paste");
|
|
2570
2551
|
function detectEmbedUrl(text) {
|
|
2571
2552
|
const trimmed = text.trim();
|
|
2572
2553
|
if (!trimmed || /\s/.test(trimmed)) return void 0;
|
|
2573
|
-
return
|
|
2554
|
+
return matchEmbed(trimmed) ? trimmed : void 0;
|
|
2574
2555
|
}
|
|
2575
2556
|
function insertEmbedFromPaste(view, url) {
|
|
2576
2557
|
const { from, to } = view.state.selection;
|
|
@@ -3663,6 +3644,63 @@ function stripMagicComment(source) {
|
|
|
3663
3644
|
return source.replace(TRAILING_MAGIC_COMMENT_RE, "");
|
|
3664
3645
|
}
|
|
3665
3646
|
|
|
3647
|
+
//#endregion
|
|
3648
|
+
//#region src/extensions/post-embed.ts
|
|
3649
|
+
/**
|
|
3650
|
+
* Wrap a loader in a 64-entry LRU keyed by URL. A settled URL answers
|
|
3651
|
+
* synchronously, so a revisited document renders its cards in the first
|
|
3652
|
+
* frame; the first visit returns the in-flight load.
|
|
3653
|
+
*/
|
|
3654
|
+
function cached(load) {
|
|
3655
|
+
const cache = createLRU({ max: 64 });
|
|
3656
|
+
return (url) => {
|
|
3657
|
+
if (cache.has(url)) return cache.get(url);
|
|
3658
|
+
const pending = load(url).then((value) => {
|
|
3659
|
+
cache.set(url, value);
|
|
3660
|
+
return value;
|
|
3661
|
+
}, (error) => {
|
|
3662
|
+
cache.delete(url);
|
|
3663
|
+
throw error;
|
|
3664
|
+
});
|
|
3665
|
+
cache.set(url, pending);
|
|
3666
|
+
return pending;
|
|
3667
|
+
};
|
|
3668
|
+
}
|
|
3669
|
+
const X_POST_API = "https://react-tweet.vercel.app/api/tweet/";
|
|
3670
|
+
const defaultResolveXPost = cached(async (url) => {
|
|
3671
|
+
const id = parseXPostId(url);
|
|
3672
|
+
if (id === void 0) return;
|
|
3673
|
+
const response = await fetch(X_POST_API + id);
|
|
3674
|
+
if (!response.ok) return;
|
|
3675
|
+
const json = await response.json();
|
|
3676
|
+
return fromSyndication(json.data);
|
|
3677
|
+
});
|
|
3678
|
+
const YOUTUBE_OEMBED_API = "https://www.youtube.com/oembed?format=json&url=";
|
|
3679
|
+
const defaultResolveYouTubeVideo = cached(async (url) => {
|
|
3680
|
+
const response = await fetch(YOUTUBE_OEMBED_API + encodeURIComponent(url));
|
|
3681
|
+
if (!response.ok) return;
|
|
3682
|
+
return {
|
|
3683
|
+
url,
|
|
3684
|
+
...await response.json()
|
|
3685
|
+
};
|
|
3686
|
+
});
|
|
3687
|
+
const PostEmbedSnapshotSchema = v.variant("kind", [v.object({
|
|
3688
|
+
kind: v.literal("x-post"),
|
|
3689
|
+
data: XPostSchema
|
|
3690
|
+
}), v.object({
|
|
3691
|
+
kind: v.literal("youtube-video"),
|
|
3692
|
+
data: YouTubeVideoSchema
|
|
3693
|
+
})]);
|
|
3694
|
+
/**
|
|
3695
|
+
* The snapshot a card renders from a saved JSON object, or `undefined` when
|
|
3696
|
+
* the object does not validate. Unknown fields are dropped, so a persisted
|
|
3697
|
+
* snapshot is exactly what the card needs.
|
|
3698
|
+
*/
|
|
3699
|
+
function parsePostEmbedSnapshot(value) {
|
|
3700
|
+
const result = v.safeParse(PostEmbedSnapshotSchema, value);
|
|
3701
|
+
return result.success ? result.output : void 0;
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3666
3704
|
//#endregion
|
|
3667
3705
|
//#region src/extensions/wiki-embed.ts
|
|
3668
3706
|
const WIKI_EMBED_SIZE = /^(\d+)(?:x(\d+))?$/i;
|
|
@@ -3906,7 +3944,7 @@ var ImageMarkView = class {
|
|
|
3906
3944
|
const { src } = this.#attrs;
|
|
3907
3945
|
const wrapper = document.createElement("span");
|
|
3908
3946
|
wrapper.className = "md-image-view-preview md-atom-view-preview";
|
|
3909
|
-
const kind =
|
|
3947
|
+
const kind = matchEmbed(src);
|
|
3910
3948
|
if (kind) {
|
|
3911
3949
|
wrapper.dataset.testid = `${kind}-embed`;
|
|
3912
3950
|
wrapper.dataset.postEmbed = kind;
|
|
@@ -7317,7 +7355,7 @@ function insertSoftBreak() {
|
|
|
7317
7355
|
return softBreakCommand;
|
|
7318
7356
|
}
|
|
7319
7357
|
function defineSoftBreak() {
|
|
7320
|
-
return union(defineCommands({ insertSoftBreak }), defineKeymap({ "Shift-Enter": softBreakCommand }));
|
|
7358
|
+
return union(defineCommands({ insertSoftBreak }), defineKeymap({ "Shift-Enter": chainCommands(exitCodeBlockAtEnd, softBreakCommand) }));
|
|
7321
7359
|
}
|
|
7322
7360
|
|
|
7323
7361
|
//#endregion
|
|
@@ -7576,7 +7614,7 @@ function defineEditorExtensionImpl(options) {
|
|
|
7576
7614
|
if (editorClassName) attributes.class += ` ${editorClassName}`;
|
|
7577
7615
|
if (spellCheck != null) attributes.spellcheck = String(spellCheck);
|
|
7578
7616
|
return attributes;
|
|
7579
|
-
}), defineCodeBlockSyntaxHighlight(), defineCrossEditorDrag(), defineEscapeCollapse(), defineSoftBreak(), defineMoveBlock(), defineSelectDocBoundary(), defineInlineMarkPlugin(getEditorConfig), defineInlineToggle(), defineLinkCommands(), defineWikilink(), defineMath(), defineMarkMode(getMarkMode), defineClipboard(), defineScrollToSelection(), defineHiddenRunCaret(), defineSystemSubstitutionGuard(), defineAtomMarkNavigation({ marks: ATOM_SOURCE_MARK_NAMES.map((name) => ({
|
|
7617
|
+
}), 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) => ({
|
|
7580
7618
|
name,
|
|
7581
7619
|
modes: [
|
|
7582
7620
|
"hide",
|
|
@@ -8369,7 +8407,7 @@ const EDITOR_KEY_BINDINGS = {
|
|
|
8369
8407
|
"Mod-5": "Heading 5",
|
|
8370
8408
|
"Mod-6": "Heading 6",
|
|
8371
8409
|
"Mod-.": "Fold or unfold a bullet",
|
|
8372
|
-
"Shift-Enter": "Insert a line break",
|
|
8410
|
+
"Shift-Enter": "Insert a line break, or leave a code block from its end",
|
|
8373
8411
|
"Mod-Enter": "Follow the link under the caret, or cycle a checkbox task",
|
|
8374
8412
|
"Mod-Shift-Enter": "Cycle a circle checkbox task",
|
|
8375
8413
|
"Mod-Shift-7": "Ordered list",
|
|
@@ -8550,17 +8588,17 @@ function defineMarkHoverHandler(config) {
|
|
|
8550
8588
|
//#region src/extensions/link-hover.ts
|
|
8551
8589
|
const linkHoverKey = new PluginKey("meowdown-link-hover");
|
|
8552
8590
|
/**
|
|
8553
|
-
*
|
|
8591
|
+
* Delay before a cold hover enters, in ms.
|
|
8554
8592
|
*/
|
|
8555
|
-
const OPEN_DELAY = 300;
|
|
8593
|
+
const OPEN_DELAY$1 = 300;
|
|
8556
8594
|
/**
|
|
8557
8595
|
* Grace before a leave fires, in ms. The window lets a pointer travel from
|
|
8558
8596
|
* the hovered link onto the popup it anchors.
|
|
8559
8597
|
*/
|
|
8560
|
-
const CLOSE_DELAY = 100;
|
|
8598
|
+
const CLOSE_DELAY$1 = 100;
|
|
8561
8599
|
/**
|
|
8562
8600
|
* Track the link under the user's attention: a mouse hover after a short
|
|
8563
|
-
*
|
|
8601
|
+
* delay, or a touch tap immediately, since touch has no hover. Without the
|
|
8564
8602
|
* tap entry, the popup's preview and actions would stay unreachable on
|
|
8565
8603
|
* phones, where a tap on a link only places the caret and raises the
|
|
8566
8604
|
* software keyboard.
|
|
@@ -8569,8 +8607,8 @@ function defineLinkHoverHandler(onHoverChange, { canLeave } = {}) {
|
|
|
8569
8607
|
return defineMarkHoverHandler({
|
|
8570
8608
|
key: linkHoverKey,
|
|
8571
8609
|
selector: ".md-link",
|
|
8572
|
-
openDelay: OPEN_DELAY,
|
|
8573
|
-
closeDelay: CLOSE_DELAY,
|
|
8610
|
+
openDelay: OPEN_DELAY$1,
|
|
8611
|
+
closeDelay: CLOSE_DELAY$1,
|
|
8574
8612
|
tap: true,
|
|
8575
8613
|
canLeave,
|
|
8576
8614
|
findPayloadAt: (state, pos) => {
|
|
@@ -8821,18 +8859,30 @@ function defineVirtualCaret(layer) {
|
|
|
8821
8859
|
//#region src/extensions/wikilink-hover.ts
|
|
8822
8860
|
const wikilinkHoverKey = new PluginKey("meowdown-wikilink-hover");
|
|
8823
8861
|
/**
|
|
8824
|
-
*
|
|
8862
|
+
* Delay before a cold hover enters, in ms.
|
|
8863
|
+
*/
|
|
8864
|
+
const OPEN_DELAY = 300;
|
|
8865
|
+
/**
|
|
8866
|
+
* Grace before a leave fires, in ms. Returning within it re-enters without
|
|
8867
|
+
* a new delay.
|
|
8868
|
+
*/
|
|
8869
|
+
const CLOSE_DELAY = 100;
|
|
8870
|
+
/**
|
|
8871
|
+
* Track the wikilink the pointer rests on without attaching per-link
|
|
8872
|
+
* listeners.
|
|
8825
8873
|
*
|
|
8826
|
-
*
|
|
8827
|
-
*
|
|
8828
|
-
*
|
|
8874
|
+
* A cold pointer must rest on a link for `openDelay` ms before enter fires.
|
|
8875
|
+
* Moving to an adjacent link restarts the delay, or switches at once when a
|
|
8876
|
+
* link is already entered. Leave fires `closeDelay` ms after the pointer
|
|
8877
|
+
* leaves, and immediately when the hovered link is deleted, replaced, or
|
|
8878
|
+
* changes target. Moving among descendants of one label is de-duplicated.
|
|
8829
8879
|
*/
|
|
8830
|
-
function defineWikilinkHoverHandler(onHoverChange) {
|
|
8880
|
+
function defineWikilinkHoverHandler(onHoverChange, openDelay = OPEN_DELAY, closeDelay = CLOSE_DELAY) {
|
|
8831
8881
|
return defineMarkHoverHandler({
|
|
8832
8882
|
key: wikilinkHoverKey,
|
|
8833
8883
|
selector: ".md-wikilink-view-preview",
|
|
8834
|
-
openDelay
|
|
8835
|
-
closeDelay
|
|
8884
|
+
openDelay,
|
|
8885
|
+
closeDelay,
|
|
8836
8886
|
tap: false,
|
|
8837
8887
|
findPayloadAt: findWikilinkAt,
|
|
8838
8888
|
findPayloadForElement: findWikilinkForElement,
|
|
@@ -8937,4 +8987,4 @@ function getVirtualElementFromRange(view, range) {
|
|
|
8937
8987
|
}
|
|
8938
8988
|
|
|
8939
8989
|
//#endregion
|
|
8940
|
-
export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, 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, docToMarkdown, 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,
|
|
8990
|
+
export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, 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, docToMarkdown, 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, 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.
|
|
4
|
+
"version": "0.72.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@codemirror/language-data": "^6.5.2",
|
|
26
26
|
"@floating-ui/dom": "^1.8.0",
|
|
27
27
|
"@lezer/highlight": "^1.2.3",
|
|
28
|
+
"@meowdown/markdown": "^0.72.0",
|
|
28
29
|
"@ocavue/utils": "^1.8.0",
|
|
29
30
|
"@post-embed/elements": "^0.5.0",
|
|
30
31
|
"@post-embed/exporter": "^0.4.1",
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"@prosekit/pm": "^0.1.19",
|
|
36
37
|
"@prosekit/web": "^0.9.2",
|
|
37
38
|
"hast-util-to-mdast": "^10.1.2",
|
|
38
|
-
"katex": "^0.18.
|
|
39
|
+
"katex": "^0.18.7",
|
|
39
40
|
"lru.min": "^1.1.5",
|
|
40
41
|
"mdast-util-to-markdown": "^2.1.2",
|
|
41
42
|
"micromark-util-decode-string": "^2.0.1",
|
|
@@ -48,22 +49,20 @@
|
|
|
48
49
|
"remark-stringify": "^11.0.0",
|
|
49
50
|
"unicode-by-name": "^0.2.0",
|
|
50
51
|
"unified": "^11.0.5",
|
|
51
|
-
"valibot": "^1.5.0"
|
|
52
|
-
"@meowdown/markdown": "^0.71.2"
|
|
52
|
+
"valibot": "^1.5.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
+
"@meowdown/vitest": "0.0.0",
|
|
55
56
|
"@ocavue/tsconfig": "^0.7.1",
|
|
56
|
-
"@tsdown/css": "^0.23.0
|
|
57
|
+
"@tsdown/css": "^0.23.0",
|
|
57
58
|
"@types/hast": "^3.0.5",
|
|
58
59
|
"@types/mdast": "^4.0.4",
|
|
59
|
-
"@vitest/browser-playwright": "^5.0.
|
|
60
|
+
"@vitest/browser-playwright": "^5.0.1",
|
|
60
61
|
"commonmark.json": "^0.31.0",
|
|
61
62
|
"dedent": "^1.7.2",
|
|
62
63
|
"diffable-html-snapshot": "^0.3.0",
|
|
63
|
-
"tsdown": "^0.23.0
|
|
64
|
-
"vitest": "^5.0.
|
|
65
|
-
"vitest-browser-commands": "^0.4.1",
|
|
66
|
-
"@meowdown/vitest": "0.0.0"
|
|
64
|
+
"tsdown": "^0.23.0",
|
|
65
|
+
"vitest": "^5.0.1"
|
|
67
66
|
},
|
|
68
67
|
"publishConfig": {
|
|
69
68
|
"access": "public"
|