@meowdown/core 0.64.3 → 0.65.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 +82 -39
  2. package/dist/index.js +218 -168
  3. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -1055,6 +1055,11 @@ interface FileClickPayload {
1055
1055
  * pill. Read modifier keys or position a popover from it.
1056
1056
  */
1057
1057
  event: MouseEvent | KeyboardEvent;
1058
+ /**
1059
+ * Whether the platform's mod key (`Command` on Apple, `Ctrl` elsewhere) was held
1060
+ * beyond the gesture that triggered the activation.
1061
+ */
1062
+ mod: boolean;
1058
1063
  }
1059
1064
  type FileClickHandler = (payload: FileClickPayload) => void;
1060
1065
  /**
@@ -1140,6 +1145,45 @@ declare function getFileKind(href: string): string;
1140
1145
  */
1141
1146
  declare function defineFileView(options?: FileViewOptions): PlainExtension;
1142
1147
  //#endregion
1148
+ //#region src/extensions/image-click.d.ts
1149
+ /**
1150
+ * Payload for {@link ImageClickHandler}.
1151
+ */
1152
+ interface ImageClickPayload {
1153
+ /**
1154
+ * The resolved source from `![alt](src)` or a claimed `![[target]]`.
1155
+ */
1156
+ src: string;
1157
+ /**
1158
+ * The image alt text.
1159
+ */
1160
+ alt: string;
1161
+ /**
1162
+ * The originating click or touch tap, or the `Enter`/`Mod-Enter` key press
1163
+ * that followed the selected image. Read the target or position a popover
1164
+ * from it; a touch surface delivers the `touchend` instead of a click.
1165
+ */
1166
+ event: MouseEvent | TouchEvent | KeyboardEvent;
1167
+ /**
1168
+ * Whether the platform's mod key (`Command` on Apple, `Ctrl` elsewhere) was held
1169
+ * beyond the gesture that triggered the activation.
1170
+ */
1171
+ mod: boolean;
1172
+ }
1173
+ type ImageClickHandler = (payload: ImageClickPayload) => void;
1174
+ /**
1175
+ * Call `onClick` when the user clicks or taps a rendered image preview, with
1176
+ * the image's markdown `src`, `alt`, and the originating event.
1177
+ *
1178
+ * Touch taps are handled from `touchend` rather than the synthetic click:
1179
+ * previews live inside the editor contenteditable, and iOS WebKit's
1180
+ * tap-to-focus is a native gesture default action that only cancelling the
1181
+ * `touchend` can suppress — otherwise a tap briefly focuses the editor and
1182
+ * raises the software keyboard before the handler opens its own surface
1183
+ * (such as a lightbox).
1184
+ */
1185
+ declare function defineImageClickHandler(onClick: ImageClickHandler): PlainExtension;
1186
+ //#endregion
1143
1187
  //#region src/extensions/link-click.d.ts
1144
1188
  interface LinkClickPayload {
1145
1189
  href: string;
@@ -1147,6 +1191,11 @@ interface LinkClickPayload {
1147
1191
  * The originating click, or the `Enter`/`Mod-Enter` key press that followed the link.
1148
1192
  */
1149
1193
  event: MouseEvent | KeyboardEvent;
1194
+ /**
1195
+ * Whether the platform's mod key (`Command` on Apple, `Ctrl` elsewhere) was held
1196
+ * beyond the gesture that triggered the activation.
1197
+ */
1198
+ mod: boolean;
1150
1199
  }
1151
1200
  type LinkClickHandler = (payload: LinkClickPayload) => void;
1152
1201
  interface LinkCopyPayload {
@@ -1171,6 +1220,11 @@ interface TagClickPayload {
1171
1220
  * Read modifier keys or position a popover from it.
1172
1221
  */
1173
1222
  event: MouseEvent | KeyboardEvent;
1223
+ /**
1224
+ * Whether the platform's mod key (`Command` on Apple, `Ctrl` elsewhere) was held
1225
+ * beyond the gesture that triggered the activation.
1226
+ */
1227
+ mod: boolean;
1174
1228
  }
1175
1229
  type TagClickHandler = (payload: TagClickPayload) => void;
1176
1230
  /**
@@ -1192,6 +1246,11 @@ interface WikilinkClickPayload {
1192
1246
  * The originating click, or the `Enter`/`Mod-Enter` key press that followed the link.
1193
1247
  */
1194
1248
  event: MouseEvent | KeyboardEvent;
1249
+ /**
1250
+ * Whether the platform's mod key (`Command` on Apple, `Ctrl` elsewhere) was held
1251
+ * beyond the gesture that triggered the activation.
1252
+ */
1253
+ mod: boolean;
1195
1254
  }
1196
1255
  type WikilinkClickHandler = (payload: WikilinkClickPayload) => void;
1197
1256
  /**
@@ -1207,49 +1266,24 @@ interface FollowLinkHandlers {
1207
1266
  onTagClick?: TagClickHandler;
1208
1267
  onFileClick?: FileClickHandler;
1209
1268
  onLinkClick?: LinkClickHandler;
1269
+ onImageClick?: ImageClickHandler;
1210
1270
  }
1211
1271
  /**
1212
1272
  * Binds `Mod-Enter` to follow the wikilink, tag, file pill, or Markdown link
1213
- * under the caret, and plain `Enter` to follow a selected atom unit, firing
1214
- * the same handlers a click does. Off a link, `Mod-Enter` falls through so
1215
- * the list keymap keeps cycling checkbox tasks; off a selected unit, `Enter`
1216
- * falls through to the regular split. High priority puts this ahead of every
1217
- * keymap binding.
1218
- */
1219
- declare function defineFollowLinkHandler(handlers: FollowLinkHandlers): PlainExtension;
1220
- //#endregion
1221
- //#region src/extensions/image-click.d.ts
1222
- /**
1223
- * Payload for {@link ImageClickHandler}.
1224
- */
1225
- interface ImageClickPayload {
1226
- /**
1227
- * The resolved source from `![alt](src)` or a claimed `![[target]]`.
1228
- */
1229
- src: string;
1230
- /**
1231
- * The image alt text.
1232
- */
1233
- alt: string;
1234
- /**
1235
- * The originating click or touch tap. Read the target or position a popover
1236
- * from it; a touch surface delivers the `touchend` instead of a click.
1237
- */
1238
- event: MouseEvent | TouchEvent;
1239
- }
1240
- type ImageClickHandler = (payload: ImageClickPayload) => void;
1241
- /**
1242
- * Call `onClick` when the user clicks or taps a rendered image preview, with
1243
- * the image's markdown `src`, `alt`, and the originating event.
1273
+ * under the caret, and plain `Enter` to follow a selected atom unit (a
1274
+ * wikilink, file pill, or image), firing the same handlers a click does.
1275
+ * "Under the caret" means strictly inside the unit: a caret merely touching
1276
+ * a unit's edge is next to it, not on it.
1277
+ * Off a link, `Mod-Enter` falls through so the list keymap keeps cycling
1278
+ * checkbox tasks; off a selected unit, `Enter` falls through to the regular
1279
+ * split. High priority puts this ahead of every keymap binding.
1244
1280
  *
1245
- * Touch taps are handled from `touchend` rather than the synthetic click:
1246
- * previews live inside the editor contenteditable, and iOS WebKit's
1247
- * tap-to-focus is a native gesture default action that only cancelling the
1248
- * `touchend` can suppress — otherwise a tap briefly focuses the editor and
1249
- * raises the software keyboard before the handler opens its own surface
1250
- * (such as a lightbox).
1281
+ * A selected-unit follow reports `mod: true` when the platform's mod key
1282
+ * (`⌘` on Apple, `Ctrl` elsewhere) was held beyond its plain-`Enter` trigger;
1283
+ * a caret follow always reports `mod: false`, its mod key being the trigger
1284
+ * itself.
1251
1285
  */
1252
- declare function defineImageClickHandler(onClick: ImageClickHandler): PlainExtension;
1286
+ declare function defineFollowLinkHandler(handlers: FollowLinkHandlers): PlainExtension;
1253
1287
  //#endregion
1254
1288
  //#region src/extensions/image.d.ts
1255
1289
  type ImageUrlResolver = (src: string) => string | undefined;
@@ -1455,6 +1489,15 @@ declare function getTextblockDisplayText(textblock: ProseMirrorNode): string;
1455
1489
  */
1456
1490
  declare function formatFileSize(bytes: number): string;
1457
1491
  //#endregion
1492
+ //#region src/utils/is-mod-event.d.ts
1493
+ /**
1494
+ * Whether the platform's mod key is held on `event`: `Command` on Apple, `Ctrl` elsewhere.
1495
+ */
1496
+ declare function isModEvent(event: MouseEvent | KeyboardEvent | TouchEvent | {
1497
+ metaKey: boolean;
1498
+ ctrlKey: boolean;
1499
+ }): boolean;
1500
+ //#endregion
1458
1501
  //#region src/utils/katex-chunk.d.ts
1459
1502
  type KaTeXRender = typeof render;
1460
1503
  //#endregion
@@ -1492,4 +1535,4 @@ declare function getSelectedText(state: EditorState): string;
1492
1535
  */
1493
1536
  declare function getVirtualElementFromRange(view: EditorView, range: PositionRange): VirtualElement;
1494
1537
  //#endregion
1495
- export { type AcceptPendingReplacementOptions, type CheckRoundTripOptions, type CodeBlockAttrs, type CodeBlockFenceStyle, type CodeToken, type DocToMarkdownOptions, EDITOR_KEY_BINDINGS, type EditorExtension, type EditorExtensionOptions, type EmbedDescriptor, 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 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, 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, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSpellCheckPlugin, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getFileKind, getIsComposing, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isMarkOfType, isNodeOfType, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, parseWikiEmbed, removeLink, renderMathInto, updateLink, wikiEmbedBasename, withPriority };
1538
+ export { type AcceptPendingReplacementOptions, type CheckRoundTripOptions, type CodeBlockAttrs, type CodeBlockFenceStyle, type CodeToken, type DocToMarkdownOptions, EDITOR_KEY_BINDINGS, type EditorExtension, type EditorExtensionOptions, type EmbedDescriptor, 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 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, 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, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSpellCheckPlugin, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getFileKind, getIsComposing, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isMarkOfType, isModEvent, isNodeOfType, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, parseWikiEmbed, removeLink, renderMathInto, updateLink, wikiEmbedBasename, withPriority };
package/dist/index.js CHANGED
@@ -4428,11 +4428,13 @@ function defineMeowdownListSerializer() {
4428
4428
  return (...args) => {
4429
4429
  return {
4430
4430
  ...nodesFromSchema(...args),
4431
- list: (node) => listToDOM({
4432
- node,
4433
- nativeList: true,
4434
- getAttributes: getListClipboardAttributes
4435
- })
4431
+ list: (node) => {
4432
+ return listToDOM({
4433
+ node,
4434
+ nativeList: true,
4435
+ getAttributes: getListClipboardAttributes
4436
+ });
4437
+ }
4436
4438
  };
4437
4439
  };
4438
4440
  }
@@ -6430,6 +6432,15 @@ function defineExitBoundaryHandler(onExitBoundary) {
6430
6432
  return withPriority$1(definePlugin(createExitBoundaryPlugin(onExitBoundary)), Priority$1.low);
6431
6433
  }
6432
6434
 
6435
+ //#endregion
6436
+ //#region src/utils/is-mod-event.ts
6437
+ /**
6438
+ * Whether the platform's mod key is held on `event`: `Command` on Apple, `Ctrl` elsewhere.
6439
+ */
6440
+ function isModEvent(event) {
6441
+ return isApple ? event.metaKey : event.ctrlKey;
6442
+ }
6443
+
6433
6444
  //#endregion
6434
6445
  //#region src/extensions/file-click.ts
6435
6446
  const fileClickKey = new PluginKey("meowdown-file-click");
@@ -6438,6 +6449,8 @@ function findFileAt(state, pos) {
6438
6449
  if (!range) return;
6439
6450
  const { href, name } = range.mark.attrs;
6440
6451
  return {
6452
+ from: range.from,
6453
+ to: range.to,
6441
6454
  href,
6442
6455
  name
6443
6456
  };
@@ -6460,7 +6473,8 @@ function defineFileClickHandler(onClick) {
6460
6473
  onClick({
6461
6474
  href: hit.href,
6462
6475
  name: hit.name,
6463
- event
6476
+ event,
6477
+ mod: isModEvent(event)
6464
6478
  });
6465
6479
  return true;
6466
6480
  } }
@@ -6748,6 +6762,126 @@ function defineFileView(options = {}) {
6748
6762
  });
6749
6763
  }
6750
6764
 
6765
+ //#endregion
6766
+ //#region src/extensions/image-click.ts
6767
+ const imageClickKey = new PluginKey("meowdown-image-click");
6768
+ function getClosestImagePreview(target) {
6769
+ return target instanceof HTMLElement && target.closest(".md-image-view-preview");
6770
+ }
6771
+ function findImageAt(state, pos) {
6772
+ const range = getMarkRangeAt(state, pos, "mdImage");
6773
+ if (!range) return;
6774
+ const { src, alt } = range.mark.attrs;
6775
+ return {
6776
+ from: range.from,
6777
+ to: range.to,
6778
+ src,
6779
+ alt
6780
+ };
6781
+ }
6782
+ /**
6783
+ * Resolve the image hit for a preview element via its content holder, not the
6784
+ * event's document position: an event on the non-editable preview lands on the
6785
+ * run boundary, where `getMarkRange` would pick the next adjacent image.
6786
+ */
6787
+ function findImageForPreview(view, preview) {
6788
+ const content = preview.closest(".md-image-view")?.querySelector(".md-image-view-content");
6789
+ if (!content) return;
6790
+ return findImageAt(view.state, view.posAtDOM(content, 0));
6791
+ }
6792
+ /**
6793
+ * Fingers wander a little during a tap; past this it is a scroll or a drag.
6794
+ */
6795
+ const TAP_MOVE_TOLERANCE = 10;
6796
+ function findTouch(touches, identifier) {
6797
+ return Array.from(touches).find((touch) => touch.identifier === identifier);
6798
+ }
6799
+ function isWithinTapTolerance(pending, touch) {
6800
+ return Math.abs(touch.clientX - pending.clientX) <= TAP_MOVE_TOLERANCE && Math.abs(touch.clientY - pending.clientY) <= TAP_MOVE_TOLERANCE;
6801
+ }
6802
+ /**
6803
+ * Call `onClick` when the user clicks or taps a rendered image preview, with
6804
+ * the image's markdown `src`, `alt`, and the originating event.
6805
+ *
6806
+ * Touch taps are handled from `touchend` rather than the synthetic click:
6807
+ * previews live inside the editor contenteditable, and iOS WebKit's
6808
+ * tap-to-focus is a native gesture default action that only cancelling the
6809
+ * `touchend` can suppress — otherwise a tap briefly focuses the editor and
6810
+ * raises the software keyboard before the handler opens its own surface
6811
+ * (such as a lightbox).
6812
+ */
6813
+ function defineImageClickHandler(onClick) {
6814
+ const pendingTaps = /* @__PURE__ */ new WeakMap();
6815
+ const handleTouchEnd = (view, event) => {
6816
+ const pending = pendingTaps.get(view);
6817
+ pendingTaps.delete(view);
6818
+ if (!pending || event.touches.length > 0) return false;
6819
+ const touch = findTouch(event.changedTouches, pending.identifier);
6820
+ if (!touch || !isWithinTapTolerance(pending, touch)) return false;
6821
+ const preview = getClosestImagePreview(event.target);
6822
+ if (!preview) return false;
6823
+ event.preventDefault();
6824
+ const hit = findImageForPreview(view, preview);
6825
+ if (hit) onClick({
6826
+ src: hit.src,
6827
+ alt: hit.alt,
6828
+ event,
6829
+ mod: isModEvent(event)
6830
+ });
6831
+ return true;
6832
+ };
6833
+ return definePlugin(new Plugin({
6834
+ key: imageClickKey,
6835
+ props: {
6836
+ handleDOMEvents: {
6837
+ pointerdown: (view, event) => {
6838
+ if (getClosestImagePreview(event.target) && event.pointerType !== "mouse") event.preventDefault();
6839
+ return false;
6840
+ },
6841
+ touchstart: (view, event) => {
6842
+ pendingTaps.delete(view);
6843
+ if (event.touches.length !== 1) return false;
6844
+ if (!getClosestImagePreview(event.target)) return false;
6845
+ if (event.target instanceof HTMLElement && event.target.closest(".md-image-resize-handle")) return false;
6846
+ const touch = event.changedTouches[0];
6847
+ if (!touch) return false;
6848
+ pendingTaps.set(view, {
6849
+ identifier: touch.identifier,
6850
+ clientX: touch.clientX,
6851
+ clientY: touch.clientY
6852
+ });
6853
+ return false;
6854
+ },
6855
+ touchmove: (view, event) => {
6856
+ const pending = pendingTaps.get(view);
6857
+ if (!pending) return false;
6858
+ const touch = findTouch(event.changedTouches, pending.identifier);
6859
+ if (touch && !isWithinTapTolerance(pending, touch)) pendingTaps.delete(view);
6860
+ return false;
6861
+ },
6862
+ touchcancel: (view) => {
6863
+ pendingTaps.delete(view);
6864
+ return false;
6865
+ },
6866
+ touchend: handleTouchEnd
6867
+ },
6868
+ handleClick: (view, _pos, event) => {
6869
+ const preview = getClosestImagePreview(event.target);
6870
+ if (!preview) return false;
6871
+ const hit = findImageForPreview(view, preview);
6872
+ if (!hit) return false;
6873
+ onClick({
6874
+ src: hit.src,
6875
+ alt: hit.alt,
6876
+ event,
6877
+ mod: isModEvent(event)
6878
+ });
6879
+ return true;
6880
+ }
6881
+ }
6882
+ }));
6883
+ }
6884
+
6751
6885
  //#endregion
6752
6886
  //#region src/extensions/mark-click.ts
6753
6887
  /**
@@ -6801,7 +6935,8 @@ function defineTagClickHandler(onClick) {
6801
6935
  findPayloadAt: (state, pos) => findTagAt(state, pos)?.tag,
6802
6936
  onClick: (tag, event) => onClick({
6803
6937
  tag,
6804
- event
6938
+ event,
6939
+ mod: isModEvent(event)
6805
6940
  })
6806
6941
  });
6807
6942
  }
@@ -6848,7 +6983,8 @@ function defineWikilinkClickHandler(onClick) {
6848
6983
  findPayloadForElement: (view, element) => findWikilinkForElement(view, element)?.target,
6849
6984
  onClick: (target, event) => onClick({
6850
6985
  target,
6851
- event
6986
+ event,
6987
+ mod: isModEvent(event)
6852
6988
  })
6853
6989
  });
6854
6990
  }
@@ -6863,173 +6999,86 @@ function createFollowLinkPlugin(handlers) {
6863
6999
  if (getIsComposing() || event.key !== "Enter" || event.shiftKey) return false;
6864
7000
  const { state } = view;
6865
7001
  const selectedAtom = getSelectedAtomRange(state);
6866
- if (!(isApple ? event.metaKey : event.ctrlKey) && !selectedAtom) return false;
6867
- const pos = selectedAtom ? selectedAtom.from + 1 : state.selection.head;
6868
- const wikilink = handlers.onWikilinkClick && findWikilinkAt(state, pos);
6869
- if (wikilink) {
6870
- handlers.onWikilinkClick?.({
6871
- target: wikilink.target,
6872
- event
6873
- });
6874
- return true;
6875
- }
6876
- const tag = handlers.onTagClick && findTagAt(state, pos);
6877
- if (tag) {
6878
- handlers.onTagClick?.({
6879
- tag: tag.tag,
6880
- event
6881
- });
6882
- return true;
6883
- }
6884
- const file = handlers.onFileClick && findFileAt(state, pos);
6885
- if (file) {
6886
- handlers.onFileClick?.({
6887
- href: file.href,
6888
- name: file.name,
6889
- event
6890
- });
6891
- return true;
6892
- }
6893
- const link = handlers.onLinkClick && getLinkUnitAt(state, pos);
6894
- if (link) {
6895
- handlers.onLinkClick?.({
6896
- href: link.href,
6897
- event
6898
- });
6899
- return true;
6900
- }
7002
+ const mod = isModEvent(event);
7003
+ if (!selectedAtom && !mod) return;
7004
+ if (selectedAtom && handlerAtomMarkTrigger(state, event, handlers, mod, selectedAtom)) return true;
7005
+ if (handlerTextMarkTrigger(state, event, handlers, mod)) return true;
6901
7006
  return false;
6902
7007
  } }
6903
7008
  });
6904
7009
  }
6905
- /**
6906
- * Binds `Mod-Enter` to follow the wikilink, tag, file pill, or Markdown link
6907
- * under the caret, and plain `Enter` to follow a selected atom unit, firing
6908
- * the same handlers a click does. Off a link, `Mod-Enter` falls through so
6909
- * the list keymap keeps cycling checkbox tasks; off a selected unit, `Enter`
6910
- * falls through to the regular split. High priority puts this ahead of every
6911
- * keymap binding.
6912
- */
6913
- function defineFollowLinkHandler(handlers) {
6914
- return withPriority$1(definePlugin(createFollowLinkPlugin(handlers)), Priority$1.high);
6915
- }
6916
-
6917
- //#endregion
6918
- //#region src/extensions/image-click.ts
6919
- const imageClickKey = new PluginKey("meowdown-image-click");
6920
- function getClosestImagePreview(target) {
6921
- return target instanceof HTMLElement && target.closest(".md-image-view-preview");
6922
- }
6923
- function findImageAt(state, pos) {
6924
- const range = getMarkRangeAt(state, pos, "mdImage");
6925
- if (!range) return;
6926
- const { src, alt } = range.mark.attrs;
6927
- return {
6928
- from: range.from,
6929
- to: range.to,
6930
- src,
6931
- alt
6932
- };
6933
- }
6934
- /**
6935
- * Resolve the image hit for a preview element via its content holder, not the
6936
- * event's document position: an event on the non-editable preview lands on the
6937
- * run boundary, where `getMarkRange` would pick the next adjacent image.
6938
- */
6939
- function findImageForPreview(view, preview) {
6940
- const content = preview.closest(".md-image-view")?.querySelector(".md-image-view-content");
6941
- if (!content) return;
6942
- return findImageAt(view.state, view.posAtDOM(content, 0));
6943
- }
6944
- /**
6945
- * Fingers wander a little during a tap; past this it is a scroll or a drag.
6946
- */
6947
- const TAP_MOVE_TOLERANCE = 10;
6948
- function findTouch(touches, identifier) {
6949
- return Array.from(touches).find((touch) => touch.identifier === identifier);
7010
+ function handlerAtomMarkTrigger(state, event, handlers, mod, selectedAtom) {
7011
+ const pos = selectedAtom.from + 1;
7012
+ const { onWikilinkClick, onFileClick, onImageClick } = handlers;
7013
+ const wikilink = onWikilinkClick && findWikilinkAt(state, pos);
7014
+ if (wikilink) {
7015
+ onWikilinkClick({
7016
+ target: wikilink.target,
7017
+ event,
7018
+ mod
7019
+ });
7020
+ return true;
7021
+ }
7022
+ const file = onFileClick && findFileAt(state, pos);
7023
+ if (file) {
7024
+ onFileClick({
7025
+ href: file.href,
7026
+ name: file.name,
7027
+ event,
7028
+ mod
7029
+ });
7030
+ return true;
7031
+ }
7032
+ const image = onImageClick && findImageAt(state, pos);
7033
+ if (image) {
7034
+ onImageClick({
7035
+ src: image.src,
7036
+ alt: image.alt,
7037
+ event,
7038
+ mod
7039
+ });
7040
+ return true;
7041
+ }
6950
7042
  }
6951
- function isWithinTapTolerance(pending, touch) {
6952
- return Math.abs(touch.clientX - pending.clientX) <= TAP_MOVE_TOLERANCE && Math.abs(touch.clientY - pending.clientY) <= TAP_MOVE_TOLERANCE;
7043
+ function handlerTextMarkTrigger(state, event, handlers, mod) {
7044
+ const pos = state.selection.head;
7045
+ const { onTagClick, onLinkClick } = handlers;
7046
+ const tag = onTagClick && findTagAt(state, pos);
7047
+ if (tag && tag.from < pos && pos < tag.to) {
7048
+ onTagClick({
7049
+ tag: tag.tag,
7050
+ event,
7051
+ mod
7052
+ });
7053
+ return true;
7054
+ }
7055
+ const link = onLinkClick && getLinkUnitAt(state, pos);
7056
+ if (link && link.unit.from < pos && pos < link.unit.to) {
7057
+ onLinkClick({
7058
+ href: link.href,
7059
+ event,
7060
+ mod
7061
+ });
7062
+ return true;
7063
+ }
6953
7064
  }
6954
7065
  /**
6955
- * Call `onClick` when the user clicks or taps a rendered image preview, with
6956
- * the image's markdown `src`, `alt`, and the originating event.
7066
+ * Binds `Mod-Enter` to follow the wikilink, tag, file pill, or Markdown link
7067
+ * under the caret, and plain `Enter` to follow a selected atom unit (a
7068
+ * wikilink, file pill, or image), firing the same handlers a click does.
7069
+ * "Under the caret" means strictly inside the unit: a caret merely touching
7070
+ * a unit's edge is next to it, not on it.
7071
+ * Off a link, `Mod-Enter` falls through so the list keymap keeps cycling
7072
+ * checkbox tasks; off a selected unit, `Enter` falls through to the regular
7073
+ * split. High priority puts this ahead of every keymap binding.
6957
7074
  *
6958
- * Touch taps are handled from `touchend` rather than the synthetic click:
6959
- * previews live inside the editor contenteditable, and iOS WebKit's
6960
- * tap-to-focus is a native gesture default action that only cancelling the
6961
- * `touchend` can suppress — otherwise a tap briefly focuses the editor and
6962
- * raises the software keyboard before the handler opens its own surface
6963
- * (such as a lightbox).
7075
+ * A selected-unit follow reports `mod: true` when the platform's mod key
7076
+ * (`⌘` on Apple, `Ctrl` elsewhere) was held beyond its plain-`Enter` trigger;
7077
+ * a caret follow always reports `mod: false`, its mod key being the trigger
7078
+ * itself.
6964
7079
  */
6965
- function defineImageClickHandler(onClick) {
6966
- const pendingTaps = /* @__PURE__ */ new WeakMap();
6967
- const handleTouchEnd = (view, event) => {
6968
- const pending = pendingTaps.get(view);
6969
- pendingTaps.delete(view);
6970
- if (!pending || event.touches.length > 0) return false;
6971
- const touch = findTouch(event.changedTouches, pending.identifier);
6972
- if (!touch || !isWithinTapTolerance(pending, touch)) return false;
6973
- const preview = getClosestImagePreview(event.target);
6974
- if (!preview) return false;
6975
- event.preventDefault();
6976
- const hit = findImageForPreview(view, preview);
6977
- if (hit) onClick({
6978
- src: hit.src,
6979
- alt: hit.alt,
6980
- event
6981
- });
6982
- return true;
6983
- };
6984
- return definePlugin(new Plugin({
6985
- key: imageClickKey,
6986
- props: {
6987
- handleDOMEvents: {
6988
- pointerdown: (view, event) => {
6989
- if (getClosestImagePreview(event.target) && event.pointerType !== "mouse") event.preventDefault();
6990
- return false;
6991
- },
6992
- touchstart: (view, event) => {
6993
- pendingTaps.delete(view);
6994
- if (event.touches.length !== 1) return false;
6995
- if (!getClosestImagePreview(event.target)) return false;
6996
- if (event.target instanceof HTMLElement && event.target.closest(".md-image-resize-handle")) return false;
6997
- const touch = event.changedTouches[0];
6998
- if (!touch) return false;
6999
- pendingTaps.set(view, {
7000
- identifier: touch.identifier,
7001
- clientX: touch.clientX,
7002
- clientY: touch.clientY
7003
- });
7004
- return false;
7005
- },
7006
- touchmove: (view, event) => {
7007
- const pending = pendingTaps.get(view);
7008
- if (!pending) return false;
7009
- const touch = findTouch(event.changedTouches, pending.identifier);
7010
- if (touch && !isWithinTapTolerance(pending, touch)) pendingTaps.delete(view);
7011
- return false;
7012
- },
7013
- touchcancel: (view) => {
7014
- pendingTaps.delete(view);
7015
- return false;
7016
- },
7017
- touchend: handleTouchEnd
7018
- },
7019
- handleClick: (view, _pos, event) => {
7020
- const preview = getClosestImagePreview(event.target);
7021
- if (!preview) return false;
7022
- const hit = findImageForPreview(view, preview);
7023
- if (!hit) return false;
7024
- onClick({
7025
- src: hit.src,
7026
- alt: hit.alt,
7027
- event
7028
- });
7029
- return true;
7030
- }
7031
- }
7032
- }));
7080
+ function defineFollowLinkHandler(handlers) {
7081
+ return withPriority$1(definePlugin(createFollowLinkPlugin(handlers)), Priority$1.high);
7033
7082
  }
7034
7083
 
7035
7084
  //#endregion
@@ -7359,7 +7408,8 @@ function defineLinkClickHandler(onClick) {
7359
7408
  findPayloadAt: (state, pos) => getLinkUnitAt(state, pos)?.href,
7360
7409
  onClick: (href, event) => onClick({
7361
7410
  href,
7362
- event
7411
+ event,
7412
+ mod: isModEvent(event)
7363
7413
  })
7364
7414
  });
7365
7415
  }
@@ -8087,4 +8137,4 @@ function getVirtualElementFromRange(view, range) {
8087
8137
  }
8088
8138
 
8089
8139
  //#endregion
8090
- export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSpellCheckPlugin, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getFileKind, getIsComposing, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isMarkOfType, isNodeOfType, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, parseWikiEmbed, removeLink, renderMathInto, updateLink, wikiEmbedBasename, withPriority };
8140
+ export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSpellCheckPlugin, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getFileKind, getIsComposing, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isMarkOfType, isModEvent, isNodeOfType, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, parseWikiEmbed, removeLink, renderMathInto, 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.64.3",
4
+ "version": "0.65.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -43,7 +43,7 @@
43
43
  "remark-stringify": "^11.0.0",
44
44
  "unicode-by-name": "^0.2.0",
45
45
  "unified": "^11.0.5",
46
- "@meowdown/markdown": "^0.64.3"
46
+ "@meowdown/markdown": "^0.65.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@ocavue/tsconfig": "^0.7.1",