@meowdown/core 0.63.1 → 0.64.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.
- package/README.md +25 -68
- package/dist/index.d.ts +32 -1
- package/dist/index.js +7811 -39
- package/dist/katex-chunk-CfdGokmi.js +3 -0
- package/dist/style.css +2 -0
- package/package.json +4 -4
- package/dist/katex-chunk-Ch7kgbXr.js +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @meowdown/core
|
|
2
2
|
|
|
3
|
-
The engine
|
|
3
|
+
The engine behind Meowdown, a hybrid (live-preview) Markdown editor: Markdown
|
|
4
|
+
parsing, serializing, editing commands, and a default theme. Framework-free;
|
|
5
|
+
pairs with [@meowdown/react](https://www.npmjs.com/package/@meowdown/react).
|
|
4
6
|
|
|
5
7
|
[**Live demo**](https://meowdown.vercel.app/)
|
|
6
8
|
|
|
@@ -25,40 +27,22 @@ editor.mount(document.querySelector<HTMLElement>('#editor')!)
|
|
|
25
27
|
const markdown = docToMarkdown(editor.state.doc)
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- Ordered lists (`1. item`, `2) item`, etc.)
|
|
35
|
-
- Blockquotes (`> quote`)
|
|
36
|
-
- Fenced code blocks (` ```lang\ncode\n``` `), keeping tilde fences (`~~~`) and fence lengths through a round-trip
|
|
37
|
-
- Indented code blocks (four leading spaces)
|
|
38
|
-
- Thematic breaks (`---`, `***`, `___`)
|
|
39
|
-
- Bold (`**bold**`), italic (`*italic*`), and inline code
|
|
40
|
-
- Inline and reference links (`[text](url)`, `[text][label]`, `[label][]`, `[label]`)
|
|
41
|
-
- Inline and reference images (``, `![alt][label]`)
|
|
42
|
-
- Link reference definitions (`[label]: url "title"`)
|
|
43
|
-
- Autolinks (`<https://example.com>`)
|
|
44
|
-
- Hard line breaks
|
|
45
|
-
- GitHub Flavored Markdown (GFM)
|
|
46
|
-
- Tables, including column alignment (`:--`, `:-:`, `--:`)
|
|
47
|
-
- Strikethrough (`~~text~~`)
|
|
48
|
-
- Task lists (`- [ ]`, `- [x]`)
|
|
49
|
-
- Autolinks for `www.`, scheme, and email URLs
|
|
50
|
-
- Wikilinks (`[[target]]` and `[[target|alias]]`)
|
|
51
|
-
- Highlight (`==highlight==`)
|
|
30
|
+
## Markdown features
|
|
31
|
+
|
|
32
|
+
[CommonMark](https://commonmark.org/) and [GFM](https://github.github.com/gfm/), plus:
|
|
33
|
+
|
|
34
|
+
- Wikilinks (`[[target]]`, `[[target|alias]]`)
|
|
35
|
+
- Wiki embeds (`![[path]]`)
|
|
52
36
|
- Tags (`#tag`)
|
|
53
|
-
-
|
|
54
|
-
- Math
|
|
55
|
-
|
|
56
|
-
- Display math (`$$` fences on their own lines), kept as `$$` through a round-trip
|
|
57
|
-
- ` ```math ` fenced code blocks
|
|
37
|
+
- Highlight (`==highlight==`)
|
|
38
|
+
- Math (`$x$`, `$$x$$`, and ` ```math ` fenced code blocks), rendered as native MathML
|
|
39
|
+
- Bare-domain autolinks (`google.com`), behind a curated TLD allowlist so `README.md` and `node.js` stay plain text
|
|
58
40
|
|
|
59
41
|
## Keyboard shortcuts
|
|
60
42
|
|
|
61
|
-
`Mod` is Cmd on macOS and Ctrl elsewhere.
|
|
43
|
+
`Mod` is Cmd on macOS and Ctrl elsewhere. Formatting shortcuts insert or remove
|
|
44
|
+
the literal Markdown delimiters around the selection; heading shortcuts toggle
|
|
45
|
+
the current block to that level (or back to a paragraph).
|
|
62
46
|
|
|
63
47
|
| Key | Action | Markdown |
|
|
64
48
|
| ---------------------- | --------------------------------------------------------- | ------------------- |
|
|
@@ -89,21 +73,24 @@ const markdown = docToMarkdown(editor.state.doc)
|
|
|
89
73
|
| `Shift-Meta-ArrowDown` | Select to the document end | |
|
|
90
74
|
| `Escape` | Collapse the selection | |
|
|
91
75
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`
|
|
76
|
+
`Mod-Shift-7/8/9` wrap the current block, convert a list of a different kind in
|
|
77
|
+
place, and unwrap a list of the same kind back to a paragraph. `Alt-ArrowUp` /
|
|
78
|
+
`Alt-ArrowDown` move the block or list item with its nested children. Typing `[`
|
|
79
|
+
over a selection wraps it into an open wikilink (`[[selection`) with the
|
|
80
|
+
wikilink menu searching it. `EDITOR_KEY_BINDINGS` exports this table as a
|
|
81
|
+
literal object for host settings UIs.
|
|
95
82
|
|
|
96
83
|
## Round-trip fidelity
|
|
97
84
|
|
|
98
|
-
[`checkRoundTrip(markdown)`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-checkRoundTrip) reports how faithfully
|
|
85
|
+
[`checkRoundTrip(markdown)`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-checkRoundTrip) reports how faithfully Markdown survives a parse-then-serialize round trip: `'exact'`, `'normalizing'`, or `'lossy'`.
|
|
99
86
|
|
|
100
87
|
## Styling
|
|
101
88
|
|
|
102
|
-
`@meowdown/core/style.css` ships a default
|
|
89
|
+
`@meowdown/core/style.css` ships a default theme. Colors use `light-dark()`, so they follow the page's `color-scheme` (set `color-scheme: light dark` on `:root` for automatic dark mode). Override the `--meowdown-*` variables on `:root` or any ancestor; the full list, with a one-line description and default for each, lives in the commented `:root` block at the top of `style.css`, which is the single source of truth.
|
|
103
90
|
|
|
104
|
-
|
|
91
|
+
CSS is wrapped in `@layer meowdown` (sub-layers `meowdown.base`, `meowdown.theme`, `meowdown.editor`). An un-layered rule always beats a layered one, so any plain rule you write overrides meowdown with no `!important` and no specificity hacks. Put your overrides outside any `@layer`, or in a layer you declare after `meowdown`.
|
|
105
92
|
|
|
106
|
-
**With Tailwind CSS v4**, import `@meowdown/core/style.css` _after_ `@import 'tailwindcss'` so the `meowdown` layer sorts after Tailwind's `
|
|
93
|
+
**With Tailwind CSS v4**, import `@meowdown/core/style.css` _after_ `@import 'tailwindcss'` so the `meowdown` layer sorts after Tailwind's `base` (Preflight). If you also need Tailwind utilities to win over meowdown (while meowdown still beats Preflight), declare the layer order yourself:
|
|
107
94
|
|
|
108
95
|
```css
|
|
109
96
|
@layer theme, base, components, meowdown, utilities;
|
|
@@ -111,36 +98,6 @@ meowdown's CSS is wrapped in a cascade layer, `@layer meowdown` (with sub-layers
|
|
|
111
98
|
@import '@meowdown/core/style.css';
|
|
112
99
|
```
|
|
113
100
|
|
|
114
|
-
Two things the variable list cannot show: `--meowdown-gutter` is the horizontal editor padding, applied to the editable root's `.meowdown-content` class, not `.ProseMirror`, so the block handle's drag preview stays unpadded; floating UI such as the block handle lives inside it, so keep it at least `3.5rem`. `defineEditorExtension` puts `.meowdown-content` on the editable root itself, so every mount (the headless quick start above included) is padded from the first paint. The selection variables (`--meowdown-selection`, `--meowdown-node-outline`, `--meowdown-node-selection`) are standalone, not derived from `--meowdown-accent`, so selection can be restyled independently.
|
|
115
|
-
|
|
116
|
-
Tags (`#tag`) render as pills via the `.md-tag` class, tinted from `--meowdown-accent`. Wire click handling with `defineTagClickHandler(({ tag, event }) => ...)` (or `@meowdown/react`'s `onTagClick` prop); `tag` is read from the rendered text without the leading `#`.
|
|
117
|
-
|
|
118
|
-
Wikilinks (`[[target]]`/`[[target|alias]]`) render in place via a mark view as an immutable label (the alias, or the target when there is no alias), with the raw source hidden in hide and focus modes and shown dimmed in show mode. The label uses the `.md-wikilink-view-label` class, dashed-underlined and colored by `--meowdown-accent`. In every mark mode the link is a single immutable caret stop: arrowing onto it selects the whole source (ringed with `--meowdown-node-outline` in hide and focus, the native selection over the visible source in show), and Backspace/Delete remove it as a unit, and `Enter` on the selected unit fires the click handler instead of replacing it. Wire click navigation with `defineWikilinkClickHandler(({ target, event }) => ...)` (or `@meowdown/react`'s `onWikilinkClick` prop); `Mod-Enter` with the caret on a wikilink, tag, or Markdown link fires the same handler, with the `KeyboardEvent` as `event`. `defineWikilinkHoverHandler` reports the hovered target, source range, and visible anchor element, then reports `undefined` on leave, deletion, replacement, or editor teardown.
|
|
119
|
-
|
|
120
|
-
Markdown links render the label as an `<a href>` with the `.md-link` class, colored by `--meowdown-accent`; the source syntax dims in show mode and hides in hide and focus modes. This includes inline links (`[text](url)`), full references (`[text][label]`), collapsed references (`[label][]`), and shortcut references (`[label]`). Definitions use CommonMark label normalization and first-definition-wins semantics. Definitions remain visible and editable as literal source in the editor, while unresolved references remain literal and preserve nested formatting. Reference destinations are read-only through the link commands and menu because editing one safely requires a definition-aware workflow. Citing links update after a 200 ms pause in definition editing, which coalesces large fan-out restyles; definition-shaped textblocks longer than 1,024 characters stay literal and are not indexed. `[[target]]` continues to use meowdown's wikilink syntax. Wire click handling with `defineLinkClickHandler(({ href, event }) => ...)` (or `@meowdown/react`'s `onLinkClick` prop).
|
|
121
|
-
|
|
122
|
-
Low-level renderers can import `collectReferenceDefinitions` and `inlineTextToMarkChunksWithContext` directly from `@meowdown/core` to share the editor's definition collection and reference resolution.
|
|
123
|
-
|
|
124
|
-
Bare URLs autolink without `[text](url)` brackets and share the same `.md-link` rendering and click handling: a scheme URL (`https://example.com`), an angle autolink (`<https://example.com>`), a `www.` host (`www.example.com`), an email (`me@example.com`), and a bare domain (`google.com`, `sub.domain.io/path`). Bare domains are matched against a curated list of common TLDs, so file names and prose keep their dots without linkifying (`README.md`, `node.js`, `i.e.` stay plain text); reach for `[text](url)` or `<url>` to link anything off that list. Autolinks are derived live from the text, so editing one re-evaluates it; the caret sitting inside a link never un-links it.
|
|
125
|
-
|
|
126
|
-
Inline and reference images (``, `![alt][label]`, `![label][]`, and `![label]`) stay literal text and render in place via a mark view, with their raw source hidden in hide and focus modes. Add an inline image with [`defineImage`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineImage) (or `@meowdown/react`'s image props) and wire click handling with [`defineImageClickHandler`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineImageClickHandler) (`@meowdown/react`'s `onImageClick` prop).
|
|
127
|
-
|
|
128
|
-
Pasted or dropped files persist through [`defineFilePaste`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineFilePaste) (or `@meowdown/react`'s `onFilePaste` prop): the handler persists each file and returns its markdown destination. An image (`image/*` MIME type) inserts ``; any other file inserts a `[name](src)` link; multiple files insert one link per line, in the order they appear in the drop. Without `onFilePaste`, file events are left to the browser's default handling. A host command that inserts file links itself (e.g. an attach-file picker) can build the same markdown with [`buildFileMarkdown`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-buildFileMarkdown).
|
|
129
|
-
|
|
130
|
-
A host can render chosen file links as inline **file pills**: pass `resolveFileLink` to [`defineEditorExtension`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineEditorExtension) (or `@meowdown/react`'s `resolveFileLink` prop) to claim inline or resolved reference links by their href (e.g. everything under `assets/`), and add [`defineFileView`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineFileView) to render each claimed link as a pill: a file-kind icon, the name, and the size supplied (possibly async) by `resolveFileInfo`. The markdown text is untouched, and a claimed link behaves like an image: one caret unit with an editable source, clicks (and `Enter` on the selected pill or `Mod-Enter` with the caret on it) reported through [`defineFileClickHandler`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineFileClickHandler) (`@meowdown/react`'s `onFileClick`) rather than the link click handler, and no link hover or edit menu.
|
|
131
|
-
|
|
132
|
-
Rendered images are resizable: drag the corner handle and the chosen size is written back into the source as a trailing comment, `<!-- {"width":320,"height":240} -->`, which round-trips as plain Markdown. A comment immediately after an image is folded into its mark and drives the image's `width` and `height` attributes, so the box keeps its dimensions before the image loads; any other comment stays literal text.
|
|
133
|
-
|
|
134
|
-
GFM table column alignment (`| :-: |` in the delimiter row) is kept as an `align` attribute on every cell and rendered through a `data-align` DOM attribute (`text-align` in the bundled stylesheet). Alignment is a column property: the header row's cells carry the source of truth, data cells (including rows inserted later) follow it automatically. Change it with the `setTableColumnAlign` command (`editor.commands.setTableColumnAlign('center')`, `null` resets to `---`), and read the alignment of the selection's column with [`getTableColumnAlign`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-getTableColumnAlign).
|
|
135
|
-
|
|
136
|
-
Pasting a lone tweet or YouTube link can auto-embed it: [`defineEmbedPaste`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineEmbedPaste) (`@meowdown/react`'s `embedPaste` prop, on by default).
|
|
137
|
-
|
|
138
|
-
The clipboard pipeline ships inside `defineEditorExtension`. Copying writes two flavors: `text/html` is standard semantic HTML (`<h3>`, `<ol><li>`, `<strong>`, real `<table>`) with the Markdown source preserved in `data-md` attributes so meowdown-to-meowdown pastes stay byte-exact, and `text/plain` is Markdown, where opening markers such as ATX heading and blockquote prefixes are kept when the selection includes the block's content start, while fenced code blocks and tables keep their markers only when selected completely; the mark mode decides the inline layer (hide strips the syntax characters, focus and show keep the full source). Pasting picks a path by flavor: meowdown's own HTML (stamped `data-meowdown`) parses natively; foreign rich-text HTML, including other ProseMirror editors, converts to Markdown through the unified (rehype / remark) pipeline; plain text follows Markdown newline semantics (a blank line separates paragraphs without inserting an empty one, a single newline stays a soft break) while a Shift-paste keeps ProseMirror's line-per-paragraph behavior; a paste landing in a code block stays plain text.
|
|
139
|
-
|
|
140
|
-
Enter at the end of the document's first heading (the title line) can start a fresh empty bullet instead of a plain paragraph: [`defineBulletAfterHeading`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineBulletAfterHeading) (`@meowdown/react`'s `bulletAfterHeading` prop). Not part of `defineEditorExtension`.
|
|
141
|
-
|
|
142
|
-
An arrow press that can move the caret no further can notify the host, so it can move focus elsewhere (a previous/next note or page): [`defineExitBoundaryHandler`](https://npmx.dev/package-docs/@meowdown%2Fcore#function-defineExitBoundaryHandler) (`@meowdown/react`'s `onExitBoundary` prop). Not part of `defineEditorExtension`.
|
|
143
|
-
|
|
144
101
|
## API
|
|
145
102
|
|
|
146
103
|
See the full API reference [here](https://npmx.dev/package-docs/@meowdown%2Fcore/).
|
package/dist/index.d.ts
CHANGED
|
@@ -448,6 +448,10 @@ interface FileLinkPayload {
|
|
|
448
448
|
type FileLinkResolver = (link: FileLinkPayload) => boolean;
|
|
449
449
|
/** Host options that influence inline parsing. */
|
|
450
450
|
interface FileLinkOptions {
|
|
451
|
+
/**
|
|
452
|
+
* Claim `[label](url)` links as file attachments; see {@link FileLinkResolver}.
|
|
453
|
+
* Read once when the editor is created.
|
|
454
|
+
*/
|
|
451
455
|
resolveFileLink?: FileLinkResolver;
|
|
452
456
|
}
|
|
453
457
|
/** Host options that influence source-backed inline atom parsing. */
|
|
@@ -929,6 +933,11 @@ interface LinkCopyPayload {
|
|
|
929
933
|
href: string;
|
|
930
934
|
}
|
|
931
935
|
type LinkCopyHandler = (payload: LinkCopyPayload) => void;
|
|
936
|
+
/**
|
|
937
|
+
* Call `onClick` when the user clicks a rendered Markdown link
|
|
938
|
+
* (`[text](url)`), or presses `Mod-Enter` with the caret on one. The `event`
|
|
939
|
+
* is the originating `MouseEvent` or `KeyboardEvent`.
|
|
940
|
+
*/
|
|
932
941
|
declare function defineLinkClickHandler(onClick: LinkClickHandler): PlainExtension;
|
|
933
942
|
//#endregion
|
|
934
943
|
//#region src/extensions/tag-click.d.ts
|
|
@@ -942,6 +951,11 @@ interface TagClickPayload {
|
|
|
942
951
|
event: MouseEvent | KeyboardEvent;
|
|
943
952
|
}
|
|
944
953
|
type TagClickHandler = (payload: TagClickPayload) => void;
|
|
954
|
+
/**
|
|
955
|
+
* Call `onClick` when the user clicks a rendered `#tag`, or presses
|
|
956
|
+
* `Mod-Enter` with the caret on one. The `tag` is read from the rendered text
|
|
957
|
+
* without the leading `#`.
|
|
958
|
+
*/
|
|
945
959
|
declare function defineTagClickHandler(onClick: TagClickHandler): PlainExtension;
|
|
946
960
|
//#endregion
|
|
947
961
|
//#region src/extensions/wikilink-click.d.ts
|
|
@@ -956,6 +970,11 @@ interface WikilinkClickPayload {
|
|
|
956
970
|
event: MouseEvent | KeyboardEvent;
|
|
957
971
|
}
|
|
958
972
|
type WikilinkClickHandler = (payload: WikilinkClickPayload) => void;
|
|
973
|
+
/**
|
|
974
|
+
* Call `onClick` when the user clicks a rendered wikilink label, or presses
|
|
975
|
+
* `Mod-Enter` with the caret on one. The `event` is the originating
|
|
976
|
+
* `MouseEvent` or `KeyboardEvent`.
|
|
977
|
+
*/
|
|
959
978
|
declare function defineWikilinkClickHandler(onClick: WikilinkClickHandler): PlainExtension;
|
|
960
979
|
//#endregion
|
|
961
980
|
//#region src/extensions/follow-link.d.ts
|
|
@@ -1021,7 +1040,13 @@ interface ImageOptions {
|
|
|
1021
1040
|
}
|
|
1022
1041
|
/** Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it. */
|
|
1023
1042
|
declare function defaultResolveImageUrl(src: string): string | undefined;
|
|
1024
|
-
/**
|
|
1043
|
+
/**
|
|
1044
|
+
* Inline image/embed rendering: a mark view on the `mdImage` mark. Images
|
|
1045
|
+
* render in place from their literal Markdown source. Drag a rendered image's
|
|
1046
|
+
* corner handle to write the size back into the source as a trailing comment,
|
|
1047
|
+
* `<!-- {"width":320,"height":240} -->`, which round-trips as
|
|
1048
|
+
* plain Markdown.
|
|
1049
|
+
*/
|
|
1025
1050
|
declare function defineImage(options?: ImageOptions): PlainExtension;
|
|
1026
1051
|
//#endregion
|
|
1027
1052
|
//#region src/extensions/key-bindings.d.ts
|
|
@@ -1131,6 +1156,12 @@ declare function defineViewAttributes(attributes: {
|
|
|
1131
1156
|
* so IME, clicks, and typing keep their native behavior; only the caret pixels
|
|
1132
1157
|
* are ours. Applies to every mark mode.
|
|
1133
1158
|
*
|
|
1159
|
+
* On a touch screen, while the last input was a finger or pen
|
|
1160
|
+
* ({@link getIsTouchInput}), the roles flip: the native caret stays visible
|
|
1161
|
+
* (it carries the system touch affordances: the drag magnifier, the caret-drag
|
|
1162
|
+
* long-press mode) and the virtual caret draws only at positions where the
|
|
1163
|
+
* native caret has no geometry, such as beside hidden Markdown syntax.
|
|
1164
|
+
*
|
|
1134
1165
|
* `layer` is the element the caret draws into. The host owns its placement:
|
|
1135
1166
|
* it must live outside the contenteditable and scroll together with the
|
|
1136
1167
|
* content.
|