@meowdown/react 0.63.1 → 0.64.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/README.md CHANGED
@@ -27,9 +27,6 @@ export function App() {
27
27
  ## Usage
28
28
 
29
29
  ```tsx
30
- import '@meowdown/core/style.css'
31
- import '@meowdown/react/style.css'
32
-
33
30
  import { MeowdownEditor, type EditorHandle } from '@meowdown/react'
34
31
  import { useRef, useCallback } from 'react'
35
32
 
@@ -50,125 +47,27 @@ export function App() {
50
47
  }
51
48
  ```
52
49
 
53
- ## API
54
-
55
- See the full API reference [here](https://npmx.dev/package-docs/@meowdown%2Freact/).
56
-
57
- Slash menu host items can include `keywords` to match hidden terms without changing the displayed label.
58
-
59
- The caret glides between positions by default; pass `caretGlide={false}` to move it instantly (hosts can also tune the duration via the `--meowdown-caret-glide` CSS variable).
60
-
61
- `MarkdownView` folds collapsed (`+`) bullets like the editor; pass `expandCollapsed` to render them expanded at every depth, for views that show slices of a note (e.g. a backlinks panel) where the source's fold state must not hide the content the view exists to show.
62
-
63
- Both `MeowdownEditor` and `MarkdownView` resolve CommonMark full, collapsed,
64
- and shortcut reference links and images. The editor keeps reference definitions
65
- visible as editable source and preserves them through `getMarkdown()`.
66
- `MarkdownView` uses the same first-definition-wins resolution but omits
67
- definition paragraphs from its rendered output. Reference links are read-only
68
- in the editor's link menu; edit their definition source to change the target.
69
-
70
- ### Wiki embeds
71
-
72
- Obsidian-style wiki embeds (`![[path]]`, with optional `|width` or
73
- `|widthxheight`) stay literal and editable unless the host classifies them.
74
- `resolveWikiEmbed` is a pure, creation-time resolver: return an image, file, or
75
- note result to reuse the corresponding Meowdown atom and its existing click
76
- hook; return `undefined` for missing or ambiguous targets.
77
-
78
- ```tsx
79
- <MeowdownEditor
80
- initialMarkdown="![[assets/photo.png|320]]"
81
- resolveWikiEmbed={({ target }) => (target.endsWith('.png') ? { kind: 'image' } : undefined)}
82
- resolveImageUrl={resolveLocalImageUrl}
83
- onImageClick={openLightbox}
84
- />
85
- ```
86
-
87
- Use `EditorHandle.revealHeading('#Section%20name')` to move the selection to a
88
- matching heading and scroll it into view after following a heading link.
50
+ ## Components
89
51
 
90
- Both `MeowdownEditor` and the read-only `MarkdownView` accept
91
- `resolveFileLink`. Return `true` to render a standard `[label](path)` Markdown
92
- link with the same file pill, metadata resolver, and click hook used by wiki
93
- file embeds; return `false` to keep it as a normal link.
94
-
95
- ```tsx
96
- <MarkdownView
97
- markdown="[Quarterly report](docs/report.pdf)"
98
- resolveFileLink={({ href }) => href.startsWith('docs/')}
99
- resolveFileInfo={resolveLocalFileInfo}
100
- onFileClick={openLocalFile}
101
- />
102
- ```
103
-
104
- ### Find in document
105
-
106
- `searchQuery` highlights every match and selects the first one at or after the
107
- caret; changing it re-anchors from wherever the selection is, so refining a
108
- query keeps the match the user is looking at. A query that matches nothing
109
- collapses the previous match selection to a caret, and an empty string (the
110
- default) clears the highlights and leaves the selection alone. `onSearchChange` reports
111
- `{ total, active }` for a match counter (`active` is one-based; `0` means the
112
- selection is not on a match), and `EditorHandle.findNext()` /
113
- `EditorHandle.findPrevious()` walk the matches and wrap at the document edges.
114
- The query is applied through `useDeferredValue`, so fast typing may skip
115
- intermediate values on a slow device.
116
-
117
- ```tsx
118
- const [query, setQuery] = useState('')
119
- const [status, setStatus] = useState({ total: 0, active: 0 })
120
-
121
- <input value={query} onChange={(event) => setQuery(event.target.value)} />
122
- <span>{status.active} / {status.total}</span>
123
- <MeowdownEditor
124
- handleRef={editorRef}
125
- searchQuery={query}
126
- onSearchChange={setStatus}
127
- />
128
- ```
129
-
130
- ### Wiki-link hover cards
131
-
132
- Mount `WikilinkHoverCard` inside `MeowdownEditor` and render host-owned preview
133
- content from the hovered wiki link's `target`. Returning `null` renders no
134
- card. The render function may also return a promise: the card stays closed
135
- until it resolves, resolving to `null` (or rejecting) renders no card, and a
136
- result that lands after the pointer moved on is discarded, so a host can look
137
- up local content without ever flashing an empty card.
138
-
139
- ```tsx
140
- <MeowdownEditor initialMarkdown="See [[Project plan]]">
141
- <WikilinkHoverCard>
142
- {async (hit) => {
143
- const note = await readLocalNote(hit.target)
144
- return note == null ? null : <LocalNotePreview note={note} />
145
- }}
146
- </WikilinkHoverCard>
147
- </MeowdownEditor>
148
- ```
149
-
150
- For local-only passive preview content, render Markdown with interaction
151
- disabled: the tree contains no anchors or focusable controls, and recognized
152
- tweet and YouTube embeds are omitted before any image resolver runs. Supply a
153
- resolver that accepts only trusted local image sources.
154
-
155
- ```tsx
156
- <MarkdownView markdown={markdown} interactive={false} resolveImageUrl={resolveLocalImageUrl} />
157
- ```
52
+ | Component | Description |
53
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------------- |
54
+ | `MeowdownEditor` | The editor. Callbacks and resolvers must be stable; pass them via `useCallback`. |
55
+ | `MarkdownView` | Read-only Markdown renderer. `interactive={false}` renders passive content for previews. |
56
+ | `WikilinkHoverCard` | Mount inside `MeowdownEditor`; renders host content for the hovered wiki link's `target`. Return `null` to render no card. |
158
57
 
159
- ### Mermaid code blocks
58
+ Common `MeowdownEditor` props:
160
59
 
161
- Fenced `mermaid` code blocks render as diagrams in both `MeowdownEditor` and
162
- `MarkdownView`. The editor shows only the preview while the caret is outside
163
- the block, then shows source and a live preview while editing it.
60
+ | Prop | What it does |
61
+ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
62
+ | `mode` | `'focus'` (default), `'show'`, or `'hide'`: how much Markdown syntax stays in view |
63
+ | `searchQuery` / `onSearchChange` | Find in document, with `EditorHandle.findNext()` / `findPrevious()` |
64
+ | `onWikilinkClick` / `onLinkClick` / `onTagClick` / `onImageClick` / `onFileClick` | Click handling for the rendered atoms |
65
+ | `resolveImageUrl` / `resolveWikiEmbed` / `resolveFileLink` / `resolveFileInfo` | Resolve and classify local content |
66
+ | `onFilePaste` | Persist pasted or dropped files |
67
+ | `onSlashMenuSearch` / `onTagSearch` / `onWikilinkSearch` / `onSelectionMenuSearch` | Search menus for `/`, `#`, `[[`, and selection commands |
68
+ | `readOnly` / `placeholder` / `blockHandle` / `caretGlide` / `embedPaste` / `linkPaste` / `bulletAfterHeading` | Behavior toggles |
164
69
 
165
- Rendering uses `beautiful-mermaid`, which currently supports Flowchart, State,
166
- Sequence, Class, ER, and XY Chart diagrams. Unsupported syntax renders an error
167
- instead of an empty preview. Override `--meowdown-mermaid-bg`,
168
- `--meowdown-mermaid-fg`, `--meowdown-mermaid-line`,
169
- `--meowdown-mermaid-accent`, `--meowdown-mermaid-muted`,
170
- `--meowdown-mermaid-surface`, `--meowdown-mermaid-border`, or
171
- `--meowdown-mermaid-error` to customize the diagram palette.
70
+ Every prop, callback, and `EditorHandle` method is documented in the [API reference](https://npmx.dev/package-docs/@meowdown%2Freact/).
172
71
 
173
72
  ## Styling
174
73
 
package/dist/index.d.ts CHANGED
@@ -6,7 +6,9 @@ import { useEditor, useExtension, useKeymap } from "@prosekit/react";
6
6
  type TimeFormat = '12' | '24';
7
7
  //#endregion
8
8
  //#region src/components/types.d.ts
9
- /** A selection to restore: an exact JSON selection, or a document edge. */
9
+ /**
10
+ * A selection to restore: an exact JSON selection, or a document edge.
11
+ */
10
12
  type SelectionHint = SelectionJSON$1 | 'start' | 'end';
11
13
  /**
12
14
  * The current Markdown and selection. Selection positions are in the mounted
@@ -24,7 +26,9 @@ interface EditorHandle {
24
26
  * documents; call it on demand (e.g. throttled) instead of on every change.
25
27
  */
26
28
  getMarkdown: () => string;
27
- /** Replaces the whole document as a single undoable edit. */
29
+ /**
30
+ * Replaces the whole document as a single undoable edit.
31
+ */
28
32
  setMarkdown: (markdown: string) => void;
29
33
  /**
30
34
  * Parses `markdown` and inserts it at the current selection as a single
@@ -36,7 +40,9 @@ interface EditorHandle {
36
40
  * `onDocChange`: the host cannot know the resulting document.
37
41
  */
38
42
  insertMarkdown: (markdown: string) => void;
39
- /** Returns the current Markdown and selection. */
43
+ /**
44
+ * Returns the current Markdown and selection.
45
+ */
40
46
  getState: () => EditorStateSnapshot;
41
47
  /**
42
48
  * Replaces the document (if `markdown` is given) and restores `selection`:
@@ -51,13 +57,21 @@ interface EditorHandle {
51
57
  * this when data consulted by a stable resolver changes out of band.
52
58
  */
53
59
  refreshMarkdownRendering: () => void;
54
- /** Returns the current selection. */
60
+ /**
61
+ * Returns the current selection.
62
+ */
55
63
  getSelection: () => SelectionJSON$1;
56
- /** Restores a selection with the same hint semantics as `setState`. */
64
+ /**
65
+ * Restores a selection with the same hint semantics as `setState`.
66
+ */
57
67
  setSelection: (selection: SelectionHint) => void;
58
- /** Focuses the editor. */
68
+ /**
69
+ * Focuses the editor.
70
+ */
59
71
  focus: () => void;
60
- /** Scrolls the selection into view. */
72
+ /**
73
+ * Scrolls the selection into view.
74
+ */
61
75
  scrollIntoView: () => void;
62
76
  /**
63
77
  * Moves the selection to the first heading matching a decoded `#fragment`
@@ -82,7 +96,9 @@ interface EditorHandle {
82
96
  * is how a retry starts.
83
97
  */
84
98
  startPendingReplacement: (options: StartPendingReplacementOptions) => boolean;
85
- /** Appends streamed text to the staged replacement. */
99
+ /**
100
+ * Appends streamed text to the staged replacement.
101
+ */
86
102
  appendPendingReplacementText: (text: string) => void;
87
103
  /**
88
104
  * Applies the staged replacement to the document as a single edit. Pass a
@@ -90,11 +106,17 @@ interface EditorHandle {
90
106
  * below" on a replace stage).
91
107
  */
92
108
  acceptPendingReplacement: (options?: AcceptPendingReplacementOptions) => void;
93
- /** Clears the staged replacement without touching the document. */
109
+ /**
110
+ * Clears the staged replacement without touching the document.
111
+ */
94
112
  discardPendingReplacement: () => void;
95
- /** Selects the next match of the current search query, wrapping at the document end. */
113
+ /**
114
+ * Selects the next match of the current search query, wrapping at the document end.
115
+ */
96
116
  findNext: () => void;
97
- /** Selects the previous match of the current search query, wrapping at the document start. */
117
+ /**
118
+ * Selects the previous match of the current search query, wrapping at the document start.
119
+ */
98
120
  findPrevious: () => void;
99
121
  /**
100
122
  * Escape hatch: the underlying ProseKit editor, or `undefined` when the
@@ -102,17 +124,29 @@ interface EditorHandle {
102
124
  */
103
125
  readonly editor: TypedEditor | undefined;
104
126
  }
105
- /** One row of host items in the slash menu. The host ranks the rows; the menu does not re-sort. */
127
+ /**
128
+ * One row of host items in the slash menu. The host ranks the rows; the menu does not re-sort.
129
+ */
106
130
  interface SlashMenuItem {
107
- /** Stable row key; defaults to `label`. */
131
+ /**
132
+ * Stable row key; defaults to `label`.
133
+ */
108
134
  id?: string;
109
- /** Display text, matched against the typed query like the built-in items. */
135
+ /**
136
+ * Display text, matched against the typed query like the built-in items.
137
+ */
110
138
  label: string;
111
- /** Extra match terms beyond the label; never displayed. */
139
+ /**
140
+ * Extra match terms beyond the label; never displayed.
141
+ */
112
142
  keywords?: string[];
113
- /** Secondary text shown beside the label. */
143
+ /**
144
+ * Secondary text shown beside the label.
145
+ */
114
146
  detail?: string;
115
- /** Runs after the menu closes and the typed `/query` text is removed. */
147
+ /**
148
+ * Runs after the menu closes and the typed `/query` text is removed.
149
+ */
116
150
  onSelect: () => void;
117
151
  }
118
152
  /**
@@ -121,15 +155,25 @@ interface SlashMenuItem {
121
155
  * show after the built-in items, either synchronously or as a promise.
122
156
  */
123
157
  type SlashMenuSearchHandler = (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
124
- /** One row in the tag menu. The host ranks the rows; the menu does not re-sort. */
158
+ /**
159
+ * One row in the tag menu. The host ranks the rows; the menu does not re-sort.
160
+ */
125
161
  interface TagItem {
126
- /** Inserted as `#tag `. */
162
+ /**
163
+ * Inserted as `#tag `.
164
+ */
127
165
  tag: string;
128
- /** Display text; defaults to `#tag`. */
166
+ /**
167
+ * Display text; defaults to `#tag`.
168
+ */
129
169
  label?: string;
130
- /** Secondary text shown beside the label. */
170
+ /**
171
+ * Secondary text shown beside the label.
172
+ */
131
173
  detail?: string;
132
- /** Side effect run after the tag is inserted (e.g. create the tag). */
174
+ /**
175
+ * Side effect run after the tag is inserted (e.g. create the tag).
176
+ */
133
177
  onSelect?: () => void;
134
178
  }
135
179
  /**
@@ -138,15 +182,25 @@ interface TagItem {
138
182
  * synchronously or as a promise.
139
183
  */
140
184
  type TagSearchHandler = (query: string) => TagItem[] | Promise<TagItem[]>;
141
- /** One row in the wikilink menu. The host ranks the rows; the menu does not re-sort. */
185
+ /**
186
+ * One row in the wikilink menu. The host ranks the rows; the menu does not re-sort.
187
+ */
142
188
  interface WikilinkItem {
143
- /** Inserted as `[[target]]`. */
189
+ /**
190
+ * Inserted as `[[target]]`.
191
+ */
144
192
  target: string;
145
- /** Display text; defaults to `target`. */
193
+ /**
194
+ * Display text; defaults to `target`.
195
+ */
146
196
  label?: string;
147
- /** Secondary text shown beside the label. */
197
+ /**
198
+ * Secondary text shown beside the label.
199
+ */
148
200
  detail?: string;
149
- /** Side effect run after the link is inserted (e.g. create the note). */
201
+ /**
202
+ * Side effect run after the link is inserted (e.g. create the note).
203
+ */
150
204
  onSelect?: () => void;
151
205
  }
152
206
  /**
@@ -156,24 +210,42 @@ interface WikilinkItem {
156
210
  * a promise.
157
211
  */
158
212
  type WikilinkSearchHandler = (query: string) => WikilinkItem[] | Promise<WikilinkItem[]>;
159
- /** The selection the selection menu was opened over. */
213
+ /**
214
+ * The selection the selection menu was opened over.
215
+ */
160
216
  interface SelectionMenuContext {
161
- /** The selected text, with block boundaries as blank lines. */
217
+ /**
218
+ * The selected text, with block boundaries as blank lines.
219
+ */
162
220
  selectedText: string;
163
- /** Start of the selection. */
221
+ /**
222
+ * Start of the selection.
223
+ */
164
224
  from: number;
165
- /** End of the selection. */
225
+ /**
226
+ * End of the selection.
227
+ */
166
228
  to: number;
167
229
  }
168
- /** One row in the selection menu. The host ranks the rows; the menu does not re-sort. */
230
+ /**
231
+ * One row in the selection menu. The host ranks the rows; the menu does not re-sort.
232
+ */
169
233
  interface SelectionMenuItem {
170
- /** Stable identity for the row. */
234
+ /**
235
+ * Stable identity for the row.
236
+ */
171
237
  id: string;
172
- /** Display text. */
238
+ /**
239
+ * Display text.
240
+ */
173
241
  label: string;
174
- /** Secondary text shown beside the label. */
242
+ /**
243
+ * Secondary text shown beside the label.
244
+ */
175
245
  detail?: string;
176
- /** Runs when the row is picked, with the selection the menu was opened over. */
246
+ /**
247
+ * Runs when the row is picked, with the selection the menu was opened over.
248
+ */
177
249
  onSelect: (context: SelectionMenuContext) => void;
178
250
  }
179
251
  /**
@@ -182,7 +254,9 @@ interface SelectionMenuItem {
182
254
  * returns the rows to show, either synchronously or as a promise.
183
255
  */
184
256
  type SelectionMenuSearchHandler = (query: string, context: SelectionMenuContext) => SelectionMenuItem[] | Promise<SelectionMenuItem[]>;
185
- /** Reports how a pending replacement ended and its final staged value. */
257
+ /**
258
+ * Reports how a pending replacement ended and its final staged value.
259
+ */
186
260
  type PendingReplacementResolveHandler = (outcome: PendingReplacementOutcome, pending: PendingReplacement) => void;
187
261
  //#endregion
188
262
  //#region src/components/editor.d.ts
@@ -331,7 +405,9 @@ interface EditorProps {
331
405
  * other file. Return `undefined` to decline. Pass a stable function.
332
406
  */
333
407
  onFilePaste?: FilePasteOptions['onFilePaste'];
334
- /** Called when persisting a pasted/dropped file throws. */
408
+ /**
409
+ * Called when persisting a pasted/dropped file throws.
410
+ */
335
411
  onFileSaveError?: FilePasteOptions['onFileSaveError'];
336
412
  /**
337
413
  * Called when the user clicks a rendered image, with its markdown `src`,
@@ -360,7 +436,9 @@ interface EditorProps {
360
436
  * e.g. `->` with `→` and `(c)` with `©`. On by default.
361
437
  */
362
438
  substitution?: boolean;
363
- /** Handles a leading `---` frontmatter block (off by default). */
439
+ /**
440
+ * Handles a leading `---` frontmatter block (off by default).
441
+ */
364
442
  frontmatter?: boolean;
365
443
  /**
366
444
  * Shows the per-block gutter handle: a drag grip for reordering blocks and a
@@ -381,7 +459,9 @@ interface EditorProps {
381
459
  * receives the editor state. Pass a stable function.
382
460
  */
383
461
  placeholder?: PlaceholderOptions['placeholder'];
384
- /** Makes the editor read-only. */
462
+ /**
463
+ * Makes the editor read-only.
464
+ */
385
465
  readOnly?: boolean;
386
466
  /**
387
467
  * Enables the browser's native spell checking. Defaults to the browser's
@@ -405,13 +485,21 @@ interface EditorProps {
405
485
  * for "15:45". Defaults to '12'.
406
486
  */
407
487
  timeFormat?: TimeFormat;
408
- /** Class on the editable root (the contenteditable). */
488
+ /**
489
+ * Class on the editable root (the contenteditable).
490
+ */
409
491
  editorClassName?: string;
410
- /** Class on the outer `.meowdown` wrapper div. */
492
+ /**
493
+ * Class on the outer `.meowdown` wrapper div.
494
+ */
411
495
  wrapperClassName?: string;
412
- /** Imperative handle for the editor. */
496
+ /**
497
+ * Imperative handle for the editor.
498
+ */
413
499
  handleRef?: Ref<EditorHandle>;
414
- /** Nodes rendered inside the editor's ProseKit context. */
500
+ /**
501
+ * Nodes rendered inside the editor's ProseKit context.
502
+ */
415
503
  children?: ReactNode;
416
504
  }
417
505
  /**
@@ -423,7 +511,9 @@ interface EditorProps {
423
511
  declare function MeowdownEditor({ mode, initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution, frontmatter, blockHandle, caretGlide, placeholder, readOnly, spellCheck, searchQuery, onSearchChange, timeFormat, editorClassName, wrapperClassName, handleRef, children }: EditorProps): ReactElement;
424
512
  //#endregion
425
513
  //#region src/components/markdown-view.d.ts
426
- /** Payload for {@link TaskClickHandler}. */
514
+ /**
515
+ * Payload for {@link TaskClickHandler}.
516
+ */
427
517
  interface TaskClickPayload {
428
518
  /**
429
519
  * Zero-based position of the clicked checkbox among all the checkboxes this
@@ -432,9 +522,13 @@ interface TaskClickPayload {
432
522
  * same source.
433
523
  */
434
524
  index: number;
435
- /** The checkbox's rendered state. The view never flips it — see the handler doc. */
525
+ /**
526
+ * The checkbox's rendered state. The view never flips it — see the handler doc.
527
+ */
436
528
  checked: boolean;
437
- /** The item's list marker as written (`+` renders a circle checkbox, `-`/`*` a square). */
529
+ /**
530
+ * The item's list marker as written (`+` renders a circle checkbox, `-`/`*` a square).
531
+ */
438
532
  marker: ListMarker;
439
533
  /**
440
534
  * First line of the item's own inline content, exactly as it appears in the
@@ -443,7 +537,9 @@ interface TaskClickPayload {
443
537
  * mismatch instead of toggling the wrong item.
444
538
  */
445
539
  text: string;
446
- /** The originating click. Read modifier keys or position a popover from it. */
540
+ /**
541
+ * The originating click. Read modifier keys or position a popover from it.
542
+ */
447
543
  event: globalThis.MouseEvent;
448
544
  }
449
545
  /**
@@ -453,11 +549,17 @@ interface TaskClickPayload {
453
549
  */
454
550
  type TaskClickHandler = (payload: TaskClickPayload) => void;
455
551
  interface MarkdownViewProps {
456
- /** The Markdown to render. Live: changing it re-renders the content. */
552
+ /**
553
+ * The Markdown to render. Live: changing it re-renders the content.
554
+ */
457
555
  markdown: string;
458
- /** Mark mode for the read-only view. Defaults to `'hide'`. */
556
+ /**
557
+ * Mark mode for the read-only view. Defaults to `'hide'`.
558
+ */
459
559
  markMode?: MarkMode;
460
- /** Peel a leading YAML frontmatter block before rendering. Off by default. */
560
+ /**
561
+ * Peel a leading YAML frontmatter block before rendering. Off by default.
562
+ */
461
563
  frontmatter?: boolean;
462
564
  /**
463
565
  * Whether rendered links, images, file pills, and task checkboxes can be activated.
@@ -473,28 +575,46 @@ interface MarkdownViewProps {
473
575
  * the view exists to show.
474
576
  */
475
577
  expandCollapsed?: boolean;
476
- /** Map an image `src` to a displayable URL, or `undefined` to skip it. */
578
+ /**
579
+ * Map an image `src` to a displayable URL, or `undefined` to skip it.
580
+ */
477
581
  resolveImageUrl?: (src: string) => string | undefined;
478
582
  /**
479
583
  * Claim a `[label](url)` link as a file pill instead of a regular link.
480
584
  * Must be pure; return `false` for links that should render normally.
481
585
  */
482
586
  resolveFileLink?: FileLinkResolver;
483
- /** Classify `![[target]]` as an image, file, or note; unresolved source stays literal. */
587
+ /**
588
+ * Classify `![[target]]` as an image, file, or note; unresolved source stays literal.
589
+ */
484
590
  resolveWikiEmbed?: WikiEmbedResolver;
485
- /** Resolve metadata shown on a file pill. */
591
+ /**
592
+ * Resolve metadata shown on a file pill.
593
+ */
486
594
  resolveFileInfo?: FileInfoResolver;
487
- /** Called when a rendered wikilink is clicked. Pass a stable function. */
595
+ /**
596
+ * Called when a rendered wikilink is clicked. Pass a stable function.
597
+ */
488
598
  onWikilinkClick?: WikilinkClickHandler;
489
- /** Called when a rendered Markdown link is clicked. Pass a stable function. */
599
+ /**
600
+ * Called when a rendered Markdown link is clicked. Pass a stable function.
601
+ */
490
602
  onLinkClick?: LinkClickHandler;
491
- /** Called when a rendered image is clicked. Pass a stable function. */
603
+ /**
604
+ * Called when a rendered image is clicked. Pass a stable function.
605
+ */
492
606
  onImageClick?: ImageClickHandler;
493
- /** Called when a rendered file pill is clicked. Pass a stable function. */
607
+ /**
608
+ * Called when a rendered file pill is clicked. Pass a stable function.
609
+ */
494
610
  onFileClick?: FileClickHandler;
495
- /** Called when a rendered task checkbox is clicked. Pass a stable function. */
611
+ /**
612
+ * Called when a rendered task checkbox is clicked. Pass a stable function.
613
+ */
496
614
  onTaskClick?: TaskClickHandler;
497
- /** Extra class on the content root (alongside `ProseMirror meowdown-content`). */
615
+ /**
616
+ * Extra class on the content root (alongside `ProseMirror meowdown-content`).
617
+ */
498
618
  className?: string;
499
619
  }
500
620
  /**
@@ -511,7 +631,9 @@ interface MarkdownViewProps {
511
631
  declare function MarkdownView({ markdown, markMode, frontmatter, interactive, expandCollapsed, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onWikilinkClick, onLinkClick, onImageClick, onFileClick, onTaskClick, className }: MarkdownViewProps): ReactElement;
512
632
  //#endregion
513
633
  //#region src/components/wikilink-hover-card.d.ts
514
- /** Props for {@link WikilinkHoverCard}. */
634
+ /**
635
+ * Props for {@link WikilinkHoverCard}.
636
+ */
515
637
  interface WikilinkHoverCardProps {
516
638
  /**
517
639
  * Render the card body from the hovered wiki link. Returning `null` renders
@@ -522,7 +644,9 @@ interface WikilinkHoverCardProps {
522
644
  * re-runs it for the current link.
523
645
  */
524
646
  readonly children: (hit: WikilinkHoverHit) => ReactNode | Promise<ReactNode>;
525
- /** Optional class applied to the popup, after the default card surface. */
647
+ /**
648
+ * Optional class applied to the popup, after the default card surface.
649
+ */
526
650
  readonly className?: string;
527
651
  }
528
652
  /**
package/dist/index.js CHANGED
@@ -487,10 +487,14 @@ function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick,
487
487
 
488
488
  //#endregion
489
489
  //#region src/hooks/use-delayed-flag.ts
490
- /** Delay before the flag opens, in ms. */
490
+ /**
491
+ * Delay before the flag opens, in ms.
492
+ */
491
493
  const OPEN_DELAY$1 = 400;
492
- /** Grace before the flag closes, in ms. The window lets a pointer travel from
493
- * the hovered link onto the popover it anchors. */
494
+ /**
495
+ * Grace before the flag closes, in ms. The window lets a pointer travel from
496
+ * the hovered link onto the popover it anchors.
497
+ */
494
498
  const CLOSE_DELAY$1 = 300;
495
499
  /**
496
500
  * Mirrors `value` into a boolean that flips true `openDelay`ms after `value`
@@ -527,14 +531,18 @@ var link_menu_module_default = {
527
531
 
528
532
  //#endregion
529
533
  //#region src/components/link-menu.tsx
530
- /** Select the link unit so the text-backed commands target it, and keep the
531
- * editor focused so its virtual selection stays visible behind the popover. */
534
+ /**
535
+ * Select the link unit so the text-backed commands target it, and keep the
536
+ * editor focused so its virtual selection stays visible behind the popover.
537
+ */
532
538
  function selectLinkUnit(editor, link) {
533
539
  editor.commands.selectText(link.unit.from, link.unit.to);
534
540
  editor.focus();
535
541
  }
536
- /** A Base UI popover anchored at `anchor`. Base UI dismisses it on an outside
537
- * press or Escape, both routed through `onClose`. */
542
+ /**
543
+ * A Base UI popover anchored at `anchor`. Base UI dismisses it on an outside
544
+ * press or Escape, both routed through `onClose`.
545
+ */
538
546
  function LinkPopover({ anchor, onClose, onPopupHover, children }) {
539
547
  return /* @__PURE__ */ jsx(Popover.Root, {
540
548
  open: true,
@@ -558,7 +566,9 @@ function LinkPopover({ anchor, onClose, onPopupHover, children }) {
558
566
  }) })
559
567
  });
560
568
  }
561
- /** The hover preview: the url plus copy, edit, and remove actions. */
569
+ /**
570
+ * The hover preview: the url plus copy, edit, and remove actions.
571
+ */
562
572
  function LinkInfoContent({ href, onLinkClick, onLinkCopy, onEdit, onRemove }) {
563
573
  return /* @__PURE__ */ jsxs("div", {
564
574
  className: link_menu_module_default.Row,
@@ -605,7 +615,9 @@ function LinkInfoContent({ href, onLinkClick, onLinkCopy, onEdit, onRemove }) {
605
615
  ]
606
616
  });
607
617
  }
608
- /** The url and title form, opened by `Mod-k` or the preview's edit button. */
618
+ /**
619
+ * The url and title form, opened by `Mod-k` or the preview's edit button.
620
+ */
609
621
  function LinkEditContent({ link, onSubmit }) {
610
622
  const hrefInputRef = useRef(null);
611
623
  const titleInputRef = useRef(null);
@@ -658,17 +670,19 @@ function LinkMenu({ onLinkClick, onLinkCopy }) {
658
670
  const [overPopup, setOverPopup] = useState(false);
659
671
  const [edit, setEdit] = useState();
660
672
  const hoverOpen = useDelayedFlag(onLink || overPopup);
661
- useExtension$1(useMemo(() => {
673
+ const linkHoverExtension = useMemo(() => {
662
674
  return defineLinkHoverHandler((hit) => {
663
675
  setOnLink(!!hit);
664
676
  if (hit) setHover(hit.payload);
665
677
  });
666
- }, []));
667
- useExtension$1(useMemo(() => {
678
+ }, []);
679
+ useExtension$1(linkHoverExtension);
680
+ const linkEditExtension = useMemo(() => {
668
681
  return defineLinkEditKeymap((options) => {
669
682
  setEdit(options);
670
683
  });
671
- }, []));
684
+ }, []);
685
+ useExtension$1(linkEditExtension);
672
686
  const closeHover = useCallback(() => {
673
687
  setOnLink(false);
674
688
  setOverPopup(false);
@@ -771,9 +785,13 @@ var pending_replacement_preview_module_default = {
771
785
  * 14rem max-height plus the footer, borders, and the anchor offset.
772
786
  */
773
787
  const PREVIEW_CLEARANCE = 320;
774
- /** Minimum gap (px) kept above the anchor line when scrolling to make room. */
788
+ /**
789
+ * Minimum gap (px) kept above the anchor line when scrolling to make room.
790
+ */
775
791
  const SCROLL_TOP_MARGIN = 16;
776
- /** The nearest ancestor that can scroll vertically, or the page scroller. */
792
+ /**
793
+ * The nearest ancestor that can scroll vertically, or the page scroller.
794
+ */
777
795
  function closestScrollable(element) {
778
796
  for (let node = element.parentElement; node; node = node.parentElement) {
779
797
  const { overflowY } = getComputedStyle(node);
@@ -1002,8 +1020,10 @@ function SelectionMenu({ onSelectionMenuSearch, context, onOpen, onClose, afford
1002
1020
  });
1003
1021
  return null;
1004
1022
  }
1005
- /** The menu content. Mounted only while the menu is open, so its filter state
1006
- * resets naturally on close. */
1023
+ /**
1024
+ * The menu content. Mounted only while the menu is open, so its filter state
1025
+ * resets naturally on close.
1026
+ */
1007
1027
  function SelectionMenuPopup({ onSelectionMenuSearch, context, onClose }) {
1008
1028
  const [query, setQuery] = useState("");
1009
1029
  const [items, setItems] = useState([]);
@@ -1079,11 +1099,15 @@ function SelectionMenuPopup({ onSelectionMenuSearch, context, onClose }) {
1079
1099
 
1080
1100
  //#endregion
1081
1101
  //#region src/utils/date-format.ts
1082
- /** Formats the current wall-clock time for the `/now` slash command. */
1102
+ /**
1103
+ * Formats the current wall-clock time for the `/now` slash command.
1104
+ */
1083
1105
  function formatNowTime(timeFormat) {
1084
1106
  return formatTime(/* @__PURE__ */ new Date(), timeFormat);
1085
1107
  }
1086
- /** Formats a given time as `3:45pm` ('12') or `15:45` ('24'). */
1108
+ /**
1109
+ * Formats a given time as `3:45pm` ('12') or `15:45` ('24').
1110
+ */
1087
1111
  function formatTime(date, timeFormat) {
1088
1112
  return timeFormat === "12" ? formatTime12(date) : formatTime24(date);
1089
1113
  }
@@ -1689,11 +1713,13 @@ function findHeadingPosition(doc, fragment) {
1689
1713
  }
1690
1714
  function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution = true, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, searchQuery = "", onSearchChange, timeFormat, editorClassName, ref, children }) {
1691
1715
  const [editor] = useState(() => {
1692
- const editor = createEditor({ extension: union(defineEditorExtension({
1716
+ const baseExtension = defineEditorExtension({
1693
1717
  resolveFileLink,
1694
1718
  resolveWikiEmbed,
1695
1719
  markMode
1696
- }), defineCodeBlockView()) });
1720
+ });
1721
+ const extension = union(baseExtension, defineCodeBlockView());
1722
+ const editor = createEditor({ extension });
1697
1723
  if (initialMarkdown) editor.setContent(markdownToDoc(initialMarkdown, {
1698
1724
  nodes: editor.nodes,
1699
1725
  frontmatter
@@ -2082,9 +2108,7 @@ function attributesToProps(attributes = {}, nodeName) {
2082
2108
  case BOOLEAN:
2083
2109
  props[propName] = true;
2084
2110
  break;
2085
- case OVERLOADED_BOOLEAN:
2086
- if (attributeValue === "") props[propName] = true;
2087
- break;
2111
+ case OVERLOADED_BOOLEAN: if (attributeValue === "") props[propName] = true;
2088
2112
  }
2089
2113
  continue;
2090
2114
  }
@@ -2147,7 +2171,8 @@ function outputSpecToReact(spec, content, context) {
2147
2171
  reactProps.tabIndex = -1;
2148
2172
  }
2149
2173
  }
2150
- return createElement(tag, reactProps, ...rest.map((child) => outputSpecToReact(child, content, context)));
2174
+ const reactChildren = rest.map((child) => outputSpecToReact(child, content, context));
2175
+ return createElement(tag, reactProps, ...reactChildren);
2151
2176
  }
2152
2177
  function WikilinkChip(props) {
2153
2178
  const { target, display, onWikilinkClick, children } = props;
@@ -2381,7 +2406,9 @@ function MathView(props) {
2381
2406
  })]
2382
2407
  });
2383
2408
  }
2384
- /** A `math` code block: the rendered formula alone, the source while KaTeX loads. */
2409
+ /**
2410
+ * A `math` code block: the rendered formula alone, the source while KaTeX loads.
2411
+ */
2385
2412
  function MathCodeBlock({ code }) {
2386
2413
  const katex = useKaTeX(true);
2387
2414
  if (!katex) return /* @__PURE__ */ jsx(CodeBlock, {
@@ -2409,7 +2436,9 @@ function MermaidCodeBlock({ code }) {
2409
2436
  "data-testid": "code-block-mermaid-preview"
2410
2437
  });
2411
2438
  }
2412
- /** Wrap inline `children` in one mark, special-casing the view/link marks. */
2439
+ /**
2440
+ * Wrap inline `children` in one mark, special-casing the view/link marks.
2441
+ */
2413
2442
  function wrapMark(mark, children, context) {
2414
2443
  switch (mark.type.name) {
2415
2444
  case "mdWikilink": {
@@ -2515,7 +2544,9 @@ function renderInline(node, context) {
2515
2544
  marks: Mark.setFrom(marks)
2516
2545
  })), 0, context);
2517
2546
  }
2518
- /** A collapsed list renders as an expanded one */
2547
+ /**
2548
+ * A collapsed list renders as an expanded one
2549
+ */
2519
2550
  function expandCollapsedList(node) {
2520
2551
  const attrs = node.attrs;
2521
2552
  if (!attrs.collapsed) return node;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/react",
3
3
  "type": "module",
4
- "version": "0.63.1",
4
+ "version": "0.64.1",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -29,9 +29,9 @@
29
29
  "beautiful-mermaid": "^1.1.3",
30
30
  "clsx": "^2.1.1",
31
31
  "github-slugger": "^2.0.0",
32
- "lucide-react": "^1.27.0",
32
+ "lucide-react": "^1.28.0",
33
33
  "react-property": "^2.0.2",
34
- "@meowdown/core": "0.63.1"
34
+ "@meowdown/core": "0.64.1"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "react": "^19.0.0",
@@ -48,14 +48,14 @@
48
48
  "devDependencies": {
49
49
  "@css-modules-kit/codegen": "^1.4.0",
50
50
  "@ocavue/tsconfig": "^0.7.1",
51
- "@tsdown/css": "^0.22.14",
52
- "@types/react": "^19.2.17",
53
- "@types/react-dom": "^19.2.3",
51
+ "@tsdown/css": "^0.23.0-beta.2",
52
+ "@types/react": "^19.2.18",
53
+ "@types/react-dom": "^19.2.4",
54
54
  "@vitest/browser-playwright": "^4.1.10",
55
55
  "dedent": "^1.7.2",
56
56
  "react": "^19.2.8",
57
57
  "react-dom": "^19.2.8",
58
- "tsdown": "^0.22.14",
58
+ "tsdown": "^0.23.0-beta.2",
59
59
  "vitest": "^4.1.10",
60
60
  "vitest-browser-commands": "^0.2.1",
61
61
  "vitest-browser-react": "^2.2.0",