@orz/ui-codemirror 0.0.0-stage → 0.0.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 (76) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +251 -2
  3. package/dist/components/code-editor.d.ts +94 -0
  4. package/dist/components/code-editor.d.ts.map +1 -0
  5. package/dist/components/code-editor.js +243 -0
  6. package/dist/components/code-editor.js.map +1 -0
  7. package/dist/components/editor-shortcuts-help.d.ts +7 -0
  8. package/dist/components/editor-shortcuts-help.d.ts.map +1 -0
  9. package/dist/components/editor-shortcuts-help.js +29 -0
  10. package/dist/components/editor-shortcuts-help.js.map +1 -0
  11. package/dist/components/markdown-editor.d.ts +83 -0
  12. package/dist/components/markdown-editor.d.ts.map +1 -0
  13. package/dist/components/markdown-editor.js +149 -0
  14. package/dist/components/markdown-editor.js.map +1 -0
  15. package/dist/extensions/code-context.d.ts +15 -0
  16. package/dist/extensions/code-context.d.ts.map +1 -0
  17. package/dist/extensions/code-context.js +41 -0
  18. package/dist/extensions/code-context.js.map +1 -0
  19. package/dist/extensions/completion-space.d.ts +7 -0
  20. package/dist/extensions/completion-space.d.ts.map +1 -0
  21. package/dist/extensions/completion-space.js +76 -0
  22. package/dist/extensions/completion-space.js.map +1 -0
  23. package/dist/extensions/completion.d.ts +29 -0
  24. package/dist/extensions/completion.d.ts.map +1 -0
  25. package/dist/extensions/completion.js +85 -0
  26. package/dist/extensions/completion.js.map +1 -0
  27. package/dist/extensions/composition-safe-measure.d.ts +21 -0
  28. package/dist/extensions/composition-safe-measure.d.ts.map +1 -0
  29. package/dist/extensions/composition-safe-measure.js +56 -0
  30. package/dist/extensions/composition-safe-measure.js.map +1 -0
  31. package/dist/extensions/json.d.ts +9 -0
  32. package/dist/extensions/json.d.ts.map +1 -0
  33. package/dist/extensions/json.js +21 -0
  34. package/dist/extensions/json.js.map +1 -0
  35. package/dist/extensions/parked-tooltip.d.ts +19 -0
  36. package/dist/extensions/parked-tooltip.d.ts.map +1 -0
  37. package/dist/extensions/parked-tooltip.js +32 -0
  38. package/dist/extensions/parked-tooltip.js.map +1 -0
  39. package/dist/extensions/paste-as.d.ts +21 -0
  40. package/dist/extensions/paste-as.d.ts.map +1 -0
  41. package/dist/extensions/paste-as.js +448 -0
  42. package/dist/extensions/paste-as.js.map +1 -0
  43. package/dist/extensions/quote-toggle.d.ts +16 -0
  44. package/dist/extensions/quote-toggle.d.ts.map +1 -0
  45. package/dist/extensions/quote-toggle.js +65 -0
  46. package/dist/extensions/quote-toggle.js.map +1 -0
  47. package/dist/extensions/slash-commands.d.ts +7 -0
  48. package/dist/extensions/slash-commands.d.ts.map +1 -0
  49. package/dist/extensions/slash-commands.js +249 -0
  50. package/dist/extensions/slash-commands.js.map +1 -0
  51. package/dist/extensions/tag-completion.d.ts +24 -0
  52. package/dist/extensions/tag-completion.d.ts.map +1 -0
  53. package/dist/extensions/tag-completion.js +81 -0
  54. package/dist/extensions/tag-completion.js.map +1 -0
  55. package/dist/extensions/tooltip-space.d.ts +11 -0
  56. package/dist/extensions/tooltip-space.d.ts.map +1 -0
  57. package/dist/extensions/tooltip-space.js +41 -0
  58. package/dist/extensions/tooltip-space.js.map +1 -0
  59. package/dist/extensions/trim-trailing-space.d.ts +15 -0
  60. package/dist/extensions/trim-trailing-space.d.ts.map +1 -0
  61. package/dist/extensions/trim-trailing-space.js +56 -0
  62. package/dist/extensions/trim-trailing-space.js.map +1 -0
  63. package/dist/lib/html-to-markdown.d.ts +22 -0
  64. package/dist/lib/html-to-markdown.d.ts.map +1 -0
  65. package/dist/lib/html-to-markdown.js +327 -0
  66. package/dist/lib/html-to-markdown.js.map +1 -0
  67. package/dist/lib/paste-converter.d.ts +14 -0
  68. package/dist/lib/paste-converter.d.ts.map +1 -0
  69. package/dist/lib/paste-converter.js +27 -0
  70. package/dist/lib/paste-converter.js.map +1 -0
  71. package/dist/lib/platform.d.ts +13 -0
  72. package/dist/lib/platform.d.ts.map +1 -0
  73. package/dist/lib/platform.js +19 -0
  74. package/dist/lib/platform.js.map +1 -0
  75. package/package.json +83 -3
  76. package/styles.css +4 -0
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright 2026 the orzui team
4
+ Copyright 2026 the todou team
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -1,3 +1,252 @@
1
- # Temporary Holding Version
1
+ # @orz/ui-codemirror
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ CodeMirror 6 editors themed by `@orz/ui`: a prose-first `CodeEditor` shell, a GFM `MarkdownEditor`,
4
+ the completion extensions they are assembled from, and the slash-command panel and highlight over
5
+ `@orz/query`'s command grammar.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @orz/ui-codemirror @orz/ui react react-dom lucide-react sonner \
11
+ @codemirror/state @codemirror/view @codemirror/language @codemirror/autocomplete \
12
+ @codemirror/commands @codemirror/search @lezer/highlight
13
+ ```
14
+
15
+ These are peer dependencies, so the app and the editors share one copy of each:
16
+
17
+ - `@orz/ui`: every editor registers itself with `useDirtySource` from `@orz/ui/lib/unsaved-guard`,
18
+ a module-level registry. A second copy of `@orz/ui` would hold a second registry, and the app's
19
+ navigation guard would never see an edited editor.
20
+ - CodeMirror and `@lezer/highlight`: you pass your own extensions into these editors, and
21
+ CodeMirror checks extensions, facets and state fields by identity. A second `@codemirror/state`
22
+ raises "Unrecognized extension value", a second `@codemirror/language` gives your `syntaxTree()`
23
+ a different language facet, and a second `@lezer/highlight` gives your `tags` a different
24
+ identity, so your `HighlightStyle` would silently colour nothing.
25
+
26
+ The markdown and JSON parsers (`@lezer/markdown`, `@lezer/json`) are ordinary dependencies. The
27
+ package does not use `@codemirror/lang-markdown`, which pulls in the HTML, JS and CSS parsers for
28
+ about 170 KB gzip. Neither is the HTML-to-Markdown converter a paste uses (`hast-util-from-dom`,
29
+ `hast-util-to-mdast`, `mdast-util-to-markdown`, `mdast-util-gfm`): it is a dynamic import, so a
30
+ bundler splits it into a chunk of its own, fetched once an editor is idle. `sonner` carries the
31
+ one message a paste can raise, through `@orz/ui`'s `Toaster`.
32
+
33
+ ## CSS
34
+
35
+ ```css
36
+ @import "tailwindcss";
37
+ @import "@orz/ui/styles.css";
38
+ @import "@orz/ui-codemirror/styles.css";
39
+ ```
40
+
41
+ Import it after `@orz/ui/styles.css`. The editor's host element is styled with the same Tailwind
42
+ utilities as `@orz/ui`'s `Textarea`, and the stylesheet registers `@source "./dist"` so Tailwind
43
+ finds them. Everything inside the editor is styled with `EditorView.theme` against `@orz/ui`'s CSS
44
+ variables. If you do not import the stylesheet, point Tailwind at the package yourself:
45
+
46
+ ```css
47
+ @source "../node_modules/@orz/ui-codemirror/dist";
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ### A markdown editor
53
+
54
+ ```tsx
55
+ import {
56
+ MarkdownEditor,
57
+ type MarkdownEditorHandle,
58
+ } from "@orz/ui-codemirror/components/markdown-editor";
59
+
60
+ const editor = useRef<MarkdownEditorHandle>(null);
61
+
62
+ <MarkdownEditor
63
+ ref={editor}
64
+ ariaLabel="Comment"
65
+ placeholder="Write a comment…"
66
+ onSubmit={(value) => send(value)}
67
+ onCancel={() => close()}
68
+ className="min-h-24 max-h-96"
69
+ />;
70
+ ```
71
+
72
+ The document lives in the EditorView, not in React state: read it through the handle or from
73
+ `onSubmit`, and keep `onChange` for cheap derived flags. Mod-Enter submits, Escape cancels, and
74
+ Alt-Enter inserts a blank line.
75
+
76
+ ### Paste and quote
77
+
78
+ `MarkdownEditor` turns a formatted paste into Markdown, GitHub style: links, emphasis, lists,
79
+ tables and code survive, and a paste that is one line keeps only its inline formatting, so a copied
80
+ heading dropped into a sentence stays in it. Code copied out of an editor (VS Code's clipboard
81
+ type, or HTML that is all preformatted) goes in as plain text, with a fenced block on offer. After
82
+ a multi-line or formatted paste a toolbar under it switches the pasted range between Plain text
83
+ and Markdown and toggles Quote, one undo step per switch.
84
+
85
+ | Key | Does |
86
+ |---|---|
87
+ | Mod-V | Paste; formatting becomes Markdown |
88
+ | Mod-Shift-V | Paste as plain text |
89
+ | Mod-Alt-V | Paste as a quote |
90
+ | Mod-Alt-Shift-V | Paste plain text as a quote |
91
+ | Mod-. | Into the paste toolbar, right after a paste |
92
+ | Mod-Shift-. | Quote or unquote the selected lines, under every cursor |
93
+
94
+ No browser raises a paste event for Mod-Alt-V, so those keys read the clipboard themselves, and
95
+ say so in a toast when the browser refuses. A paste that beats the converter's chunk goes in as
96
+ plain text. `onPaste` sees every paste first; one it takes as files (`preventDefault`) is left
97
+ alone.
98
+
99
+ `EditorShortcutsHelp` is a keyboard button listing these keys, for a toolbar beside the editor,
100
+ with Apple's ⌘ ⌥ ⇧ on Apple platforms. Its list portals into the button's own subtree, so a box
101
+ that folds when focus leaves it stays open while the list is.
102
+
103
+ For an editor of your own, `pasteAs()` from `extensions/paste-as` is the toolbar and keys;
104
+ call its `handlePaste(view, event)` from your paste handler, and `prefetchPasteConverter()` once
105
+ the editor is up. `toggleQuote` and `blockquote` are in `extensions/quote-toggle`.
106
+
107
+ ### Completion
108
+
109
+ ```tsx
110
+ import type { CompletionSource } from "@codemirror/autocomplete";
111
+ import { EditorView } from "@codemirror/view";
112
+ import { completionWith } from "@orz/ui-codemirror/extensions/completion";
113
+ import { applyWithSpace } from "@orz/ui-codemirror/extensions/completion-space";
114
+
115
+ const mentions: CompletionSource = (context) => {
116
+ const match = context.matchBefore(/@\w*$/);
117
+ if (match === null) return null;
118
+ return {
119
+ from: match.from,
120
+ options: [{ label: "@alice", type: "mention", apply: applyWithSpace("@alice") }],
121
+ };
122
+ };
123
+
124
+ // Icons for your own completion types.
125
+ const icons = EditorView.theme({
126
+ ".cm-completionIcon-mention::after": { content: "'@'", color: "var(--primary)" },
127
+ });
128
+
129
+ // Build this once (module scope or useMemo): a new identity reconfigures the editor and closes
130
+ // the open panel.
131
+ const extensions = [completionWith([mentions]), icons];
132
+
133
+ <MarkdownEditor extensions={extensions} />;
134
+ ```
135
+
136
+ Pass every source through one `completionWith` call. It installs the only `autocompletion()` for
137
+ the editor, binds Tab only while a panel is open, adds the `<details>` tag template, and keeps the
138
+ space after an accepted row out of the way of punctuation typed next.
139
+
140
+ ### Slash commands
141
+
142
+ The command grammar lives in `@orz/query`, a dependency of this package; these extensions are the
143
+ panel and the highlight on top of it.
144
+
145
+ ```tsx
146
+ import {
147
+ createCommandRegistry,
148
+ parseCommandLines,
149
+ type ValueProviders,
150
+ } from "@orz/query";
151
+ import {
152
+ commandCompletionSource,
153
+ commandDecoration,
154
+ } from "@orz/ui-codemirror/extensions/slash-commands";
155
+
156
+ type Action =
157
+ | { type: "close" }
158
+ | { type: "label"; name: string }
159
+ | { type: "move"; status: string; assignee?: string };
160
+
161
+ const registry = createCommandRegistry<Action>([
162
+ {
163
+ name: "close",
164
+ detail: "→ Done",
165
+ args: [],
166
+ summarize: () => "close",
167
+ compile: () => ({ type: "close" }),
168
+ },
169
+ {
170
+ name: "label",
171
+ detail: "add a label",
172
+ args: [{ name: "label", value: { kind: "named", source: "label" }, rest: true }],
173
+ summarize: ([name]) => `label ${name}`,
174
+ compile: ([name]) => ({ type: "label", name: String(name) }),
175
+ },
176
+ {
177
+ name: "move",
178
+ detail: "change status",
179
+ args: [
180
+ { name: "status", value: { kind: "named", source: "status" } },
181
+ { name: "assignee", value: { kind: "named", source: "member" }, optional: true },
182
+ ],
183
+ summarize: ([status]) => `move to ${status}`,
184
+ compile: ([status, assignee]) => ({
185
+ type: "move",
186
+ status: String(status),
187
+ ...(assignee === undefined ? {} : { assignee: String(assignee) }),
188
+ }),
189
+ },
190
+ ]);
191
+
192
+ // Snapshot lists, or `{ search: (prefix, signal) => … }` for a list too long to load.
193
+ let providers: ValueProviders = {
194
+ label: { options: [{ value: "bug" }, { value: "docs" }] },
195
+ status: { options: [{ value: "Todo" }, { value: "In Progress" }] },
196
+ member: { options: undefined }, // still loading
197
+ };
198
+
199
+ // Getters, so a registry or providers built from data that arrives later need no reconfigure.
200
+ const getRegistry = () => registry;
201
+ const getProviders = () => providers;
202
+ const extensions = [
203
+ completionWith([commandCompletionSource(getRegistry, getProviders)]),
204
+ commandDecoration(getRegistry, getProviders),
205
+ ];
206
+
207
+ <MarkdownEditor
208
+ extensions={extensions}
209
+ onSubmit={(value) => {
210
+ const { body, commands, invalid } = parseCommandLines(value, registry, providers);
211
+ // body: the text with every command line removed; commands: Action[].
212
+ }}
213
+ />;
214
+ ```
215
+
216
+ A command is a whole line of the document. Lines inside fenced code are never commands, an unknown
217
+ `/word` stays prose, and a recognized line whose argument names nothing lands in `invalid` with a
218
+ reason. Arguments are shell words (`/move 'In Progress' alice`); see `@orz/query` for rest and
219
+ `multiple` arguments.
220
+
221
+ The panel offers the current argument's values, inserts a space and reopens for the next argument,
222
+ and leads with an `↵ end of command` row while every remaining argument is optional. A `search`
223
+ provider is asked per keystroke and the request is aborted when the next key arrives. The highlight
224
+ tints recognized lines, marks the name (`cm-command-name`) and each argument (`cm-command-arg`,
225
+ plus `-special` or `-invalid`), and redraws when the getters return a new registry or providers
226
+ object, so replace `providers` rather than mutating it.
227
+
228
+ ### Provenance
229
+
230
+ `CodeEditorHandle.userChanged()` reports whether the reader changed the document since mount.
231
+ Mark your own writes so they do not count. `view` here is the `EditorView` that `CodeEditor`'s
232
+ `onView` hands out, or the one a command or `ViewPlugin` of yours receives:
233
+
234
+ ```ts
235
+ import { PROGRAMMATIC_USER_EVENT } from "@orz/ui-codemirror/components/code-editor";
236
+
237
+ view.dispatch({ changes, userEvent: PROGRAMMATIC_USER_EVENT });
238
+ ```
239
+
240
+ The handle's `setValue` and `append` are not marked, so they count as the reader's changes.
241
+
242
+ ## Entry points
243
+
244
+ | Import | Contents |
245
+ |---|---|
246
+ | `@orz/ui-codemirror/components/code-editor` | `CodeEditor`, `CodeEditorHandle`, `CodeEditorProps`, `PROGRAMMATIC_USER_EVENT` |
247
+ | `@orz/ui-codemirror/components/editor-shortcuts-help` | `EditorShortcutsHelp` |
248
+ | `@orz/ui-codemirror/components/markdown-editor` | `MarkdownEditor`, `MarkdownEditorHandle`, `MarkdownEditorProps`, `FileClipboardEvent`, `FileDragEvent`, `markdownLanguage`, `markdownHighlightStyle` |
249
+ | `@orz/ui-codemirror/extensions/<name>` | `code-context`, `completion`, `completion-space`, `json`, `paste-as`, `quote-toggle`, `slash-commands`, `tag-completion`, `trim-trailing-space` |
250
+ | `@orz/ui-codemirror/styles.css` | `@source "./dist"` |
251
+
252
+ There is no root entry, so a markdown-only app never reaches `@lezer/json` through an index file.
@@ -0,0 +1,94 @@
1
+ import { type HighlightStyle } from "@codemirror/language";
2
+ import { type Extension } from "@codemirror/state";
3
+ import type { KeyBinding } from "@codemirror/view";
4
+ import { EditorView } from "@codemirror/view";
5
+ /**
6
+ * The user event that marks a write as the program's rather than the
7
+ * reader's. Tag a dispatch with
8
+ * `Transaction.userEvent.of(PROGRAMMATIC_USER_EVENT)` (or
9
+ * `userEvent: PROGRAMMATIC_USER_EVENT`) and it leaves `userChanged()` alone.
10
+ */
11
+ export declare const PROGRAMMATIC_USER_EVENT = "orz.programmatic";
12
+ export type CodeEditorHandle = {
13
+ getValue: () => string;
14
+ /**
15
+ * CAUTION: this dispatch is NOT annotated with `PROGRAMMATIC_USER_EVENT`,
16
+ * so it counts as a user change. An owner that judges provenance through
17
+ * `userChanged()` would read a `setValue` as the reader's edit; annotate
18
+ * the write before using it under such an owner.
19
+ */
20
+ setValue: (value: string) => void;
21
+ /**
22
+ * Add to the end of the document, caret after it and scrolled into view.
23
+ *
24
+ * Unmarked, like `setValue`, but here on purpose: the text arrives because
25
+ * the reader asked for it, so `userChanged()` reading true afterwards is
26
+ * the right answer.
27
+ */
28
+ append: (text: string) => void;
29
+ focus: () => void;
30
+ /**
31
+ * True when the document changed through a CM user event (typing, paste,
32
+ * drop) since mount — provenance, not byte-equality: writes tagged
33
+ * `PROGRAMMATIC_USER_EVENT` do not count.
34
+ */
35
+ userChanged: () => boolean;
36
+ /**
37
+ * Close an open completion panel, reporting whether there was one. An
38
+ * editor inside a dismissable layer needs this because that layer listens
39
+ * for Escape on the document in the capture phase and calls
40
+ * `preventDefault` — after which CodeMirror's own handlers decline the
41
+ * event, so the layer's owner has to do the closing.
42
+ */
43
+ dismissCompletion: () => boolean;
44
+ };
45
+ export type CodeEditorProps = {
46
+ /** Read once, at mount: the editor owns its document from then on. */
47
+ initialValue?: string;
48
+ placeholder?: string;
49
+ ariaLabel?: string;
50
+ autoFocus?: boolean;
51
+ readOnly?: boolean;
52
+ /**
53
+ * The caller owns both the live value and its dirty baseline. This editor
54
+ * then acts only as a view and does not register a duplicate dirty source.
55
+ */
56
+ ownerManagedDirty?: boolean;
57
+ /**
58
+ * Caller's own key bindings, placed ahead of the base keymap. The order is
59
+ * load-bearing: defaultKeymap binds Mod-Enter to insertBlankLine, so a
60
+ * submit binding listed after it never fires.
61
+ */
62
+ keymap?: KeyBinding[];
63
+ /** Language support, e.g. a markdown Language or a StreamLanguage. */
64
+ language?: Extension;
65
+ /** Token-level colouring to pair with `language`. */
66
+ highlightStyle?: HighlightStyle;
67
+ /**
68
+ * Fires on every document change. Only for cheap derived state (an empty
69
+ * draft disabling its submit button) — the document itself lives in the
70
+ * editor, not in React.
71
+ */
72
+ onChange?: (value: string) => void;
73
+ /** Extra CodeMirror extensions, appended last so they can override. */
74
+ extensions?: Extension;
75
+ /**
76
+ * data-slot of the host element. Tests reach a specific editor kind
77
+ * through it; the default names the generic shell.
78
+ */
79
+ slot?: string;
80
+ /** Height is the caller's business: pass the min-height/max-height here. */
81
+ className?: string;
82
+ /** The underlying EditorView, once mounted — for callers that drive the
83
+ * cursor or the document programmatically. */
84
+ onView?: (view: EditorView | null) => void;
85
+ ref?: React.Ref<CodeEditorHandle>;
86
+ };
87
+ /**
88
+ * The common CodeMirror shell for every editor built on it: mounting, the
89
+ * placeholder/readOnly/extensions compartments, the theme, dirty tracking,
90
+ * and the base key bindings. Callers bring their own language and, ahead of
91
+ * the base keymap, their own semantics — submit keys, line editing, snippets.
92
+ */
93
+ export declare function CodeEditor({ initialValue, placeholder, ariaLabel, autoFocus, readOnly, ownerManagedDirty, keymap: callerKeymap, language, highlightStyle, onChange, extensions, slot, onView, className, ref, }: CodeEditorProps): import("react").JSX.Element;
94
+ //# sourceMappingURL=code-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.d.ts","sourceRoot":"","sources":["../../src/components/code-editor.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAEL,UAAU,EAGX,MAAM,kBAAkB,CAAC;AAQ1B;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,qBAAqB,CAAC;AAE1D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,MAAM,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;;;OAIG;IACH,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,OAAO,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,qDAAqD;IACrD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,uEAAuE;IACvE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;kDAC8C;IAC9C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;CACnC,CAAC;AAiEF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,YAAiB,EACjB,WAAgB,EAChB,SAAS,EACT,SAAiB,EACjB,QAAgB,EAChB,iBAAyB,EACzB,MAAM,EAAE,YAAY,EACpB,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,MAAM,EACN,SAAS,EACT,GAAG,GACJ,EAAE,eAAe,+BAgLjB"}
@@ -0,0 +1,243 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { closeCompletion, completionStatus } from "@codemirror/autocomplete";
3
+ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
4
+ import { syntaxHighlighting } from "@codemirror/language";
5
+ import { Compartment, EditorState } from "@codemirror/state";
6
+ import { drawSelection, EditorView, keymap, placeholder as placeholderExt, } from "@codemirror/view";
7
+ import { cn } from "@orz/ui";
8
+ import { useDirtySource } from "@orz/ui/lib/unsaved-guard";
9
+ import { useEffect, useImperativeHandle, useRef } from "react";
10
+ import { compositionSafeMeasure } from "../extensions/composition-safe-measure.js";
11
+ import { parkedTooltip } from "../extensions/parked-tooltip.js";
12
+ import { tooltipSpace } from "../extensions/tooltip-space.js";
13
+ /**
14
+ * The user event that marks a write as the program's rather than the
15
+ * reader's. Tag a dispatch with
16
+ * `Transaction.userEvent.of(PROGRAMMATIC_USER_EVENT)` (or
17
+ * `userEvent: PROGRAMMATIC_USER_EVENT`) and it leaves `userChanged()` alone.
18
+ */
19
+ export const PROGRAMMATIC_USER_EVENT = "orz.programmatic";
20
+ /** Mirrors `@orz/ui`'s `Textarea` look, with every colour coming from a variable. */
21
+ const editorTheme = EditorView.theme({
22
+ "&": {
23
+ color: "var(--foreground)",
24
+ backgroundColor: "transparent",
25
+ fontFamily: "var(--font-sans)",
26
+ // style-mod cannot express a media query against the generated theme
27
+ // class, so the breakpoint lives in the wrapper's Tailwind classes and
28
+ // the editor simply inherits it.
29
+ fontSize: "inherit",
30
+ // Lets the wrapper's max-height clamp the editor so .cm-scroller,
31
+ // not the page, does the scrolling.
32
+ flex: "1 1 auto",
33
+ minHeight: "0",
34
+ borderRadius: "inherit",
35
+ },
36
+ "&.cm-focused": { outline: "none" },
37
+ /**
38
+ * alignSelf and minHeight here, with flexGrow on .cm-scroller below, are what
39
+ * makes the blank area under a short document part of the editor rather than
40
+ * a dead shell. The base theme already means to do that, with
41
+ * `.cm-scroller { height: 100% }` and `.cm-content { min-height: 100% }` —
42
+ * but callers give this component a min-height, never a height, so the
43
+ * containing block is never definite, both percentages silently resolve to
44
+ * `auto`, and the contenteditable stays as short as its text. None of the
45
+ * three below depends on percentage resolution: the scroller takes the
46
+ * leftover height through flex, align-self overrides the base theme's
47
+ * `align-items: flex-start !important` so the content stretches down the
48
+ * cross axis, and min-content floors that stretch — a stretched box is
49
+ * clamped to the flex line, which costs a document taller than the caller's
50
+ * max-height its bottom padding, with no way to scroll to it.
51
+ */
52
+ ".cm-content": {
53
+ padding: "0.5rem 0.625rem",
54
+ lineHeight: "1.5",
55
+ caretColor: "var(--foreground)",
56
+ alignSelf: "stretch",
57
+ minHeight: "min-content",
58
+ },
59
+ // CodeMirror's own base theme puts monospace here; this shell is for prose
60
+ // first, so it inherits the host's font instead.
61
+ ".cm-scroller": {
62
+ overflow: "auto",
63
+ fontFamily: "inherit",
64
+ fontSize: "inherit",
65
+ lineHeight: "inherit",
66
+ // Not `flex: 1`, which would zero the basis; the other two components of
67
+ // the shorthand are already at their defaults.
68
+ flexGrow: "1",
69
+ // The wrapper cannot clip to its rounded corners — that would clip an
70
+ // absolute completion popup too — so the scroller clips itself.
71
+ borderRadius: "inherit",
72
+ },
73
+ ".cm-line": { padding: "0" },
74
+ ".cm-cursor, .cm-dropCursor": { borderLeftColor: "var(--foreground)" },
75
+ "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection": {
76
+ backgroundColor: "color-mix(in oklab, var(--primary) 22%, transparent)",
77
+ },
78
+ ".cm-placeholder": { color: "var(--muted-foreground)" },
79
+ ".cm-gutters": { display: "none" },
80
+ });
81
+ /**
82
+ * The common CodeMirror shell for every editor built on it: mounting, the
83
+ * placeholder/readOnly/extensions compartments, the theme, dirty tracking,
84
+ * and the base key bindings. Callers bring their own language and, ahead of
85
+ * the base keymap, their own semantics — submit keys, line editing, snippets.
86
+ */
87
+ export function CodeEditor({ initialValue = "", placeholder = "", ariaLabel, autoFocus = false, readOnly = false, ownerManagedDirty = false, keymap: callerKeymap, language, highlightStyle, onChange, extensions, slot, onView, className, ref, }) {
88
+ const host = useRef(null);
89
+ const view = useRef(null);
90
+ // Compared against, never written: a ref outlives the mount effect below,
91
+ // so the box stays "edited from what it was opened with" even after
92
+ // Suspense rebuilds the document — which is also why the baseline cannot
93
+ // come from the EditorView, whose doc may already be the rebuilt one.
94
+ const baseline = useRef(initialValue);
95
+ const latestInitialValue = useRef(initialValue);
96
+ latestInitialValue.current = initialValue;
97
+ // Keymap and DOM handlers are built once but must always call today's
98
+ // props, not the ones captured at mount.
99
+ const handlers = useRef({ onChange, ownerManagedDirty });
100
+ handlers.current = { onChange, ownerManagedDirty };
101
+ // One compartment per mutable extension, so a prop change reconfigures
102
+ // that slice instead of rebuilding the view (and losing undo history).
103
+ const placeholderSlot = useRef(new Compartment()).current;
104
+ const readOnlySlot = useRef(new Compartment()).current;
105
+ const extensionsSlot = useRef(new Compartment()).current;
106
+ // Read through the ref rather than the closure: the EditorView is a
107
+ // different instance after a rebuild, and only the baseline has to survive
108
+ // that. Whitespace-only differences are not worth a confirmation.
109
+ useDirtySource(() => !handlers.current.ownerManagedDirty &&
110
+ !readOnly &&
111
+ (view.current?.state.doc.toString() ?? baseline.current).trim() !==
112
+ baseline.current.trim());
113
+ const userChangedRef = useRef(false);
114
+ // Every method reads only refs, so an empty list costs nothing and keeps
115
+ // the handle's identity stable across renders, which callers rely on.
116
+ useImperativeHandle(ref, () => ({
117
+ getValue: () => view.current?.state.doc.toString() ?? "",
118
+ // CAUTION (mirrors CodeEditorHandle.setValue): unannotated, so it counts
119
+ // as a user change.
120
+ setValue: (value) => {
121
+ const current = view.current;
122
+ if (!current)
123
+ return;
124
+ current.dispatch({
125
+ changes: { from: 0, to: current.state.doc.length, insert: value },
126
+ });
127
+ },
128
+ append: (text) => {
129
+ const current = view.current;
130
+ if (!current)
131
+ return;
132
+ const at = current.state.doc.length;
133
+ current.dispatch({
134
+ changes: { from: at, insert: text },
135
+ selection: { anchor: at + text.length },
136
+ scrollIntoView: true,
137
+ });
138
+ },
139
+ focus: () => view.current?.focus(),
140
+ // Provenance as in the handle type: user-event transactions mark the
141
+ // document as reader-touched; tagged programmatic writes never do.
142
+ userChanged: () => userChangedRef.current,
143
+ dismissCompletion: () => {
144
+ const current = view.current;
145
+ // "pending" is a query in flight with nothing on screen yet, which is
146
+ // not something the reader can have meant to dismiss.
147
+ if (current === null || completionStatus(current.state) !== "active") {
148
+ return false;
149
+ }
150
+ closeCompletion(current);
151
+ return true;
152
+ },
153
+ }), []);
154
+ // Mount once. initialValue/ariaLabel changes do not rebuild the view —
155
+ // remount with a new `key` if a caller ever needs that.
156
+ // biome-ignore lint/correctness/useExhaustiveDependencies: mount-only by design; every mutable prop is reconfigured through the compartments below.
157
+ useEffect(() => {
158
+ const parent = host.current;
159
+ if (parent === null)
160
+ return;
161
+ const instance = new EditorView({
162
+ parent,
163
+ state: EditorState.create({
164
+ doc: latestInitialValue.current,
165
+ extensions: [
166
+ history(),
167
+ drawSelection(),
168
+ compositionSafeMeasure,
169
+ // Without this CM silently collapses every extra range, which is
170
+ // what makes Mod-D look like a no-op.
171
+ EditorState.allowMultipleSelections.of(true),
172
+ EditorView.lineWrapping,
173
+ keymap.of([
174
+ ...(callerKeymap ?? []),
175
+ ...defaultKeymap,
176
+ ...historyKeymap,
177
+ ]),
178
+ ...(language ? [language] : []),
179
+ ...(highlightStyle ? [syntaxHighlighting(highlightStyle)] : []),
180
+ editorTheme,
181
+ parkedTooltip,
182
+ tooltipSpace,
183
+ EditorView.contentAttributes.of({
184
+ // CodeMirror's contenteditable opts out of the niceties a
185
+ // <textarea> gets for free.
186
+ spellcheck: "true",
187
+ autocapitalize: "sentences",
188
+ autocorrect: "on",
189
+ ...(ariaLabel === undefined ? {} : { "aria-label": ariaLabel }),
190
+ }),
191
+ EditorView.updateListener.of((update) => {
192
+ if (update.docChanged) {
193
+ // Default to "the reader did this": every UI path to a doc
194
+ // change (typing, paste, drop, delete, and the whole move.*
195
+ // family — defaultKeymap's line moves and transposes annotate
196
+ // their transactions) arrives as a user event, and a whitelist
197
+ // loses a family every time CM adds one. The exception is a
198
+ // write the caller marked with PROGRAMMATIC_USER_EVENT.
199
+ // Mount-time \r\n normalization rides in EditorState.create —
200
+ // no transaction, never judged.
201
+ const ours = update.transactions.every((t) => t.isUserEvent(PROGRAMMATIC_USER_EVENT));
202
+ if (!ours)
203
+ userChangedRef.current = true;
204
+ handlers.current.onChange?.(update.state.doc.toString());
205
+ }
206
+ }),
207
+ placeholderSlot.of(placeholderExt(placeholder)),
208
+ readOnlySlot.of(EditorState.readOnly.of(readOnly)),
209
+ extensionsSlot.of(extensions ?? []),
210
+ ],
211
+ }),
212
+ });
213
+ view.current = instance;
214
+ onView?.(instance);
215
+ if (autoFocus)
216
+ instance.focus();
217
+ return () => {
218
+ instance.destroy();
219
+ view.current = null;
220
+ onView?.(null);
221
+ };
222
+ }, []);
223
+ useEffect(() => {
224
+ view.current?.dispatch({
225
+ effects: placeholderSlot.reconfigure(placeholderExt(placeholder)),
226
+ });
227
+ }, [placeholder, placeholderSlot]);
228
+ useEffect(() => {
229
+ view.current?.dispatch({
230
+ effects: readOnlySlot.reconfigure(EditorState.readOnly.of(readOnly)),
231
+ });
232
+ }, [readOnly, readOnlySlot]);
233
+ useEffect(() => {
234
+ view.current?.dispatch({
235
+ effects: extensionsSlot.reconfigure(extensions ?? []),
236
+ });
237
+ }, [extensions, extensionsSlot]);
238
+ return (_jsx("div", { ref: host, "data-slot": slot ?? "code-editor", "data-read-only": readOnly ? "true" : undefined, className: cn(
239
+ // text-base below md is not cosmetic: iOS auto-zooms a focused field
240
+ // under 16px. Same rule `@orz/ui`'s Textarea follows.
241
+ "flex w-full flex-col rounded-lg border border-input bg-transparent text-base transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 md:text-sm dark:bg-input/30", readOnly && "opacity-50", className) }));
242
+ }
243
+ //# sourceMappingURL=code-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.js","sourceRoot":"","sources":["../../src/components/code-editor.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `MarkdownEditor`'s paste and quote keys, none of which leave a trace on
3
+ * screen until they are used, behind a keyboard button for a toolbar beside
4
+ * the editor. Apple platforms get ⌘ ⌥ ⇧.
5
+ */
6
+ export declare function EditorShortcutsHelp(): import("react").JSX.Element;
7
+ //# sourceMappingURL=editor-shortcuts-help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-shortcuts-help.d.ts","sourceRoot":"","sources":["../../src/components/editor-shortcuts-help.tsx"],"names":[],"mappings":"AAqBA;;;;GAIG;AACH,wBAAgB,mBAAmB,gCAkDlC"}