codemirror-obsidian-md 0.1.0 → 0.2.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 (93) hide show
  1. package/README.md +114 -3
  2. package/dist/editor/editing/auto-pair.d.ts +24 -0
  3. package/dist/editor/editing/auto-pair.d.ts.map +1 -0
  4. package/dist/editor/editing/auto-pair.js +93 -0
  5. package/dist/editor/editing/auto-pair.js.map +1 -0
  6. package/dist/editor/editing/backspace.d.ts +14 -0
  7. package/dist/editor/editing/backspace.d.ts.map +1 -0
  8. package/dist/editor/editing/backspace.js +19 -0
  9. package/dist/editor/editing/backspace.js.map +1 -0
  10. package/dist/editor/editing/code-fence.d.ts +9 -0
  11. package/dist/editor/editing/code-fence.d.ts.map +1 -0
  12. package/dist/editor/editing/code-fence.js +36 -0
  13. package/dist/editor/editing/code-fence.js.map +1 -0
  14. package/dist/editor/editing/composition.d.ts +6 -0
  15. package/dist/editor/editing/composition.d.ts.map +1 -0
  16. package/dist/editor/editing/composition.js +5 -0
  17. package/dist/editor/editing/composition.js.map +1 -0
  18. package/dist/editor/editing/emphasis.d.ts +4 -0
  19. package/dist/editor/editing/emphasis.d.ts.map +1 -0
  20. package/dist/editor/editing/emphasis.js +68 -0
  21. package/dist/editor/editing/emphasis.js.map +1 -0
  22. package/dist/editor/editing/line-text.d.ts +6 -0
  23. package/dist/editor/editing/line-text.d.ts.map +1 -0
  24. package/dist/editor/editing/line-text.js +10 -0
  25. package/dist/editor/editing/line-text.js.map +1 -0
  26. package/dist/editor/editing/link.d.ts +6 -0
  27. package/dist/editor/editing/link.d.ts.map +1 -0
  28. package/dist/editor/editing/link.js +16 -0
  29. package/dist/editor/editing/link.js.map +1 -0
  30. package/dist/editor/editing/list-continuation.d.ts +9 -0
  31. package/dist/editor/editing/list-continuation.d.ts.map +1 -0
  32. package/dist/editor/editing/list-continuation.js +117 -0
  33. package/dist/editor/editing/list-continuation.js.map +1 -0
  34. package/dist/editor/editing/list-indent.d.ts +10 -0
  35. package/dist/editor/editing/list-indent.d.ts.map +1 -0
  36. package/dist/editor/editing/list-indent.js +163 -0
  37. package/dist/editor/editing/list-indent.js.map +1 -0
  38. package/dist/editor/editing/list-line.d.ts +28 -0
  39. package/dist/editor/editing/list-line.d.ts.map +1 -0
  40. package/dist/editor/editing/list-line.js +56 -0
  41. package/dist/editor/editing/list-line.js.map +1 -0
  42. package/dist/editor/editing/pairs.d.ts +7 -0
  43. package/dist/editor/editing/pairs.d.ts.map +1 -0
  44. package/dist/editor/editing/pairs.js +11 -0
  45. package/dist/editor/editing/pairs.js.map +1 -0
  46. package/dist/editor/editing/paste-url.d.ts +7 -0
  47. package/dist/editor/editing/paste-url.d.ts.map +1 -0
  48. package/dist/editor/editing/paste-url.js +37 -0
  49. package/dist/editor/editing/paste-url.js.map +1 -0
  50. package/dist/editor/editing/selection-wrap.d.ts +15 -0
  51. package/dist/editor/editing/selection-wrap.d.ts.map +1 -0
  52. package/dist/editor/editing/selection-wrap.js +30 -0
  53. package/dist/editor/editing/selection-wrap.js.map +1 -0
  54. package/dist/editor/editing/task-toggle.d.ts +4 -0
  55. package/dist/editor/editing/task-toggle.d.ts.map +1 -0
  56. package/dist/editor/editing/task-toggle.js +32 -0
  57. package/dist/editor/editing/task-toggle.js.map +1 -0
  58. package/dist/editor/editing/typing.d.ts +16 -0
  59. package/dist/editor/editing/typing.d.ts.map +1 -0
  60. package/dist/editor/editing/typing.js +52 -0
  61. package/dist/editor/editing/typing.js.map +1 -0
  62. package/dist/editor/editing-shortcuts.d.ts +25 -0
  63. package/dist/editor/editing-shortcuts.d.ts.map +1 -0
  64. package/dist/editor/editing-shortcuts.js +67 -0
  65. package/dist/editor/editing-shortcuts.js.map +1 -0
  66. package/dist/editor/index.d.ts +1 -0
  67. package/dist/editor/index.d.ts.map +1 -1
  68. package/dist/editor/index.js +1 -0
  69. package/dist/editor/index.js.map +1 -1
  70. package/dist/editor/mount.d.ts.map +1 -1
  71. package/dist/editor/mount.js +2 -0
  72. package/dist/editor/mount.js.map +1 -1
  73. package/dist/editor/multi-cursor.d.ts +10 -0
  74. package/dist/editor/multi-cursor.d.ts.map +1 -0
  75. package/dist/editor/multi-cursor.js +28 -0
  76. package/dist/editor/multi-cursor.js.map +1 -0
  77. package/dist/editor/parse.js +34 -1
  78. package/dist/editor/parse.js.map +1 -1
  79. package/dist/editor/picker.d.ts +1 -0
  80. package/dist/editor/picker.d.ts.map +1 -1
  81. package/dist/editor/picker.js +4 -0
  82. package/dist/editor/picker.js.map +1 -1
  83. package/dist/editor/providers/callout-embed.d.ts.map +1 -1
  84. package/dist/editor/providers/callout-embed.js +55 -32
  85. package/dist/editor/providers/callout-embed.js.map +1 -1
  86. package/dist/editor/providers/inline.d.ts.map +1 -1
  87. package/dist/editor/providers/inline.js +22 -5
  88. package/dist/editor/providers/inline.js.map +1 -1
  89. package/dist/editor/providers/markdown.d.ts.map +1 -1
  90. package/dist/editor/providers/markdown.js +4 -2
  91. package/dist/editor/providers/markdown.js.map +1 -1
  92. package/dist/styles.css +313 -0
  93. package/package.json +5 -4
package/README.md CHANGED
@@ -73,6 +73,35 @@ editor.destroy();
73
73
 
74
74
  Peer dependencies: `remark-obsidian-md` (the parser, ADR 0014), `@codemirror/state` and `@codemirror/view`.
75
75
 
76
+ ### Editor Stylesheet
77
+
78
+ `createEditor` emits `cm-*` classes and styles nothing. For the Live Preview look, opt in with one import, like the plugin Stylesheet (ADR 0008):
79
+
80
+ ```ts
81
+ import "codemirror-obsidian-md/styles.css";
82
+ import "remark-obsidian-md/styles.css"; // Highlights and Callouts, as on the Wiki
83
+ ```
84
+
85
+ The Stylesheet covers headings (2em to 1em, bold, `#` markers dimmed on the cursor line), emphasis, inline code, quotes, bullets, task checkboxes, Wiki Links, Embeds, a Callout drawn as one box, the `[[` picker, and a cursor and selection that follow the theme. It has no dark-mode logic and does not read `prefers-color-scheme`: set the CSS variables from your own tokens, under `.dark` too. Every variable is optional and defaults to the inherited text colour or a neutral value.
86
+
87
+ ```css
88
+ body {
89
+ --obsidian-editor-fg: var(--foreground);
90
+ --obsidian-editor-bg: var(--background);
91
+ --obsidian-editor-cursor: var(--foreground);
92
+ --obsidian-editor-selection: color-mix(
93
+ in oklab,
94
+ var(--primary) 30%,
95
+ transparent
96
+ );
97
+ --obsidian-editor-popover-bg: var(
98
+ --popover
99
+ ); /* the `[[` picker mounts on body */
100
+ }
101
+ ```
102
+
103
+ Variables: `--obsidian-editor-` + `font`, `mono-font`, `line-height`, `fg`, `bg`, `cursor`, `selection`, `muted`, `accent`, `border`, `code-bg`, `popover-fg`, `popover-bg`, `popover-border`, `active-fg`, `active-bg`, `radius`, `heading-weight`, `h1-size` to `h6-size`. Define them on `body` or an ancestor that also contains the picker, which CodeMirror mounts outside the editor.
104
+
76
105
  ### How it works
77
106
 
78
107
  1. `createNoteParser()` parses the Note with `unified().use(remarkParse).use(remarkGfm).use(remarkMath).use(remarkObsidianMd)` (parse only, no transform, so Comments keep their positions). Frontmatter is skipped. Tables, fenced code, math, Mermaid and reference footnotes (`PLAIN_SOURCE_TYPES`) yield no constructs, so they stay plain source.
@@ -95,22 +124,99 @@ Later Live Preview work adds to the core without changing it.
95
124
  types: ["obsidianHighlight"], // MDAST node types: wikiLink, embed, obsidianComment, obsidianFootnote, blockquote, heading, ...
96
125
  decorate(construct, { revealed }) {
97
126
  if (revealed) return null; // cursor inside: show the raw syntax
98
- return Decoration.mark({ class: "obsidian-highlight" }).range(construct.from, construct.to);
127
+ return Decoration.mark({ class: "obsidian-highlight" }).range(
128
+ construct.from,
129
+ construct.to,
130
+ );
99
131
  },
100
132
  };
101
133
  ```
102
134
 
103
135
  A provider returns one `Range<Decoration>`, an array of them, or nothing. Decorations may hide, replace or restyle text, never change it. Widgets are CodeMirror widgets reusing the plugin Stylesheet class names.
136
+
104
137
  - **Cursor reveal.** `context.revealed` is `isRevealed(state, construct.from, construct.to)`. Call `isRevealed(state, from, to)` yourself for another range (a whole Callout, a line).
105
138
  - **Parser.** Pass `parser: (text) => Construct[]` to replace the default, e.g. to add block-level scans such as `scanBlockIds`.
106
139
  - **Extra extensions** (autocomplete picker, keymaps, save flow): pass `extensions`, or use `obsidianMd(doc, options)` to compose the core into your own `EditorState`. `reparse(view)` forces a re-parse; `constructsField` and `decorationsField` expose the state.
107
140
 
108
- - **Callouts and Embeds.** `calloutEmbed({ types?, resolveAsset? })` (pass it in `extensions`) renders Callouts, Asset Embeds (images) and Note Embeds (chips) with the plugin Stylesheet class names; import `remark-obsidian-md/styles.css` for the colors and icons. The editor gets the `markdown-rendered` class.
141
+ - **Callouts and Embeds.** `calloutEmbed({ types?, resolveAsset? })` (pass it in `extensions`) renders Callouts, Asset Embeds (images) and Note Embeds (chips) with the plugin Stylesheet class names; import `remark-obsidian-md/styles.css` for the colors and icons. The editor gets the `markdown-rendered` class (the Stylesheet above joins a Callout's lines into one box).
109
142
 
110
143
  Decorations are rebuilt when a re-parse lands or the selection moves, and only mapped while typing.
111
144
 
112
145
  Line breaks: the document always splits on `\n`, so a `\r` stays a character and positions equal text offsets. In a Note that is purely CRLF, Enter inserts `\r\n`.
113
146
 
147
+ ### Editing Shortcuts
148
+
149
+ `editingShortcuts(options?)` (pass it in `extensions`, from `codemirror-obsidian-md/editor`) gives the editor Obsidian's editing feel: formatting and list keys, typing behaviors, and list continuation. It is opt-in, so an editor without it is unchanged. It runs at high precedence so it beats the stock `defaultKeymap`, applies at every cursor, and does nothing while an IME is composing. Every behavior is a plain text edit, so Live Preview and the Save payload are unchanged. Desktop is the target; mobile and soft keyboards are best effort, so do not promise them to your users.
150
+
151
+ ```ts
152
+ editingShortcuts({
153
+ // Keyed by command name, merged over the defaults. `false` disables one.
154
+ keys: { bold: "Mod-j", italic: false },
155
+ // Typing behaviors, each on by default.
156
+ listContinuation: true,
157
+ autoPair: true,
158
+ wikiLinkAutoClose: true,
159
+ pasteUrlAsLink: true,
160
+ selectionWrap: true,
161
+ });
162
+ ```
163
+
164
+ #### Commands
165
+
166
+ Rebind or disable a command by name through `keys`. `Mod` is Ctrl on Windows and Linux and Cmd on macOS. `DEFAULT_EDITING_KEYS` exports the defaults.
167
+
168
+ | Command | Default | What it does |
169
+ | ------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
170
+ | `bold` | `Mod-b` | Toggles bold. Detects `**` and `__`, always wraps with `**`, leaves the inner text selected, and inserts an empty `****` with the cursor inside when nothing is selected. |
171
+ | `italic` | `Mod-i` | The same for italic: detects `*` and `_`, wraps with `_`, and inserts an empty `__` with the cursor inside when nothing is selected. |
172
+ | `link` | `Mod-k` | Makes the selection `[selection]()` with the cursor in the parentheses, or inserts `[]()` with the cursor in the brackets. |
173
+ | `indent` | `Mod-]` | Indents the current line or every selected line by one tab, list or not, and renumbers numbered items. |
174
+ | `unindent` | `Mod-[` | Removes one tab (or up to four spaces) from those lines, and renumbers. |
175
+ | `toggleTask` | `Mod-Enter` | Flips `- [ ]` and `- [x]`, turns a plain bullet or numbered item into an unchecked task, and does nothing on any other line. |
176
+
177
+ Each range toggles by its own state, so a mix of bold and plain ranges is not wrongly unwrapped.
178
+
179
+ #### Behavior flags
180
+
181
+ | Flag | Default | Behavior |
182
+ | ------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
183
+ | `selectionWrap` | `true` | Typing a wrap character over a selection wraps it and leaves the inner text selected, so a repeat of the same key nests (`*` twice gives `**x**`). Each wrap is its own undo step. With nothing selected it does nothing. |
184
+ | `autoPair` | `true` | With nothing selected, typing an opener inserts its closer too with the cursor between. Typing a closer steps over an identical next character. Backspace between an opener and its closer deletes both. `'` and `"` do not pair after a word character, so "don't" is untouched. |
185
+ | `wikiLinkAutoClose` | `true` | Typing `[` twice gives `[[]]` with the cursor inside, which opens the `[[` picker when `pickerExtension` is installed; a typed `]` steps over the `]]`. It is independent of `autoPair`: with `autoPair` off, this alone closes `[[` and leaves a single `[` unpaired. With `autoPair` on, `[` already pairs, so `[[]]` results either way and `false` changes nothing. It never leaves an unbalanced `[[]`. `Ctrl-Space` inside `[[…]]` reopens the picker after it was dismissed. |
186
+ | `pasteUrlAsLink` | `true` | Pasting a single http(s) URL over selected text makes `[text](url)` and leaves the cursor after it. With nothing selected the paste is left to the browser. |
187
+ | `listContinuation` | `true` | Enter continues a bullet (`-`, `*`, `+`), numbered (`1.`, `1)`, counting up), task (`- [ ]`, continued unchecked), quote or Callout (`>`) line with the same marker and indentation, and splits an item at the cursor. Enter on an empty item and Backspace on an empty marker remove the marker. Also owns Tab and Shift-Tab (below). |
188
+
189
+ List continuation does nothing inside a fenced code block, treats a `%%` Comment as ordinary text, and keeps a CRLF Note on `\r\n`.
190
+
191
+ Tab and Shift-Tab are part of list behavior, not named commands, so `listContinuation: false` switches them off. On a bullet, numbered, task or quote item they indent and unindent it by one level (a tab character; Shift-Tab removes a tab or up to four spaces) and renumber the numbered items they affect and the ones that follow at that level. An item moved to the first place at its level restarts at 1.
192
+
193
+ #### Fixed character sets
194
+
195
+ Which characters wrap and which pair is Obsidian's behavior, not a preference, and cannot be configured (ADR 0016).
196
+
197
+ - **Selection Wrap:** `*`, `_`, `` ` ``, `~`, `=`, `[`, `(`, `{`, `"`, `'`. `[`, `(` and `{` close with `]`, `)` and `}`; the others close with themselves.
198
+ - **Auto-pair:** `(`, `[`, `{`, `"`, `'`, `` ` ``. The markers `*`, `_`, `~` and `=` never pair.
199
+
200
+ #### Multiple cursors
201
+
202
+ `createEditor` supports several cursors, drawn by CodeMirror (the native caret and selection are hidden, so the Stylesheet styles `.cm-cursor` and `.cm-selectionBackground`):
203
+
204
+ - Alt-click (Option-click on macOS) adds a cursor.
205
+ - Shift-Alt-drag (Shift-Option on macOS) or a middle-mouse drag selects a rectangle.
206
+ - Ctrl-Alt-Up and Ctrl-Alt-Down (Cmd-Alt on macOS) add a cursor above or below.
207
+ - Escape collapses to one.
208
+
209
+ Typing, deleting and every command and flag above apply at each cursor. The `[[` picker stays closed while there are several, and `Ctrl-Space` does nothing. See `docs/research/obsidian-editing-parity.md` for how these gestures compare to Obsidian's.
210
+
211
+ #### Departures from Obsidian
212
+
213
+ - **Tab outside a list** falls through, so the browser moves keyboard focus out of the editor. Obsidian inserts a tab character. The same holds inside a fenced code block.
214
+ - **Shift-Tab on a list item** is always consumed, even on a top-level item that cannot be unindented, so it never moves focus from inside a list. Outside a list it falls through like Tab.
215
+ - **`Mod-k` stops propagating** while the editor handles it, so a host's global `Mod-k` (the Fumadocs search dialog) does not also fire. Outside the editor it is untouched. Obsidian uses `Mod-k` for inserting a link, so the extension resolves the collision itself.
216
+ - **Every Editing Shortcut key** is claimed even when it has nothing to do (`Mod-Enter` on a line that is not a list item, or any of them while composing), so the stock binding (`Mod-Enter` inserts a line) never fires instead.
217
+
218
+ Pasting a multi-line list is not specially handled: the text is inserted as it is.
219
+
114
220
  ### `[[` picker
115
221
 
116
222
  `pickerExtension({ candidates, resolver?, maxResults? })` (pass it in `extensions`) opens a Note picker when `[[` is typed. `candidates` is `NoteCandidate[]` (`{ path, title?, aliases? }`) or a getter, ranked by `rankCandidates`. Choosing one inserts a closed Wiki Link; `wikiLinkTarget` uses the bare name when the injected `Resolver` (or, by default, name uniqueness) reaches that Note, else the path.
@@ -128,7 +234,12 @@ Line breaks: the document always splits on `\n`, so a `\r` stays a character and
128
234
  `inlineProviders` renders Wiki Links, Highlights (with Highlight Color), Comments, inline Footnotes and Block IDs, revealing the raw syntax under the cursor. Block IDs are not parse nodes, so wrap the parser with `blockIdParser()`:
129
235
 
130
236
  ```ts
131
- createEditor({ parent, doc, providers: inlineProviders, parser: blockIdParser() });
237
+ createEditor({
238
+ parent,
239
+ doc,
240
+ providers: inlineProviders,
241
+ parser: blockIdParser(),
242
+ });
132
243
  ```
133
244
 
134
245
  ## New Note screen
@@ -0,0 +1,24 @@
1
+ import { type EditorState, type SelectionRange } from "@codemirror/state";
2
+ /** Whether the character typed here can be handled by `pairRange` at all. */
3
+ export declare function isPairKey(text: string): boolean;
4
+ /**
5
+ * What typing `text` does at an empty `range`: inserts its closer too, or
6
+ * steps over an identical closer already there. `null` leaves the key to the
7
+ * default input.
8
+ *
9
+ * With `pairing` off only the `[[` -> `[[]]` close is left (and stepping over
10
+ * its `]]`), so a consumer can drop general pairing and keep that one.
11
+ */
12
+ export declare function pairRange(state: EditorState, range: SelectionRange, text: string, pairing: boolean): {
13
+ range: SelectionRange;
14
+ changes?: never;
15
+ } | {
16
+ changes: {
17
+ from: number;
18
+ insert: string;
19
+ };
20
+ range: SelectionRange;
21
+ } | null;
22
+ /** Backspace between an opener and its closer deletes both. */
23
+ export declare const autoPairBackspace: import("@codemirror/state").Extension;
24
+ //# sourceMappingURL=auto-pair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-pair.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/auto-pair.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAwB3B,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO;;;;;;;;;SAgCjB;AAoCD,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,uCAK5B,CAAC"}
@@ -0,0 +1,93 @@
1
+ import { EditorSelection, } from "@codemirror/state";
2
+ import { keymap } from "@codemirror/view";
3
+ import { plainBackspace } from "./backspace.js";
4
+ import { isComposing } from "./composition.js";
5
+ import { BRACKET_CLOSERS } from "./pairs.js";
6
+ /** Openers that insert their closer too: brackets, quotes and the backtick. */
7
+ const PAIRS = {
8
+ ...BRACKET_CLOSERS,
9
+ '"': '"',
10
+ "'": "'",
11
+ "`": "`",
12
+ };
13
+ const CLOSERS = new Set(Object.values(BRACKET_CLOSERS));
14
+ const WORD = /[\p{L}\p{N}_]/u;
15
+ /** Whether the character before `pos` is a word character. */
16
+ function afterWord(state, pos) {
17
+ return pos > 0 && WORD.test(state.sliceDoc(pos - 1, pos));
18
+ }
19
+ /** Whether the character typed here can be handled by `pairRange` at all. */
20
+ export function isPairKey(text) {
21
+ return PAIRS[text] !== undefined || CLOSERS.has(text);
22
+ }
23
+ /**
24
+ * What typing `text` does at an empty `range`: inserts its closer too, or
25
+ * steps over an identical closer already there. `null` leaves the key to the
26
+ * default input.
27
+ *
28
+ * With `pairing` off only the `[[` -> `[[]]` close is left (and stepping over
29
+ * its `]]`), so a consumer can drop general pairing and keep that one.
30
+ */
31
+ export function pairRange(state, range, text, pairing) {
32
+ const next = state.sliceDoc(range.from, range.from + 1);
33
+ const close = PAIRS[text];
34
+ if (!pairing) {
35
+ const line = state.doc.lineAt(range.from);
36
+ if (text === "]" &&
37
+ next === "]" &&
38
+ line.text.slice(0, range.from - line.from).includes("[["))
39
+ return { range: EditorSelection.cursor(range.from + 1) };
40
+ if (text === "[" && state.sliceDoc(range.from - 1, range.from) === "[") {
41
+ return {
42
+ changes: { from: range.from, insert: "[]]" },
43
+ range: EditorSelection.cursor(range.from + 1),
44
+ };
45
+ }
46
+ return null;
47
+ }
48
+ if (close === undefined && !CLOSERS.has(text))
49
+ return null;
50
+ if (next === text)
51
+ return { range: EditorSelection.cursor(range.from + 1) };
52
+ if (close === undefined || (isQuote(text) && afterWord(state, range.from))) {
53
+ return {
54
+ changes: { from: range.from, insert: text },
55
+ range: EditorSelection.cursor(range.from + text.length),
56
+ };
57
+ }
58
+ return {
59
+ changes: { from: range.from, insert: text + close },
60
+ range: EditorSelection.cursor(range.from + text.length),
61
+ };
62
+ }
63
+ function isQuote(text) {
64
+ return text === '"' || text === "'";
65
+ }
66
+ /** Whether the cursor sits between an opener and its closer. */
67
+ function inEmptyPair(state, pos) {
68
+ if (pos === 0)
69
+ return false;
70
+ const before = state.sliceDoc(pos - 1, pos);
71
+ return (PAIRS[before] !== undefined &&
72
+ PAIRS[before] === state.sliceDoc(pos, pos + 1));
73
+ }
74
+ function deleteEmptyPair(view) {
75
+ const { state } = view;
76
+ if (!state.selection.ranges.some((r) => r.empty && inEmptyPair(state, r.from)))
77
+ return false;
78
+ view.dispatch(state.changeByRange((range) => range.empty && inEmptyPair(state, range.from)
79
+ ? {
80
+ changes: { from: range.from - 1, to: range.from + 1 },
81
+ range: EditorSelection.cursor(range.from - 1),
82
+ }
83
+ : plainBackspace(state, range)), { userEvent: "delete.backward", scrollIntoView: true });
84
+ return true;
85
+ }
86
+ /** Backspace between an opener and its closer deletes both. */
87
+ export const autoPairBackspace = keymap.of([
88
+ {
89
+ key: "Backspace",
90
+ run: (view) => !isComposing(view) && deleteEmptyPair(view),
91
+ },
92
+ ]);
93
+ //# sourceMappingURL=auto-pair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-pair.js","sourceRoot":"","sources":["../../../src/editor/editing/auto-pair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type EditorState, type SelectionRange } from "@codemirror/state";
2
+ /**
3
+ * What a plain Backspace does at `range`: deletes the selection, or the
4
+ * character before the cursor. For commands that handle only some of several
5
+ * ranges and leave the rest to behave as usual.
6
+ */
7
+ export declare function plainBackspace(state: EditorState, range: SelectionRange): {
8
+ changes: {
9
+ from: number;
10
+ to: number;
11
+ };
12
+ range: SelectionRange;
13
+ };
14
+ //# sourceMappingURL=backspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backspace.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/backspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc;;;;;;EAWvE"}
@@ -0,0 +1,19 @@
1
+ import { EditorSelection, findClusterBreak, } from "@codemirror/state";
2
+ /**
3
+ * What a plain Backspace does at `range`: deletes the selection, or the
4
+ * character before the cursor. For commands that handle only some of several
5
+ * ranges and leave the rest to behave as usual.
6
+ */
7
+ export function plainBackspace(state, range) {
8
+ const line = state.doc.lineAt(range.from);
9
+ const from = !range.empty
10
+ ? range.from
11
+ : range.from === line.from
12
+ ? Math.max(0, range.from - 1)
13
+ : line.from + findClusterBreak(line.text, range.from - line.from, false);
14
+ return {
15
+ changes: { from, to: range.to },
16
+ range: EditorSelection.cursor(from),
17
+ };
18
+ }
19
+ //# sourceMappingURL=backspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backspace.js","sourceRoot":"","sources":["../../../src/editor/editing/backspace.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAEf,gBAAgB,GAEjB,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB,EAAE,KAAqB;IACtE,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK;QACvB,CAAC,CAAC,KAAK,CAAC,IAAI;QACZ,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;YACxB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;YAC7B,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7E,OAAO;QACL,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;QAC/B,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Text } from "@codemirror/state";
2
+ /**
3
+ * The lines (1-based) that sit in a fenced code block, including the lines
4
+ * that open and close it. A fence closes on a line of the same character that
5
+ * is at least as long and carries nothing else. One pass over the document,
6
+ * so a command asks once and not per line.
7
+ */
8
+ export declare function fencedLines(doc: Text): ReadonlySet<number>;
9
+ //# sourceMappingURL=code-fence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-fence.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/code-fence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAM9C;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CA0B1D"}
@@ -0,0 +1,36 @@
1
+ import { stripCr } from "./line-text.js";
2
+ const FENCE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
3
+ /**
4
+ * The lines (1-based) that sit in a fenced code block, including the lines
5
+ * that open and close it. A fence closes on a line of the same character that
6
+ * is at least as long and carries nothing else. One pass over the document,
7
+ * so a command asks once and not per line.
8
+ */
9
+ export function fencedLines(doc) {
10
+ const fenced = new Set();
11
+ let open = null;
12
+ for (let n = 1; n <= doc.lines; n++) {
13
+ const match = FENCE.exec(stripCr(doc.line(n).text));
14
+ if (match) {
15
+ const [, run = "", rest = ""] = match;
16
+ if (open === null) {
17
+ if (!(run[0] === "`" && rest.includes("`"))) {
18
+ open = run;
19
+ fenced.add(n);
20
+ continue;
21
+ }
22
+ }
23
+ else if (run[0] === open[0] &&
24
+ run.length >= open.length &&
25
+ !rest.trim()) {
26
+ fenced.add(n);
27
+ open = null;
28
+ continue;
29
+ }
30
+ }
31
+ if (open !== null)
32
+ fenced.add(n);
33
+ }
34
+ return fenced;
35
+ }
36
+ //# sourceMappingURL=code-fence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-fence.js","sourceRoot":"","sources":["../../../src/editor/editing/code-fence.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,MAAM,KAAK,GAAG,2BAA2B,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,GAAS;IACnC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC;YACtC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBAC5C,IAAI,GAAG,GAAG,CAAC;oBACX,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBACd,SAAS;gBACX,CAAC;YACH,CAAC;iBAAM,IACL,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;gBAClB,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM;gBACzB,CAAC,IAAI,CAAC,IAAI,EAAE,EACZ,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACd,IAAI,GAAG,IAAI,CAAC;gBACZ,SAAS;YACX,CAAC;QACH,CAAC;QACD,IAAI,IAAI,KAAK,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** Whether an IME is composing; every Editing Shortcut stays out of its way. */
2
+ export declare function isComposing(view: {
3
+ composing: boolean;
4
+ compositionStarted: boolean;
5
+ }): boolean;
6
+ //# sourceMappingURL=composition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/composition.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;CAC7B,GAAG,OAAO,CAEV"}
@@ -0,0 +1,5 @@
1
+ /** Whether an IME is composing; every Editing Shortcut stays out of its way. */
2
+ export function isComposing(view) {
3
+ return view.composing || view.compositionStarted;
4
+ }
5
+ //# sourceMappingURL=composition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composition.js","sourceRoot":"","sources":["../../../src/editor/editing/composition.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,IAG3B;IACC,OAAO,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,kBAAkB,CAAC;AACnD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Command } from "@codemirror/view";
2
+ export declare const toggleBold: Command;
3
+ export declare const toggleItalic: Command;
4
+ //# sourceMappingURL=emphasis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emphasis.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/emphasis.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAqFhD,eAAO,MAAM,UAAU,SAAe,CAAC;AACvC,eAAO,MAAM,YAAY,SAAc,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { EditorSelection, } from "@codemirror/state";
2
+ const MARKER_CHARS = ["*", "_"];
3
+ /** How many `ch` repeat going away from `pos` (backward or forward). */
4
+ function markerRun(doc, pos, dir, ch, limit) {
5
+ let n = 0;
6
+ while (n < limit) {
7
+ const at = dir < 0 ? pos - n - 1 : pos + n;
8
+ if (at < 0 || at >= doc.length || doc.sliceString(at, at + 1) !== ch)
9
+ break;
10
+ n++;
11
+ }
12
+ return n;
13
+ }
14
+ /**
15
+ * Finds the emphasis markers of `size` (2 bold, 1 italic) around a range,
16
+ * either just outside the selection or at its own edges. A run of three is
17
+ * bold and italic at once.
18
+ */
19
+ function findMarkers(doc, range, size) {
20
+ const fits = (n) => (size === 2 ? n >= 2 : n % 2 === 1);
21
+ for (const ch of MARKER_CHARS) {
22
+ const outside = Math.min(markerRun(doc, range.from, -1, ch, 3), markerRun(doc, range.to, 1, ch, 3));
23
+ if (fits(outside)) {
24
+ return { open: range.from - size, close: range.to, inside: false };
25
+ }
26
+ }
27
+ if (range.empty)
28
+ return null;
29
+ const half = Math.floor((range.to - range.from) / 2);
30
+ for (const ch of MARKER_CHARS) {
31
+ const inside = Math.min(markerRun(doc, range.from, 1, ch, Math.min(3, half)), markerRun(doc, range.to, -1, ch, Math.min(3, half)));
32
+ if (fits(inside)) {
33
+ return { open: range.from, close: range.to - size, inside: true };
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+ function toggle(marker) {
39
+ const size = marker.length;
40
+ return (view) => {
41
+ const { state } = view;
42
+ view.dispatch(state.update(state.changeByRange((range) => {
43
+ const found = findMarkers(state.doc, range, size);
44
+ if (!found) {
45
+ return {
46
+ changes: [
47
+ { from: range.from, insert: marker },
48
+ { from: range.to, insert: marker },
49
+ ],
50
+ range: EditorSelection.range(range.from + size, range.to + size),
51
+ };
52
+ }
53
+ // The selection lands on the inner text, in post-edit coordinates.
54
+ const length = range.to - range.from - (found.inside ? 2 * size : 0);
55
+ return {
56
+ changes: [
57
+ { from: found.open, to: found.open + size },
58
+ { from: found.close, to: found.close + size },
59
+ ],
60
+ range: EditorSelection.range(found.open, found.open + length),
61
+ };
62
+ }), { userEvent: "input", scrollIntoView: true }));
63
+ return true;
64
+ };
65
+ }
66
+ export const toggleBold = toggle("**");
67
+ export const toggleItalic = toggle("_");
68
+ //# sourceMappingURL=emphasis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emphasis.js","sourceRoot":"","sources":["../../../src/editor/editing/emphasis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { EditorState } from "@codemirror/state";
2
+ /** A line's text without the `\r` of a CRLF line break. */
3
+ export declare function stripCr(text: string): string;
4
+ /** Where the line at `pos` ends, before any `\r` of a CRLF line break. */
5
+ export declare function contentEnd(state: EditorState, pos: number): number;
6
+ //# sourceMappingURL=line-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-text.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/line-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,2DAA2D;AAC3D,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGlE"}
@@ -0,0 +1,10 @@
1
+ /** A line's text without the `\r` of a CRLF line break. */
2
+ export function stripCr(text) {
3
+ return text.replace(/\r$/, "");
4
+ }
5
+ /** Where the line at `pos` ends, before any `\r` of a CRLF line break. */
6
+ export function contentEnd(state, pos) {
7
+ const line = state.doc.lineAt(pos);
8
+ return Math.min(pos, line.from + stripCr(line.text).length);
9
+ }
10
+ //# sourceMappingURL=line-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-text.js","sourceRoot":"","sources":["../../../src/editor/editing/line-text.ts"],"names":[],"mappings":"AAEA,2DAA2D;AAC3D,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,KAAkB,EAAE,GAAW;IACxD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Command } from "@codemirror/view";
2
+ /** `Mod-k`: wraps each selection as `[text]()` with the cursor in the
3
+ * parentheses; with nothing selected it inserts `[]()` with the cursor inside
4
+ * the brackets. */
5
+ export declare const insertLink: Command;
6
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/link.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD;;mBAEmB;AACnB,eAAO,MAAM,UAAU,EAAE,OAiBxB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { EditorSelection } from "@codemirror/state";
2
+ /** `Mod-k`: wraps each selection as `[text]()` with the cursor in the
3
+ * parentheses; with nothing selected it inserts `[]()` with the cursor inside
4
+ * the brackets. */
5
+ export const insertLink = (view) => {
6
+ const { state } = view;
7
+ view.dispatch(state.update(state.changeByRange((range) => ({
8
+ changes: [
9
+ { from: range.from, insert: "[" },
10
+ { from: range.to, insert: "]()" },
11
+ ],
12
+ range: EditorSelection.cursor(range.empty ? range.to + 1 : range.to + 3),
13
+ })), { userEvent: "input", scrollIntoView: true }));
14
+ return true;
15
+ };
16
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../../src/editor/editing/link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;mBAEmB;AACnB,MAAM,CAAC,MAAM,UAAU,GAAY,CAAC,IAAI,EAAE,EAAE;IAC1C,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,QAAQ,CACX,KAAK,CAAC,MAAM,CACV,KAAK,CAAC,aAAa,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC9B,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;YACjC,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;SAClC;QACD,KAAK,EAAE,eAAe,CAAC,MAAM,CAC3B,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAC1C;KACF,CAAC,CAAC,EACH,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,CAC7C,CACF,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * List continuation (Enter, Backspace), above the stock and CRLF Enter.
3
+ *
4
+ * Tab and Shift-Tab are registered here too, on purpose: they are part of the
5
+ * list behavior and have no key override of their own, so the
6
+ * `listContinuation` flag switches them off with Enter and Backspace (ADR 0016).
7
+ */
8
+ export declare function listContinuation(): import("@codemirror/state").Extension;
9
+ //# sourceMappingURL=list-continuation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-continuation.d.ts","sourceRoot":"","sources":["../../../src/editor/editing/list-continuation.ts"],"names":[],"mappings":"AA6GA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,0CAS/B"}