@meowdown/react 0.1.0 → 0.3.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 ADDED
@@ -0,0 +1,68 @@
1
+ # @meowdown/react
2
+
3
+ React components for Meowdown, a hybrid (live-preview) Markdown editor.
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ import { Editor, type EditorHandle } from '@meowdown/react'
9
+ import '@meowdown/react/style.css'
10
+ import { useRef, useCallback } from 'react'
11
+
12
+ export function App() {
13
+ const ref = useRef<EditorHandle>(null)
14
+ const handleDocChange = useCallback(() => {
15
+ console.log(ref.current?.getMarkdown())
16
+ }, [])
17
+
18
+ return <Editor ref={ref} mode="focus" initialMarkdown="# Hello" onDocChange={handleDocChange} />
19
+ }
20
+ ```
21
+
22
+ ## API
23
+
24
+ ### `<Editor>`
25
+
26
+ The Markdown editor component. Renders inside a `div.meowdown` wrapper that fills a flex parent. In rich modes, typing `/` opens a slash menu for inserting blocks (headings, blockquote, lists, code block, table). Hovering a block shows a handle to its left: the plus button inserts an empty paragraph below the block, and the grip selects the block and can be dragged to move it, with a drop indicator line marking the target.
27
+
28
+ - `mode?: 'focus' | 'show' | 'hide' | 'source'`: defaults to `'focus'`.
29
+ - `'focus'`: Markdown syntax is hidden, revealed around the cursor.
30
+ - `'show'`: Markdown syntax is always visible.
31
+ - `'hide'`: Markdown syntax is always hidden.
32
+ - `'source'`: raw Markdown source with syntax highlighting.
33
+ - `initialMarkdown?: string`: first render only.
34
+ - `onDocChange?: VoidFunction`: called on every document change.
35
+ - `onTagSearch?: (query: string) => string[] | Promise<string[]>`: enables the tag menu, which opens when typing `#` followed by text in a rich mode; returns the tags to show for a query (lowercased, punctuation stripped). Omit to disable.
36
+ - `onWikilinkSearch?: (query: string) => string[] | Promise<string[]>`: enables the wikilink menu, which opens as soon as `[[` is typed in a rich mode; returns the note names to show for a query (lowercased, punctuation stripped, may be empty). Selecting a note inserts `[[Note Name]]`. Omit to disable.
37
+ - `ref?: Ref<EditorHandle>`
38
+
39
+ ### `EditorHandle`
40
+
41
+ Imperative handle for the editor, attached via `ref`.
42
+
43
+ - `getMarkdown(): string`: serializes the current document to Markdown. Can be expensive on large documents; call it on demand (e.g. throttled) instead of on every change.
44
+
45
+ ## Keyboard shortcuts
46
+
47
+ In the rich modes (`focus` / `show` / `hide`), these toggle inline formatting on the selection (`Mod` = Cmd on macOS, Ctrl elsewhere):
48
+
49
+ | Key | Action |
50
+ | ------------- | -------------------- |
51
+ | `Mod-B` | toggle bold |
52
+ | `Mod-I` | toggle italic |
53
+ | `Mod-E` | toggle inline code |
54
+ | `Mod-Shift-X` | toggle strikethrough |
55
+
56
+ ## Styling
57
+
58
+ `@meowdown/react/style.css` includes the default theme from [`@meowdown/core`](https://www.npmjs.com/package/@meowdown/core). Colors follow the page's `color-scheme`; customize via the `--meowdown-*` CSS variables documented there. The editor reserves a horizontal gutter (`--meowdown-gutter`) so the block handle has room to the left of the hovered block.
59
+
60
+ Selection colors are standalone variables (independent from `--meowdown-accent`):
61
+
62
+ - `--meowdown-node-outline`: outline of a selected node (e.g. a block grabbed by its handle).
63
+ - `--meowdown-node-selection`: background wash of a selected node.
64
+ - `--meowdown-selection`: text `::selection` background.
65
+
66
+ ## License
67
+
68
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,7 +1,71 @@
1
- //#region src/comp.d.ts
2
- declare function MyReactComponent(): import("react").JSX.Element;
1
+ import { Ref } from "react";
2
+ import { MarkMode, MarkMode as MarkMode$1, TypedEditor } from "@meowdown/core";
3
+
4
+ //#region src/components/types.d.ts
5
+ interface EditorHandle {
6
+ /**
7
+ * Serializes the current document to Markdown. Can be expensive on large
8
+ * documents; call it on demand (e.g. throttled) instead of on every change.
9
+ */
10
+ getMarkdown: () => string;
11
+ }
12
+ /**
13
+ * Searches tags for the tag menu. Receives the query typed after `#`
14
+ * (lowercased, punctuation stripped) and returns the tags to show, either
15
+ * synchronously or as a promise.
16
+ */
17
+ type TagSearchHandler = (query: string) => string[] | Promise<string[]>;
18
+ /**
19
+ * Searches notes for the wikilink menu. Receives the query typed after
20
+ * `[[` (lowercased, punctuation stripped, may be empty or contain spaces)
21
+ * and returns the note names to show, either synchronously or as a promise.
22
+ */
23
+ type WikilinkSearchHandler = (query: string) => string[] | Promise<string[]>;
3
24
  //#endregion
4
- //#region src/index.d.ts
5
- declare const b: any;
25
+ //#region src/components/editor.d.ts
26
+ type EditorMode = MarkMode$1 | 'source';
27
+ interface EditorProps {
28
+ /**
29
+ * The editor mode. The three rich modes ('focus', 'show', 'hide') render a ProseKit
30
+ * editor; 'source' renders a CodeMirror editor showing the raw Markdown text.
31
+ * Content carries over when switching between the two editor families, but undo
32
+ * history and selection do not. Defaults to 'focus'.
33
+ */
34
+ mode?: EditorMode;
35
+ /**
36
+ * The initial Markdown text of the editor. Only the value provided on the
37
+ * first render is used; later changes are ignored.
38
+ */
39
+ initialMarkdown?: string;
40
+ /** Called on every document change. */
41
+ onDocChange?: VoidFunction;
42
+ /**
43
+ * Searches tags for the tag menu, which opens when typing `#` followed by
44
+ * text in a rich mode. Receives the query (lowercased, punctuation
45
+ * stripped) and returns the tags to show, synchronously or as a promise.
46
+ * Pass a stable function (e.g. from `useCallback`). Omit to disable the
47
+ * tag menu. Ignored in source mode.
48
+ */
49
+ onTagSearch?: TagSearchHandler;
50
+ /**
51
+ * Searches notes for the wikilink menu, which opens as soon as `[[` is
52
+ * typed in a rich mode. Receives the query (lowercased, punctuation
53
+ * stripped, may be empty) and returns the note names to show,
54
+ * synchronously or as a promise. Pass a stable function (e.g. from
55
+ * `useCallback`). Omit to disable the wikilink menu. Ignored in source
56
+ * mode.
57
+ */
58
+ onWikilinkSearch?: WikilinkSearchHandler;
59
+ /** Imperative handle for the editor. */
60
+ ref?: Ref<EditorHandle>;
61
+ }
62
+ declare function Editor({
63
+ mode,
64
+ initialMarkdown,
65
+ onDocChange,
66
+ onTagSearch,
67
+ onWikilinkSearch,
68
+ ref
69
+ }: EditorProps): import("react").JSX.Element;
6
70
  //#endregion
7
- export { MyReactComponent, b };
71
+ export { Editor, type EditorHandle, type EditorMode, type EditorProps, type MarkMode, type TagSearchHandler, type TypedEditor, type WikilinkSearchHandler };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as e}from"@meowdown/core";import{useState as t}from"react";import{jsx as n,jsxs as r}from"react/jsx-runtime";function i(){let[e,i]=t(0);return r(`div`,{children:[n(`h1`,{children:`Hello, React!`}),r(`p`,{children:[`Count: `,e]}),n(`p`,{onClick:()=>i(e+1),children:`This is a simple React component.`})]})}const a=2+e;export{i as MyReactComponent,a as b};
1
+ import{useCallback as e,useEffect as t,useImperativeHandle as n,useLayoutEffect as r,useMemo as i,useRef as a,useState as o}from"react";import{defaultKeymap as s,history as c,historyKeymap as l}from"@codemirror/commands";import{markdown as u,markdownLanguage as d}from"@codemirror/lang-markdown";import{defaultHighlightStyle as f,syntaxHighlighting as p}from"@codemirror/language";import{EditorState as m}from"@codemirror/state";import{EditorView as h,keymap as g}from"@codemirror/view";import{jsx as _,jsxs as v}from"react/jsx-runtime";import{defineEditorExtension as y,defineMarkMode as b,docToMarkdown as x,markdownToDoc as S}from"@meowdown/core";import{canUseRegexLookbehind as C,createEditor as w,defineDocChangeHandler as T}from"@prosekit/core";import{ProseKit as E,useEditor as D,useExtension as O}from"@prosekit/react";import{BlockHandleAdd as k,BlockHandleDraggable as A,BlockHandlePopup as j,BlockHandlePositioner as M,BlockHandleRoot as N}from"@prosekit/react/block-handle";import{DropIndicator as P}from"@prosekit/react/drop-indicator";import{AutocompleteEmpty as F,AutocompleteItem as I,AutocompletePopup as L,AutocompletePositioner as R,AutocompleteRoot as z}from"@prosekit/react/autocomplete";function B({initialMarkdown:e,onDocChange:t,ref:i}){let o=a(null),v=a(null),y=a(t);r(()=>{y.current=t},[t]);let b=a(e??``);return n(i,()=>({getMarkdown:()=>v.current?.state.doc.toString()??b.current}),[]),r(()=>{let e=o.current;if(!e)return;let t=new h({parent:e,state:m.create({doc:b.current,extensions:[c(),g.of([...s,...l]),u({base:d}),p(f,{fallback:!0}),h.lineWrapping,h.updateListener.of(e=>{e.docChanged&&y.current?.()})]})});return v.current=t,()=>{t.destroy(),v.current=null}},[]),_(`div`,{ref:o,"data-editor":`codemirror`})}function V(){return v(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:[_(`circle`,{cx:`9`,cy:`5`,r:`1`}),_(`circle`,{cx:`9`,cy:`12`,r:`1`}),_(`circle`,{cx:`9`,cy:`19`,r:`1`}),_(`circle`,{cx:`15`,cy:`5`,r:`1`}),_(`circle`,{cx:`15`,cy:`12`,r:`1`}),_(`circle`,{cx:`15`,cy:`19`,r:`1`})]})}function H(){return v(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:[_(`path`,{d:`M5 12h14`}),_(`path`,{d:`M12 5v14`})]})}function U(){return _(N,{children:_(M,{className:`meowdown-block-handle-positioner`,children:v(j,{className:`meowdown-block-handle`,"data-testid":`block-handle`,children:[_(k,{className:`meowdown-block-handle-add`,"data-testid":`block-handle-add`,children:_(H,{})}),_(A,{className:`meowdown-block-handle-drag`,"data-testid":`block-handle-drag`,children:_(V,{})})]})})})}function W(){return _(P,{className:`meowdown-drop-indicator`,"data-testid":`drop-indicator`})}const G=C()?/(?<!\S)\/(\S.*)?$/u:/\/(\S.*)?$/u;function K({label:e,kbd:t,onSelect:n}){return v(I,{className:`meowdown-autocomplete-menu-item`,onSelect:n,children:[_(`span`,{children:e}),t&&_(`kbd`,{children:t})]})}function q(){let e=D();return _(z,{regex:G,children:_(R,{className:`meowdown-autocomplete-menu-positioner`,children:v(L,{className:`meowdown-autocomplete-menu`,"data-testid":`slash-menu`,children:[_(K,{label:`Heading 1`,kbd:`#`,onSelect:()=>e.commands.setHeading({level:1})}),_(K,{label:`Heading 2`,kbd:`##`,onSelect:()=>e.commands.setHeading({level:2})}),_(K,{label:`Heading 3`,kbd:`###`,onSelect:()=>e.commands.setHeading({level:3})}),_(K,{label:`Heading 4`,kbd:`####`,onSelect:()=>e.commands.setHeading({level:4})}),_(K,{label:`Blockquote`,kbd:`>`,onSelect:()=>e.commands.setBlockquote()}),_(K,{label:`Bullet list`,kbd:`-`,onSelect:()=>e.commands.wrapInList({kind:`bullet`})}),_(K,{label:`Ordered list`,kbd:`1.`,onSelect:()=>e.commands.wrapInList({kind:`ordered`})}),_(K,{label:`Task list`,kbd:`[]`,onSelect:()=>e.commands.wrapInList({kind:`task`})}),_(K,{label:`Code block`,kbd:"```",onSelect:()=>e.commands.setCodeBlock()}),_(K,{label:`Table`,onSelect:()=>e.commands.insertTable({row:3,col:3})}),_(F,{className:`meowdown-autocomplete-menu-item`,children:`No results`})]})})})}function J(){return!0}const Y=C()?/(?<!\S)#[\da-z]+$/iu:/#[\da-z]+$/iu;function X({onTagSearch:n}){let r=D(),[i,a]=o(!1),[s,c]=o(``),[l,u]=o([]),[d,f]=o(!1),p=e(async(e,t)=>{if(t.aborted)return;f(!0);let r=await n(e);t.aborted||(u(r),f(!1))},[n]);return t(()=>{if(!i)return;let e=new AbortController;return queueMicrotask(()=>{p(s,e.signal)}),()=>{e.abort()}},[i,s,p]),_(z,{regex:Y,filter:J,onOpenChange:e=>a(e.detail),onQueryChange:e=>c(e.detail),children:_(R,{className:`meowdown-autocomplete-menu-positioner`,children:v(L,{className:`meowdown-autocomplete-menu`,"data-testid":`tag-menu`,children:[l.map(e=>v(I,{className:`meowdown-autocomplete-menu-item`,onSelect:()=>r.commands.insertText({text:`#${e} `}),children:[`#`,e]},e)),_(F,{className:`meowdown-autocomplete-menu-item`,children:d?`Loading...`:`No tags found`})]})})})}const Z=/\[\[[^[\]]*$/u;function Q({onWikilinkSearch:n}){let r=D(),[i,a]=o(!1),[s,c]=o(``),[l,u]=o([]),[d,f]=o(!1),p=e(async(e,t)=>{if(t.aborted)return;f(!0);let r=await n(e);t.aborted||(u(r),f(!1))},[n]);return t(()=>{if(!i)return;let e=new AbortController;return queueMicrotask(()=>{p(s,e.signal)}),()=>{e.abort()}},[i,s,p]),_(z,{regex:Z,filter:J,onOpenChange:e=>a(e.detail),onQueryChange:e=>c(e.detail),children:_(R,{className:`meowdown-autocomplete-menu-positioner`,children:v(L,{className:`meowdown-autocomplete-menu`,"data-testid":`wikilink-menu`,children:[l.map(e=>_(I,{className:`meowdown-autocomplete-menu-item`,onSelect:()=>r.commands.insertText({text:`[[${e}]]`}),children:e},e)),_(F,{className:`meowdown-autocomplete-menu-item`,children:d?`Loading...`:`No notes found`})]})})})}function $({markMode:e=`focus`,initialMarkdown:t,onDocChange:r,onTagSearch:a,onWikilinkSearch:s,ref:c}){let[l]=o(()=>{let e=w({extension:y()});return t&&e.setContent(S(e,t)),e});return n(c,()=>({getMarkdown:()=>x(l.state.doc)}),[l]),O(i(()=>b(e),[e]),{editor:l}),O(i(()=>r?T(()=>{r()}):null,[r]),{editor:l}),v(E,{editor:l,children:[_(`div`,{ref:l.mount}),_(U,{}),_(W,{}),_(q,{}),a&&_(X,{onTagSearch:a}),s&&_(Q,{onWikilinkSearch:s})]})}function ee({mode:e=`focus`,initialMarkdown:t,onDocChange:r,onTagSearch:i,onWikilinkSearch:o,ref:s}){let c=a(null);n(s,()=>({getMarkdown:()=>c.current?.getMarkdown()??``}),[]);let l=c.current?.getMarkdown()??t??``;return _(`div`,{className:`meowdown`,children:e===`source`?_(B,{ref:c,initialMarkdown:l,onDocChange:r}):_($,{ref:c,markMode:e,initialMarkdown:l,onDocChange:r,onTagSearch:i,onWikilinkSearch:o})})}export{ee as Editor};
package/dist/style.css ADDED
@@ -0,0 +1,528 @@
1
+ .ProseMirror {
2
+ word-wrap: break-word;
3
+ white-space: pre-wrap;
4
+ white-space: break-spaces;
5
+ -webkit-font-variant-ligatures: none;
6
+ font-variant-ligatures: none;
7
+ font-feature-settings: "liga" 0;
8
+ position: relative;
9
+ }
10
+
11
+ .ProseMirror pre {
12
+ white-space: pre-wrap;
13
+ }
14
+
15
+ .ProseMirror li {
16
+ position: relative;
17
+ }
18
+
19
+ .ProseMirror-hideselection ::selection {
20
+ background: none;
21
+ }
22
+
23
+ .ProseMirror-hideselection {
24
+ caret-color: #0000;
25
+ }
26
+
27
+ .ProseMirror [draggable][contenteditable="false"] {
28
+ user-select: text;
29
+ }
30
+
31
+ .ProseMirror-selectednode {
32
+ outline: 2px solid #8cf;
33
+ }
34
+
35
+ li.ProseMirror-selectednode {
36
+ outline: none;
37
+ }
38
+
39
+ li.ProseMirror-selectednode:after {
40
+ content: "";
41
+ pointer-events: none;
42
+ border: 2px solid #8cf;
43
+ position: absolute;
44
+ inset: -2px -2px -2px -32px;
45
+ }
46
+
47
+ img.ProseMirror-separator {
48
+ border: none !important;
49
+ margin: 0 !important;
50
+ display: inline !important;
51
+ }
52
+
53
+ :root {
54
+ --prosekit-list-bullet-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='2.5' fill='currentColor'/%3E%3C/svg%3E");
55
+ --prosekit-list-toggle-open-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpolygon points='8,10 12,14 16,10' fill='currentColor'/%3E%3C/svg%3E");
56
+ --prosekit-list-toggle-closed-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpolygon points='10,8 14,12 10,16' fill='currentColor'/%3E%3C/svg%3E");
57
+ }
58
+
59
+ .prosemirror-flat-list {
60
+ margin: 0;
61
+ padding: 0;
62
+ list-style: none;
63
+ position: relative;
64
+ }
65
+
66
+ .prosemirror-flat-list > .list-marker {
67
+ text-align: center;
68
+ width: 1lh;
69
+ height: 1lh;
70
+ position: absolute;
71
+ inset-inline-start: 0;
72
+ }
73
+
74
+ .prosemirror-flat-list > .list-content {
75
+ margin-inline-start: 1lh;
76
+ }
77
+
78
+ .prosemirror-flat-list[data-list-kind="bullet"] > .list-marker, .prosemirror-flat-list[data-list-kind="toggle"] > .list-marker {
79
+ background-color: currentColor;
80
+ mask-position: center;
81
+ mask-size: contain;
82
+ mask-repeat: no-repeat;
83
+ }
84
+
85
+ .prosemirror-flat-list[data-list-kind="bullet"] > .list-marker {
86
+ mask-image: var(--prosekit-list-bullet-icon);
87
+ }
88
+
89
+ .prosemirror-flat-list[data-list-kind="toggle"] > .list-marker {
90
+ mask-image: var(--prosekit-list-toggle-open-icon);
91
+ }
92
+
93
+ .prosemirror-flat-list[data-list-kind="toggle"][data-list-collapsable][data-list-collapsed] > .list-marker {
94
+ mask-image: var(--prosekit-list-toggle-closed-icon);
95
+ }
96
+
97
+ .prosemirror-flat-list[data-list-kind="ordered"] > * {
98
+ contain: style;
99
+ }
100
+
101
+ .prosemirror-flat-list[data-list-kind="ordered"]:before {
102
+ content: counter(prosemirror-flat-list-counter, decimal) ". ";
103
+ font-variant-numeric: tabular-nums;
104
+ position: absolute;
105
+ inset-inline-end: calc(100% - 1lh);
106
+ }
107
+
108
+ .prosemirror-flat-list[data-list-kind="ordered"] {
109
+ counter-increment: prosemirror-flat-list-counter;
110
+ }
111
+
112
+ .prosemirror-flat-list[data-list-kind="ordered"]:first-child, :not(.prosemirror-flat-list[data-list-kind="ordered"]) + .prosemirror-flat-list[data-list-kind="ordered"] {
113
+ counter-reset: prosemirror-flat-list-counter;
114
+ }
115
+
116
+ @supports (counter-set: prosemirror-flat-list-counter 1) {
117
+ :is(.prosemirror-flat-list[data-list-kind="ordered"]:first-child, :not(.prosemirror-flat-list[data-list-kind="ordered"]) + .prosemirror-flat-list[data-list-kind="ordered"])[data-list-order] {
118
+ counter-set: prosemirror-flat-list-counter var(--prosemirror-flat-list-order);
119
+ }
120
+ }
121
+
122
+ @supports not (counter-set: prosemirror-flat-list-counter 1) {
123
+ :is(.prosemirror-flat-list[data-list-kind="ordered"]:first-child, :not(.prosemirror-flat-list[data-list-kind="ordered"]) + .prosemirror-flat-list[data-list-kind="ordered"])[data-list-order] {
124
+ counter-increment: prosemirror-flat-list-counter var(--prosemirror-flat-list-order);
125
+ }
126
+ }
127
+
128
+ .prosemirror-flat-list[data-list-kind="task"] > .list-marker, .prosemirror-flat-list[data-list-kind="task"] > .list-marker * {
129
+ cursor: pointer;
130
+ justify-content: center;
131
+ align-items: center;
132
+ margin: 0;
133
+ padding: 0;
134
+ display: flex;
135
+ }
136
+
137
+ .prosemirror-flat-list[data-list-kind="toggle"][data-list-collapsable] > .list-marker {
138
+ cursor: pointer;
139
+ }
140
+
141
+ .prosemirror-flat-list[data-list-kind="toggle"]:not([data-list-collapsable]) > .list-marker {
142
+ opacity: .4;
143
+ pointer-events: none;
144
+ }
145
+
146
+ .prosemirror-flat-list[data-list-kind="toggle"][data-list-collapsable][data-list-collapsed] > .list-content > :nth-child(n+2) {
147
+ display: none;
148
+ }
149
+
150
+ .ProseMirror .tableWrapper {
151
+ overflow-x: auto;
152
+ }
153
+
154
+ .ProseMirror table {
155
+ border-collapse: collapse;
156
+ table-layout: fixed;
157
+ width: 100%;
158
+ overflow: hidden;
159
+ }
160
+
161
+ .ProseMirror td, .ProseMirror th {
162
+ box-sizing: border-box;
163
+ vertical-align: top;
164
+ border-width: 1px;
165
+ padding-left: .75rem;
166
+ padding-right: .75rem;
167
+ position: relative;
168
+ }
169
+
170
+ prosekit-table-handle-drop-indicator {
171
+ background-color: highlighttext;
172
+ }
173
+
174
+ .ProseMirror .column-resize-handle {
175
+ z-index: 20;
176
+ pointer-events: none;
177
+ background-color: highlighttext;
178
+ width: 4px;
179
+ position: absolute;
180
+ top: 0;
181
+ bottom: 0;
182
+ right: -2px;
183
+ }
184
+
185
+ .ProseMirror.resize-cursor {
186
+ cursor: ew-resize;
187
+ cursor: col-resize;
188
+ }
189
+
190
+ .ProseMirror .selectedCell {
191
+ --color: 210, 100%, 56%;
192
+ border: 1px double hsl(var(--color));
193
+ background-color: hsla(var(--color), 20%);
194
+ }
195
+
196
+ :root {
197
+ --meowdown-text: light-dark(#3f3f46, #d4d4d8);
198
+ --meowdown-heading: light-dark(#18181b, #fafafa);
199
+ --meowdown-muted: light-dark(#52525b, #a1a1aa);
200
+ --meowdown-accent: light-dark(#7c3aed, #c084fc);
201
+ --meowdown-mark: light-dark(#a78bfa, #8b5cf6);
202
+ --meowdown-border: light-dark(#e4e4e7, #3f3f46);
203
+ --meowdown-code-bg: light-dark(#f4f4f5, #18181b);
204
+ --meowdown-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
205
+ --meowdown-gutter: 3.5rem;
206
+ }
207
+
208
+ .ProseMirror {
209
+ box-sizing: border-box;
210
+ padding: 1.25rem var(--meowdown-gutter) 1.75rem;
211
+ color: var(--meowdown-text);
212
+ caret-color: var(--meowdown-accent);
213
+ -webkit-font-smoothing: antialiased;
214
+ outline: none;
215
+ font-size: 1.0625rem;
216
+ line-height: 1.7;
217
+ }
218
+
219
+ .ProseMirror > * + * {
220
+ margin-top: .85em;
221
+ }
222
+
223
+ .ProseMirror h1, .ProseMirror h2, .ProseMirror h3, .ProseMirror h4, .ProseMirror h5, .ProseMirror h6 {
224
+ letter-spacing: -.01em;
225
+ color: var(--meowdown-heading);
226
+ font-weight: 600;
227
+ line-height: 1.25;
228
+ }
229
+
230
+ .ProseMirror h1 {
231
+ font-size: 1.75rem;
232
+ }
233
+
234
+ .ProseMirror h2 {
235
+ font-size: 1.4rem;
236
+ }
237
+
238
+ .ProseMirror h3 {
239
+ font-size: 1.2rem;
240
+ }
241
+
242
+ .ProseMirror h4 {
243
+ font-size: 1.1rem;
244
+ }
245
+
246
+ .ProseMirror h5 {
247
+ font-size: 1rem;
248
+ }
249
+
250
+ .ProseMirror h6 {
251
+ font-size: .95rem;
252
+ }
253
+
254
+ .ProseMirror a {
255
+ color: var(--meowdown-accent);
256
+ text-underline-offset: 2px;
257
+ text-decoration: underline 1px;
258
+ }
259
+
260
+ .ProseMirror code {
261
+ font-family: var(--meowdown-font-mono);
262
+ background: color-mix(in oklab, var(--meowdown-accent) 10%, transparent);
263
+ color: color-mix(in oklab, var(--meowdown-accent) 80%, var(--meowdown-heading));
264
+ border-radius: .35rem;
265
+ padding: .1em .35em;
266
+ font-size: .9em;
267
+ }
268
+
269
+ .ProseMirror blockquote {
270
+ border-left: 3px solid color-mix(in oklab, var(--meowdown-accent) 35%, transparent);
271
+ color: var(--meowdown-muted);
272
+ margin-left: 0;
273
+ padding-left: 1rem;
274
+ font-style: italic;
275
+ }
276
+
277
+ .ProseMirror ul, .ProseMirror ol {
278
+ padding-left: 1.5rem;
279
+ }
280
+
281
+ .ProseMirror ul {
282
+ list-style: outside;
283
+ }
284
+
285
+ .ProseMirror ol {
286
+ list-style: decimal;
287
+ }
288
+
289
+ .ProseMirror li {
290
+ margin-top: .25em;
291
+ }
292
+
293
+ .ProseMirror pre {
294
+ background: var(--meowdown-code-bg);
295
+ font-family: var(--meowdown-font-mono);
296
+ border-radius: .6rem;
297
+ padding: .9rem 1rem;
298
+ font-size: .9em;
299
+ line-height: 1.5;
300
+ overflow-x: auto;
301
+ }
302
+
303
+ .ProseMirror pre code {
304
+ color: inherit;
305
+ background: none;
306
+ padding: 0;
307
+ }
308
+
309
+ .ProseMirror hr {
310
+ border: none;
311
+ border-top: 1px solid var(--meowdown-border);
312
+ margin: 1.5em 0;
313
+ }
314
+
315
+ .ProseMirror .md-tag {
316
+ background: color-mix(in oklab, var(--meowdown-accent) 10%, transparent);
317
+ color: color-mix(in oklab, var(--meowdown-accent) 80%, var(--meowdown-heading));
318
+ border-radius: .35rem;
319
+ padding: .1em .35em;
320
+ }
321
+
322
+ .ProseMirror .md-wikilink {
323
+ color: var(--meowdown-accent);
324
+ text-underline-offset: 2px;
325
+ text-decoration: underline 1px dashed;
326
+ }
327
+
328
+ .ProseMirror .md-mark, .ProseMirror .md-link-uri {
329
+ color: var(--meowdown-mark);
330
+ opacity: .7;
331
+ }
332
+
333
+ .ProseMirror[data-mark-mode="hide"] .md-mark, .ProseMirror[data-mark-mode="hide"] .md-link-uri, .ProseMirror[data-mark-mode="focus"] .md-mark, .ProseMirror[data-mark-mode="focus"] .md-link-uri {
334
+ display: none;
335
+ }
336
+
337
+ .ProseMirror[data-mark-mode="focus"] .md-mark:has(.show), .ProseMirror[data-mark-mode="focus"] .md-link-uri:has(.show) {
338
+ display: inline;
339
+ }
340
+
341
+ .meowdown-autocomplete-menu-positioner {
342
+ z-index: 50;
343
+ width: min-content;
344
+ height: min-content;
345
+ display: block;
346
+ overflow: visible;
347
+ }
348
+
349
+ .meowdown-autocomplete-menu {
350
+ box-sizing: border-box;
351
+ overscroll-behavior: contain;
352
+ white-space: nowrap;
353
+ user-select: none;
354
+ border: 1px solid var(--meowdown-border);
355
+ background: light-dark(#fff, #18181b);
356
+ border-radius: .75rem;
357
+ flex-direction: column;
358
+ min-width: 15rem;
359
+ max-height: 25rem;
360
+ padding: .25rem;
361
+ display: flex;
362
+ overflow-y: auto;
363
+ box-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
364
+ }
365
+
366
+ .meowdown-autocomplete-menu-item {
367
+ box-sizing: border-box;
368
+ color: var(--meowdown-text);
369
+ white-space: nowrap;
370
+ cursor: default;
371
+ user-select: none;
372
+ border-radius: .5rem;
373
+ justify-content: space-between;
374
+ align-items: center;
375
+ gap: 1.5rem;
376
+ padding: .375rem .75rem;
377
+ scroll-margin: .25rem;
378
+ font-size: .875rem;
379
+ display: flex;
380
+
381
+ &[hidden] {
382
+ display: none;
383
+ }
384
+
385
+ &[data-highlighted] {
386
+ background: light-dark(#f4f4f5, #27272a);
387
+ }
388
+
389
+ & kbd {
390
+ font-family: var(--meowdown-font-mono);
391
+ color: var(--meowdown-muted);
392
+ opacity: .8;
393
+ font-size: .75rem;
394
+ }
395
+ }
396
+
397
+ .meowdown-block-handle-positioner {
398
+ z-index: 50;
399
+ width: min-content;
400
+ height: min-content;
401
+ transition: transform .1s ease-out;
402
+ display: block;
403
+ overflow: visible;
404
+ }
405
+
406
+ .meowdown-block-handle {
407
+ box-sizing: border-box;
408
+ transform-origin: var(--transform-origin);
409
+ transition: opacity .1s, scale .1s;
410
+ display: flex;
411
+
412
+ &[data-state="closed"] {
413
+ opacity: 0;
414
+ transition-duration: .15s;
415
+ scale: .95;
416
+ }
417
+
418
+ @starting-style {
419
+ opacity: 0;
420
+ scale: .95;
421
+ }
422
+ }
423
+
424
+ .meowdown-block-handle-add, .meowdown-block-handle-drag {
425
+ box-sizing: border-box;
426
+ height: 1.5rem;
427
+ color: color-mix(in oklab, var(--meowdown-muted) 50%, transparent);
428
+ border-radius: .25rem;
429
+ justify-content: center;
430
+ align-items: center;
431
+ display: flex;
432
+
433
+ &:hover {
434
+ background: light-dark(#f4f4f5, #27272a);
435
+ }
436
+
437
+ & svg {
438
+ width: 1.25rem;
439
+ height: 1.25rem;
440
+ display: block;
441
+ }
442
+ }
443
+
444
+ .meowdown-block-handle-add {
445
+ cursor: pointer;
446
+ width: 1.5rem;
447
+ }
448
+
449
+ .meowdown-block-handle-drag {
450
+ cursor: grab;
451
+ width: 1.25rem;
452
+ }
453
+
454
+ @media (prefers-reduced-motion: reduce) {
455
+ .meowdown-block-handle-positioner, .meowdown-block-handle {
456
+ transition: none;
457
+ }
458
+ }
459
+
460
+ .meowdown-drop-indicator {
461
+ z-index: 50;
462
+ background: var(--meowdown-accent);
463
+ transition: all .15s;
464
+ }
465
+
466
+ @media (prefers-reduced-motion: reduce) {
467
+ .meowdown-drop-indicator {
468
+ transition: none;
469
+ }
470
+ }
471
+
472
+ :root {
473
+ --meowdown-node-outline: light-dark(#53f, #8870ff);
474
+ --meowdown-node-selection: light-dark(#5533ff1a, #8870ff1f);
475
+ --meowdown-selection: light-dark(#5533ff38, #8870ff4d);
476
+ }
477
+
478
+ .meowdown {
479
+ flex-direction: column;
480
+ flex: 1 0 auto;
481
+ display: flex;
482
+
483
+ & .ProseMirror {
484
+ flex: 1 0 auto;
485
+
486
+ & ::selection {
487
+ background: var(--meowdown-selection);
488
+ }
489
+
490
+ & .ProseMirror-selectednode {
491
+ outline: 1px solid var(--meowdown-node-outline);
492
+ background: var(--meowdown-node-selection);
493
+ border-radius: 2px;
494
+ }
495
+
496
+ & pre.ProseMirror-selectednode {
497
+ border-radius: .6rem;
498
+ }
499
+
500
+ & .ProseMirror-selectednode {
501
+ --meowdown-selection: transparent;
502
+ }
503
+
504
+ &.prosekit-dragging {
505
+ --meowdown-node-selection: transparent;
506
+ }
507
+ }
508
+
509
+ & [data-editor="codemirror"] {
510
+ flex: 1 0 auto;
511
+ display: flex;
512
+
513
+ & .cm-editor {
514
+ flex: 1;
515
+ font-size: .95rem;
516
+
517
+ &.cm-focused {
518
+ outline: none;
519
+ }
520
+ }
521
+
522
+ & .cm-content {
523
+ padding: 1.25rem var(--meowdown-gutter) 1.75rem;
524
+ font-family: var(--meowdown-font-mono);
525
+ caret-color: var(--meowdown-accent);
526
+ }
527
+ }
528
+ }