@coldsmirk/inkstone-react 0.10.1 → 0.12.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 CHANGED
@@ -11,7 +11,7 @@ pnpm add @coldsmirk/inkstone-react monaco-editor react \
11
11
  @codemirror/state @codemirror/view @codemirror/language @codemirror/autocomplete @codemirror/commands @codemirror/search
12
12
  ```
13
13
 
14
- `monaco-editor`, `react`, and the `@codemirror/*` packages are **peer dependencies** — your app owns the single copy of each (`@codemirror/state` breaks at runtime if two copies load). Node.js >= 24 for build / SSR hosts.
14
+ `monaco-editor`, `react`, and the `@codemirror/*` packages are **peer dependencies** — your app owns the single copy of each (`@codemirror/state` breaks at runtime if two copies load). The engine peers are **optional**: a `/codemirror`-only consumer installs just the `@codemirror/*` set, a `/monaco`-only consumer just `monaco-editor`; only `react` is always required (`@monaco-editor/react` still declares `monaco-editor` a required peer, so a CodeMirror-only install may print one unmet-peer warning — harmless, nothing Monaco is loaded). Node.js >= 24 for build / SSR hosts.
15
15
 
16
16
  Import from the engine-specific entry points below so a CodeMirror-only build never traverses or emits Monaco (and vice versa). The aggregate `@coldsmirk/inkstone-react` entry remains available for existing consumers; `@coldsmirk/inkstone-react/shiki` exposes the standalone highlighter hook.
17
17
 
@@ -0,0 +1,261 @@
1
+ import { n as useControlledReconcileSignal, t as useLatest } from "./use-latest-BxJHbqOv.js";
2
+ import { EditorState } from "@codemirror/state";
3
+ import { EditorView, lineNumbers } from "@codemirror/view";
4
+ import { ControlledEditorHost, ControlledMergeHost, codeMirrorLanguages, defaultThemeExtensions, diffChromeColor, documentExtensions, editorRoot, installDiffChrome, installDiffControlKeyboardSupport, loadLanguage, mergeTheme, minijinjaContextField, normalizeContext, normalizeContext as normalizeContext$1, searchPhrasesZhCn, searchPhrasesZhCn as searchPhrasesZhCn$1, setMinijinjaContext, setSqlSchema, sqlSchemaField, toCssSize } from "@coldsmirk/inkstone-codemirror";
5
+ import { useEffect, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ import { indentUnit } from "@codemirror/language";
8
+ //#region src/codemirror-diff-editor.tsx
9
+ function CodeMirrorDiffEditor({ original, modified, onChange, language, readOnly = false, revertControls = false, collapseUnchanged = false, lineWrapping = false, showLineNumbers = true, extensions, dynamicExtensions, dark = false, height, width, fontSize = 14, lineHeight = 1.6, className, originalAriaLabel = "Original content", modifiedAriaLabel = "Modified content", onMount }) {
10
+ const containerRef = useRef(null);
11
+ const hostRef = useRef(null);
12
+ const modifiedTransitionRef = useRef(false);
13
+ const [reconcileRevision, requestControlledReconcile] = useControlledReconcileSignal();
14
+ const onChangeRef = useLatest(onChange);
15
+ const onMountRef = useLatest(onMount);
16
+ const [loadedLanguage, setLoadedLanguage] = useState(null);
17
+ const languageExtension = useMemo(() => loadedLanguage && loadedLanguage.id === language ? [loadedLanguage.extension] : [], [language, loadedLanguage]);
18
+ const activeRevertControls = revertControls && !readOnly;
19
+ useEffect(() => {
20
+ if (!language) return;
21
+ let cancelled = false;
22
+ loadLanguage(language).then((extension) => {
23
+ if (!cancelled) setLoadedLanguage({
24
+ id: language,
25
+ extension
26
+ });
27
+ }).catch((error) => {
28
+ console.error("inkstone: failed to load CodeMirror language", language, error);
29
+ if (!cancelled) setLoadedLanguage(null);
30
+ });
31
+ return () => {
32
+ cancelled = true;
33
+ };
34
+ }, [language]);
35
+ const dynamic = useMemo(() => [
36
+ ...defaultThemeExtensions(dark, void 0, void 0, fontSize, lineHeight),
37
+ mergeTheme(dark),
38
+ ...showLineNumbers ? [lineNumbers()] : [],
39
+ ...lineWrapping ? [EditorView.lineWrapping] : [],
40
+ ...readOnly ? [EditorState.readOnly.of(true), EditorView.editable.of(false)] : [],
41
+ ...languageExtension,
42
+ ...dynamicExtensions ?? []
43
+ ], [
44
+ dark,
45
+ fontSize,
46
+ lineHeight,
47
+ showLineNumbers,
48
+ lineWrapping,
49
+ readOnly,
50
+ languageExtension,
51
+ dynamicExtensions
52
+ ]);
53
+ useEffect(() => {
54
+ const container = containerRef.current;
55
+ if (!container) return;
56
+ const root = editorRoot(container);
57
+ installDiffChrome(root);
58
+ const host = new ControlledMergeHost({
59
+ parent: container,
60
+ root,
61
+ original,
62
+ modified,
63
+ onModifiedChange: (next) => {
64
+ if (modifiedTransitionRef.current) return;
65
+ onChangeRef.current?.(next);
66
+ requestControlledReconcile();
67
+ },
68
+ extensions: [
69
+ minijinjaContextField,
70
+ sqlSchemaField,
71
+ ...documentExtensions(),
72
+ ...extensions ?? []
73
+ ],
74
+ originalExtensions: [
75
+ EditorState.readOnly.of(true),
76
+ EditorView.editable.of(false),
77
+ EditorView.contentAttributes.of({ "aria-label": originalAriaLabel })
78
+ ],
79
+ modifiedExtensions: [EditorView.contentAttributes.of({ "aria-label": modifiedAriaLabel })],
80
+ dynamicExtensions: dynamic,
81
+ revertControls: activeRevertControls ? "a-to-b" : void 0,
82
+ collapseUnchanged: collapseUnchanged ? {} : void 0
83
+ });
84
+ hostRef.current = host;
85
+ const removeDiffControlKeyboardSupport = installDiffControlKeyboardSupport(container);
86
+ onMountRef.current?.(host.view);
87
+ return () => {
88
+ removeDiffControlKeyboardSupport();
89
+ host.destroy();
90
+ hostRef.current = null;
91
+ };
92
+ }, []);
93
+ useInsertionEffect(() => {
94
+ const host = hostRef.current;
95
+ modifiedTransitionRef.current = host !== null && host.view.b.state.doc.toString() !== modified;
96
+ }, [modified, reconcileRevision]);
97
+ useLayoutEffect(() => {
98
+ hostRef.current?.setOriginal(original);
99
+ }, [original]);
100
+ useLayoutEffect(() => {
101
+ try {
102
+ hostRef.current?.setModified(modified);
103
+ } finally {
104
+ modifiedTransitionRef.current = false;
105
+ }
106
+ }, [modified, reconcileRevision]);
107
+ useEffect(() => {
108
+ hostRef.current?.reconfigure(dynamic);
109
+ }, [dynamic]);
110
+ useEffect(() => {
111
+ hostRef.current?.view.reconfigure({
112
+ revertControls: activeRevertControls ? "a-to-b" : void 0,
113
+ collapseUnchanged: collapseUnchanged ? {} : void 0
114
+ });
115
+ }, [activeRevertControls, collapseUnchanged]);
116
+ return /* @__PURE__ */ jsx("div", {
117
+ ref: containerRef,
118
+ className,
119
+ "data-inkstone-diff": "",
120
+ style: {
121
+ height: toCssSize(height),
122
+ width: toCssSize(width),
123
+ color: diffChromeColor(dark)
124
+ }
125
+ });
126
+ }
127
+ //#endregion
128
+ //#region src/codemirror-editor.tsx
129
+ function CodeMirrorEditor({ value, onChange, language, context, sqlSchema, placeholder, lineWrapping = false, showLineNumbers = false, folding = false, spellcheck = false, search = false, extensions, dynamicExtensions, dark = false, locale = "en", height, width, fontSize = 14, lineHeight = 1.6, tabSize, className, onMount, onFocus, onBlur, ariaLabel = "Editor content" }) {
130
+ const containerRef = useRef(null);
131
+ const hostRef = useRef(null);
132
+ const valueTransitionRef = useRef(false);
133
+ const [reconcileRevision, requestControlledReconcile] = useControlledReconcileSignal();
134
+ const onChangeRef = useLatest(onChange);
135
+ const onMountRef = useLatest(onMount);
136
+ const onFocusRef = useLatest(onFocus);
137
+ const onBlurRef = useLatest(onBlur);
138
+ const lastContextKeyRef = useRef(null);
139
+ const lastSqlSchemaKeyRef = useRef(null);
140
+ const [loadedLanguage, setLoadedLanguage] = useState(null);
141
+ const languageExtension = useMemo(() => loadedLanguage && loadedLanguage.id === language ? [loadedLanguage.extension] : [], [language, loadedLanguage]);
142
+ useEffect(() => {
143
+ if (!language) return;
144
+ let cancelled = false;
145
+ loadLanguage(language).then((extension) => {
146
+ if (!cancelled) setLoadedLanguage({
147
+ id: language,
148
+ extension
149
+ });
150
+ }).catch((error) => {
151
+ console.error("inkstone: failed to load CodeMirror language", language, error);
152
+ if (!cancelled) setLoadedLanguage(null);
153
+ });
154
+ return () => {
155
+ cancelled = true;
156
+ };
157
+ }, [language]);
158
+ const dynamic = useMemo(() => [
159
+ ...defaultThemeExtensions(dark, height, width, fontSize, lineHeight),
160
+ ...tabSize === void 0 ? [] : [EditorState.tabSize.of(tabSize), indentUnit.of(" ".repeat(tabSize))],
161
+ ...locale === "zh-cn" ? [searchPhrasesZhCn] : [],
162
+ ...languageExtension,
163
+ EditorView.contentAttributes.of({ "aria-label": ariaLabel }),
164
+ ...dynamicExtensions ?? []
165
+ ], [
166
+ dark,
167
+ height,
168
+ width,
169
+ fontSize,
170
+ lineHeight,
171
+ tabSize,
172
+ locale,
173
+ languageExtension,
174
+ ariaLabel,
175
+ dynamicExtensions
176
+ ]);
177
+ const contextSchema = useMemo(() => context ? normalizeContext(context) : null, [context]);
178
+ useEffect(() => {
179
+ const container = containerRef.current;
180
+ if (!container) return;
181
+ const host = new ControlledEditorHost({
182
+ parent: container,
183
+ doc: value,
184
+ onChange: (next) => {
185
+ if (valueTransitionRef.current) return;
186
+ onChangeRef.current?.(next);
187
+ requestControlledReconcile();
188
+ },
189
+ extensions: [
190
+ minijinjaContextField,
191
+ sqlSchemaField,
192
+ ...documentExtensions({
193
+ placeholder,
194
+ lineWrapping,
195
+ showLineNumbers,
196
+ folding,
197
+ spellcheck,
198
+ search
199
+ }),
200
+ EditorView.updateListener.of((update) => {
201
+ if (update.focusChanged) if (update.view.hasFocus) onFocusRef.current?.();
202
+ else onBlurRef.current?.();
203
+ }),
204
+ ...extensions ?? []
205
+ ],
206
+ dynamicExtensions: dynamic
207
+ });
208
+ hostRef.current = host;
209
+ onMountRef.current?.(host.view);
210
+ return () => {
211
+ host.destroy();
212
+ hostRef.current = null;
213
+ };
214
+ }, []);
215
+ useInsertionEffect(() => {
216
+ const host = hostRef.current;
217
+ valueTransitionRef.current = host !== null && host.view.state.doc.toString() !== value;
218
+ }, [reconcileRevision, value]);
219
+ useLayoutEffect(() => {
220
+ try {
221
+ hostRef.current?.setValue(value);
222
+ } finally {
223
+ valueTransitionRef.current = false;
224
+ }
225
+ }, [reconcileRevision, value]);
226
+ useEffect(() => {
227
+ hostRef.current?.reconfigure(dynamic);
228
+ }, [dynamic]);
229
+ useEffect(() => {
230
+ const host = hostRef.current;
231
+ if (!host) return;
232
+ const key = contextSchema ? JSON.stringify(contextSchema) : "";
233
+ const last = lastContextKeyRef.current;
234
+ if (last?.host === host && last.key === key) return;
235
+ lastContextKeyRef.current = {
236
+ host,
237
+ key
238
+ };
239
+ if (key === "" && last?.host !== host) return;
240
+ host.view.dispatch({ effects: setMinijinjaContext.of(contextSchema) });
241
+ }, [contextSchema]);
242
+ useEffect(() => {
243
+ const host = hostRef.current;
244
+ if (!host) return;
245
+ const key = sqlSchema ? JSON.stringify(sqlSchema) : "";
246
+ const last = lastSqlSchemaKeyRef.current;
247
+ if (last?.host === host && last.key === key) return;
248
+ lastSqlSchemaKeyRef.current = {
249
+ host,
250
+ key
251
+ };
252
+ if (key === "" && last?.host !== host) return;
253
+ host.view.dispatch({ effects: setSqlSchema.of(sqlSchema ?? null) });
254
+ }, [sqlSchema]);
255
+ return /* @__PURE__ */ jsx("div", {
256
+ ref: containerRef,
257
+ className
258
+ });
259
+ }
260
+ //#endregion
261
+ export { CodeMirrorDiffEditor as a, CodeMirrorEditor as i, normalizeContext$1 as n, searchPhrasesZhCn$1 as r, codeMirrorLanguages as t };
@@ -1,2 +1,2 @@
1
- import { a as CodeMirrorDiffEditor, i as CodeMirrorEditor, n as normalizeContext, r as searchPhrasesZhCn, t as codeMirrorLanguages } from "./codemirror-B27C5np1.js";
1
+ import { a as CodeMirrorDiffEditor, i as CodeMirrorEditor, n as normalizeContext, r as searchPhrasesZhCn, t as codeMirrorLanguages } from "./codemirror-Bvwl0fxH.js";
2
2
  export { CodeMirrorDiffEditor, CodeMirrorEditor, codeMirrorLanguages, normalizeContext, searchPhrasesZhCn };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { a as codeMirrorLanguages, c as CodeMirrorEditor, d as CodeMirrorDiffEditorProps, i as SqlSchema, l as CodeMirrorEditorProps, n as EditorContext, o as normalizeContext, r as MergeView, s as searchPhrasesZhCn, t as CodeMirrorLanguage, u as CodeMirrorDiffEditor } from "./codemirror-B3GlIVaX.js";
2
- import { a as MonacoEditorProps, i as MonacoEditor, n as monacoLanguages, o as MonacoDiffEditor, r as DEFAULT_MONACO_OPTIONS, s as MonacoDiffEditorProps, t as MonacoLanguage } from "./monaco-DXifh803.js";
2
+ import { a as MonacoEditorProps, i as MonacoEditor, n as MonacoLanguage, o as MonacoDiffEditor, r as monacoLanguages, s as MonacoDiffEditorProps, t as DEFAULT_MONACO_OPTIONS } from "./monaco-BoTcTdm-.js";
3
3
  import { n as useShikiHighlighter, t as ShikiState } from "./shiki-D-IjmhhO.js";
4
4
  export { CodeMirrorDiffEditor, type CodeMirrorDiffEditorProps, CodeMirrorEditor, type CodeMirrorEditorProps, type CodeMirrorLanguage, DEFAULT_MONACO_OPTIONS, type EditorContext, type MergeView, MonacoDiffEditor, type MonacoDiffEditorProps, MonacoEditor, type MonacoEditorProps, type MonacoLanguage, type ShikiState, type SqlSchema, codeMirrorLanguages, monacoLanguages, normalizeContext, searchPhrasesZhCn, useShikiHighlighter };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { a as CodeMirrorDiffEditor, i as CodeMirrorEditor, n as normalizeContext, r as searchPhrasesZhCn, t as codeMirrorLanguages } from "./codemirror-B27C5np1.js";
2
- import { i as MonacoEditor, n as MonacoDiffEditor, r as DEFAULT_MONACO_OPTIONS, t as monacoLanguages } from "./monaco-CP5BIXrw.js";
1
+ import { a as CodeMirrorDiffEditor, i as CodeMirrorEditor, n as normalizeContext, r as searchPhrasesZhCn, t as codeMirrorLanguages } from "./codemirror-Bvwl0fxH.js";
2
+ import { i as MonacoDiffEditor, n as monacoLanguages, r as MonacoEditor, t as DEFAULT_MONACO_OPTIONS } from "./monaco-IkYCUn1k.js";
3
3
  import { t as useShikiHighlighter } from "./use-shiki-highlighter-CzSQcX2f.js";
4
4
  import "./shiki.js";
5
5
  export { CodeMirrorDiffEditor, CodeMirrorEditor, DEFAULT_MONACO_OPTIONS, MonacoDiffEditor, MonacoEditor, codeMirrorLanguages, monacoLanguages, normalizeContext, searchPhrasesZhCn, useShikiHighlighter };
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
+ import { DEFAULT_MONACO_OPTIONS as DEFAULT_MONACO_OPTIONS$1, MonacoHostOptions, MonacoLanguage, MonacoLanguage as MonacoLanguage$1, MonacoUiLocale, monacoLanguages } from "@coldsmirk/inkstone-monaco";
2
3
  import { BeforeMount, DiffBeforeMount, DiffOnMount, OnMount } from "@monaco-editor/react";
3
- import { MonacoHostOptions, MonacoLanguage, MonacoLanguage as MonacoLanguage$1, MonacoUiLocale, monacoLanguages } from "@coldsmirk/inkstone-monaco";
4
4
  import { editor } from "monaco-editor";
5
5
 
6
6
  //#region src/monaco-diff-editor.d.ts
@@ -163,13 +163,6 @@ declare function MonacoDiffEditor({
163
163
  }: MonacoDiffEditorProps): ReactNode;
164
164
  //#endregion
165
165
  //#region src/monaco-editor.d.ts
166
- /**
167
- * Baseline construction options shared by every inkstone Monaco editor: no minimap, compact
168
- * code type, in-place layout tracking, widgets floated above clipping containers, completion
169
- * tuned for API-assisted editing, thin scrollbars, and an embedded-friendly find widget.
170
- * Spread these to extend rather than replace: `options={{ ...DEFAULT_MONACO_OPTIONS, wordWrap: "on" }}`.
171
- */
172
- declare const DEFAULT_MONACO_OPTIONS: editor.IStandaloneEditorConstructionOptions;
173
166
  interface MonacoEditorProps {
174
167
  /**
175
168
  * The controlled document text.
@@ -357,4 +350,4 @@ declare function MonacoEditor({
357
350
  onBlur
358
351
  }: MonacoEditorProps): ReactNode;
359
352
  //#endregion
360
- export { MonacoEditorProps as a, MonacoEditor as i, monacoLanguages as n, MonacoDiffEditor as o, DEFAULT_MONACO_OPTIONS as r, MonacoDiffEditorProps as s, MonacoLanguage$1 as t };
353
+ export { MonacoEditorProps as a, MonacoEditor as i, MonacoLanguage$1 as n, MonacoDiffEditor as o, monacoLanguages as r, MonacoDiffEditorProps as s, DEFAULT_MONACO_OPTIONS$1 as t };