@coldsmirk/inkstone-react 0.31.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{monaco-DbsfKRWu.js → monaco-CHi4aRfZ.js} +235 -114
- package/dist/{monaco-DkmB9ON5.d.ts → monaco-DTLlEEfk.d.ts} +187 -3
- package/dist/monaco.d.ts +2 -2
- package/dist/monaco.js +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @coldsmirk/inkstone-react
|
|
2
2
|
|
|
3
|
-
Drop-in React code editors with the integration chores solved once: a preconfigured Monaco **`<MonacoEditor>`** (offline workers, Shiki catppuccin themes, dark switching, opt-in zh-CN UI) and a controlled CodeMirror 6 **`<CodeMirrorEditor>`** (catppuccin themes, restyled chrome, MiniJinja support, sizing) — plus **`<MonacoDiffEditor>`** and **`<CodeMirrorDiffEditor>`**, the same assemblies around each engine's side-by-side diff view. Drop in a component; the plumbing is already wired.
|
|
3
|
+
Drop-in React code editors with the integration chores solved once: a preconfigured Monaco **`<MonacoEditor>`** (offline workers, Shiki catppuccin themes, dark switching, opt-in zh-CN UI) and a controlled CodeMirror 6 **`<CodeMirrorEditor>`** (catppuccin themes, restyled chrome, MiniJinja support, sizing) — plus **`<MonacoDiffEditor>`** and **`<CodeMirrorDiffEditor>`**, the same assemblies around each engine's side-by-side diff view, and **`<MonacoConsoleEditor>`**, the run-key surface inkstone's database editors stand on. Drop in a component; the plumbing is already wired.
|
|
4
4
|
|
|
5
5
|
Part of [inkstone](https://github.com/coldsmirk/inkstone) — this package composes [`@coldsmirk/inkstone-core`](https://www.npmjs.com/package/@coldsmirk/inkstone-core) (shared Shiki highlighter), [`@coldsmirk/inkstone-monaco`](https://www.npmjs.com/package/@coldsmirk/inkstone-monaco) (offline Monaco host), and [`@coldsmirk/inkstone-codemirror`](https://www.npmjs.com/package/@coldsmirk/inkstone-codemirror) (controlled CM6 host + MiniJinja). See the [repository README](https://github.com/coldsmirk/inkstone#readme) for the full guide; the highlights below are what you touch daily.
|
|
6
6
|
|
|
@@ -83,6 +83,30 @@ Each component keeps its engine's conventions from the single-editor sibling:
|
|
|
83
83
|
- **`<MonacoDiffEditor>`** — the `<MonacoEditor>` assembly (offline host, Shiki themes, `locale`, `loading` / `failure`, `hostOptions`) around `monaco.editor.createDiffEditor`. `options` takes raw `IDiffEditorConstructionOptions` over the same `DEFAULT_MONACO_OPTIONS` baseline — e.g. `renderSideBySide: false` for the inline view.
|
|
84
84
|
- **`<CodeMirrorDiffEditor>`** — `@codemirror/merge`'s `MergeView` behind `<CodeMirrorEditor>`'s create-once discipline, with the catppuccin flavors extended to the diff chrome (red deletions / green insertions, themed collapse bars and revert controls). `revertControls` adds per-chunk arrows copying the original chunk into the modified document (flows through `onChange`); `language`, `extensions`, and `dynamicExtensions` apply to both sides; `onMount` hands you the `MergeView`.
|
|
85
85
|
|
|
86
|
+
## `<MonacoConsoleEditor>`
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { MonacoConsoleEditor } from "@coldsmirk/inkstone-react/monaco";
|
|
90
|
+
|
|
91
|
+
<MonacoConsoleEditor
|
|
92
|
+
ariaLabel="Query"
|
|
93
|
+
language="kql"
|
|
94
|
+
namespace="app-kql"
|
|
95
|
+
extension="kql"
|
|
96
|
+
value={query}
|
|
97
|
+
onChange={setQuery}
|
|
98
|
+
onRun={caret => run(caret)}
|
|
99
|
+
context={{ assist }}
|
|
100
|
+
register={({ monaco, path, context }) => registerKql(monaco, path, { assist: () => context().assist })}
|
|
101
|
+
/>;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The surface of a console — a document an operator writes and runs: `<MonacoEditor>` with line numbers and a model path of its own, the run key (Ctrl/Cmd-Enter) handing `onRun` the live caret (`{ text, from, to }`, the text and the offsets of one instant), `onCaret` as the caret moves, an `onReady` handle that inserts at the caret and focuses, and an optional mark on the span a run would send (`highlight`). The language editors of `@coldsmirk/inkstone-sql`, `-redis`, `-mongodb` and `-elasticsearch` are this component and their language's wiring.
|
|
105
|
+
|
|
106
|
+
- **Your language's wiring** — `beforeMount` defines a grammar before the editor exists; `register` makes the language's registrations for this editor once, as it mounts, and returns their disposables, which go with the editor. Whatever a registration reads that can change between renders goes in `context` and is read at the moment it is needed through the registration's `context()` getter — never captured at mount, so a completion after a connection switch answers from the new connection.
|
|
107
|
+
- **Names** — `namespace` names what the editor puts into Monaco: the model path `file:///<namespace>/<n>.<extension>`, the run action `<namespace>.run`, the inserts' source `<namespace>-insert`, and the mark's default class `<namespace>-runnable`.
|
|
108
|
+
- **`options`** — merged under the editor's own (`ariaLabel`) on its identity: keep the object stable, since a new one is applied to the live editor again.
|
|
109
|
+
|
|
86
110
|
## License
|
|
87
111
|
|
|
88
112
|
UNLICENSED — proprietary. All rights reserved; no use, copying, or redistribution without the author's permission.
|
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-BW_zSOVE.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-
|
|
2
|
+
import { a as MonacoEditorProps, c as ConsoleCaret, d as ConsoleRegistration, f as MonacoConsoleEditor, i as MonacoEditor, l as ConsoleEditorHandle, n as MonacoLanguage, o as MonacoDiffEditor, p as MonacoConsoleEditorProps, r as monacoLanguages, s as MonacoDiffEditorProps, t as DEFAULT_MONACO_OPTIONS, u as ConsoleEditorProps } from "./monaco-DTLlEEfk.js";
|
|
3
3
|
import { n as useShikiHighlighter, t as ShikiState } from "./shiki-Ds-uZ7XH.js";
|
|
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 };
|
|
4
|
+
export { CodeMirrorDiffEditor, type CodeMirrorDiffEditorProps, CodeMirrorEditor, type CodeMirrorEditorProps, type CodeMirrorLanguage, type ConsoleCaret, type ConsoleEditorHandle, type ConsoleEditorProps, type ConsoleRegistration, DEFAULT_MONACO_OPTIONS, type EditorContext, type MergeView, MonacoConsoleEditor, type MonacoConsoleEditorProps, 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
1
|
import { a as CodeMirrorDiffEditor, i as CodeMirrorEditor, n as normalizeContext, r as searchPhrasesZhCn, t as codeMirrorLanguages } from "./codemirror-bROXidfr.js";
|
|
2
|
-
import { i as
|
|
2
|
+
import { a as MonacoEditor, i as MonacoConsoleEditor, n as monacoLanguages, r as MonacoDiffEditor, t as DEFAULT_MONACO_OPTIONS } from "./monaco-CHi4aRfZ.js";
|
|
3
3
|
import { t as useShikiHighlighter } from "./use-shiki-highlighter-CzSQcX2f.js";
|
|
4
4
|
import "./shiki.js";
|
|
5
|
-
export { CodeMirrorDiffEditor, CodeMirrorEditor, DEFAULT_MONACO_OPTIONS, MonacoDiffEditor, MonacoEditor, codeMirrorLanguages, monacoLanguages, normalizeContext, searchPhrasesZhCn, useShikiHighlighter };
|
|
5
|
+
export { CodeMirrorDiffEditor, CodeMirrorEditor, DEFAULT_MONACO_OPTIONS, MonacoConsoleEditor, MonacoDiffEditor, MonacoEditor, codeMirrorLanguages, monacoLanguages, normalizeContext, searchPhrasesZhCn, useShikiHighlighter };
|
|
@@ -59,119 +59,6 @@ function useMonacoHost(locale, hostOptions) {
|
|
|
59
59
|
return state;
|
|
60
60
|
}
|
|
61
61
|
//#endregion
|
|
62
|
-
//#region src/monaco-diff-editor.tsx
|
|
63
|
-
function diffDocumentOptions({ lineWrapping, showLineNumbers, readOnly, collapseUnchanged, fontSize, lineHeight }) {
|
|
64
|
-
const resolved = {
|
|
65
|
-
wordWrap: lineWrapping ? "on" : "off",
|
|
66
|
-
wordWrapIndicator: lineWrapping,
|
|
67
|
-
lineNumbers: showLineNumbers ? "on" : "off",
|
|
68
|
-
readOnly,
|
|
69
|
-
hideUnchangedRegions: { enabled: collapseUnchanged }
|
|
70
|
-
};
|
|
71
|
-
if (fontSize !== void 0) resolved.fontSize = fontSize;
|
|
72
|
-
if (lineHeight !== void 0) resolved.lineHeight = lineHeight;
|
|
73
|
-
return resolved;
|
|
74
|
-
}
|
|
75
|
-
function reconcilePane(model, value, reconciling) {
|
|
76
|
-
if (!model) return;
|
|
77
|
-
reconciling.current = true;
|
|
78
|
-
try {
|
|
79
|
-
reconcileModelValue(model, value);
|
|
80
|
-
} finally {
|
|
81
|
-
reconciling.current = false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function MonacoDiffEditor({ original, modified, onChange, language = "javascript", dark = false, locale, readOnly = false, collapseUnchanged = false, lineWrapping = false, showLineNumbers = true, fontSize, lineHeight, height = "100%", width, className, options, hostOptions, loading = "Loading editor…", failure = "Editor failed to load", beforeMount, onMount }) {
|
|
85
|
-
const hostState = useMonacoHost(locale, hostOptions);
|
|
86
|
-
const { state: shikiState, highlighterRef } = useShikiHighlighter();
|
|
87
|
-
const beforeMountRef = useLatest(beforeMount);
|
|
88
|
-
const onMountRef = useLatest(onMount);
|
|
89
|
-
const onChangeRef = useLatest(onChange);
|
|
90
|
-
const originalRef = useLatest(original);
|
|
91
|
-
const modifiedRef = useLatest(modified);
|
|
92
|
-
const diffEditorRef = useRef(null);
|
|
93
|
-
const reconcilingRef = useRef(false);
|
|
94
|
-
const modifiedTransitionRef = useRef(false);
|
|
95
|
-
const [reconcileRevision, requestControlledReconcile] = useControlledReconcileSignal();
|
|
96
|
-
const initialValuesRef = useRef({
|
|
97
|
-
original,
|
|
98
|
-
modified
|
|
99
|
-
});
|
|
100
|
-
const mergedOptions = useMemo(() => {
|
|
101
|
-
return {
|
|
102
|
-
...DEFAULT_MONACO_OPTIONS,
|
|
103
|
-
...diffDocumentOptions({
|
|
104
|
-
lineWrapping,
|
|
105
|
-
showLineNumbers,
|
|
106
|
-
readOnly,
|
|
107
|
-
collapseUnchanged,
|
|
108
|
-
fontSize,
|
|
109
|
-
lineHeight
|
|
110
|
-
}),
|
|
111
|
-
...options
|
|
112
|
-
};
|
|
113
|
-
}, [
|
|
114
|
-
lineWrapping,
|
|
115
|
-
showLineNumbers,
|
|
116
|
-
readOnly,
|
|
117
|
-
collapseUnchanged,
|
|
118
|
-
fontSize,
|
|
119
|
-
lineHeight,
|
|
120
|
-
options
|
|
121
|
-
]);
|
|
122
|
-
useLayoutEffect(() => {
|
|
123
|
-
reconcilePane(diffEditorRef.current?.getOriginalEditor().getModel() ?? null, original, reconcilingRef);
|
|
124
|
-
}, [original]);
|
|
125
|
-
useInsertionEffect(() => {
|
|
126
|
-
const model = diffEditorRef.current?.getModifiedEditor().getModel();
|
|
127
|
-
modifiedTransitionRef.current = model !== null && model !== void 0 && !modelValueMatches(model, modified);
|
|
128
|
-
}, [modified, reconcileRevision]);
|
|
129
|
-
useLayoutEffect(() => {
|
|
130
|
-
try {
|
|
131
|
-
reconcilePane(diffEditorRef.current?.getModifiedEditor().getModel() ?? null, modified, reconcilingRef);
|
|
132
|
-
} finally {
|
|
133
|
-
modifiedTransitionRef.current = false;
|
|
134
|
-
}
|
|
135
|
-
}, [modified, reconcileRevision]);
|
|
136
|
-
if (hostState === "failed") return /* @__PURE__ */ jsx(EditorFace, { children: failure });
|
|
137
|
-
if (hostState === "loading" || shikiState === "loading") return /* @__PURE__ */ jsx(EditorFace, { children: loading });
|
|
138
|
-
const handleBeforeMount = (monaco) => {
|
|
139
|
-
const highlighter = highlighterRef.current;
|
|
140
|
-
if (highlighter) registerShikiHighlighting(monaco, highlighter);
|
|
141
|
-
beforeMountRef.current?.(monaco);
|
|
142
|
-
};
|
|
143
|
-
const handleMount = (diffEditor, monaco) => {
|
|
144
|
-
initialValuesRef.current = {
|
|
145
|
-
original: originalRef.current,
|
|
146
|
-
modified: modifiedRef.current
|
|
147
|
-
};
|
|
148
|
-
diffEditorRef.current = diffEditor;
|
|
149
|
-
const modifiedEditor = diffEditor.getModifiedEditor();
|
|
150
|
-
modifiedEditor.onDidChangeModelContent(() => {
|
|
151
|
-
if (reconcilingRef.current || modifiedTransitionRef.current) return;
|
|
152
|
-
onChangeRef.current?.(modifiedEditor.getValue());
|
|
153
|
-
requestControlledReconcile();
|
|
154
|
-
});
|
|
155
|
-
reconcilePane(diffEditor.getOriginalEditor().getModel(), originalRef.current, reconcilingRef);
|
|
156
|
-
reconcilePane(modifiedEditor.getModel(), modifiedRef.current, reconcilingRef);
|
|
157
|
-
onMountRef.current?.(diffEditor, monaco);
|
|
158
|
-
};
|
|
159
|
-
const theme = shikiState === "ready" ? shikiThemeId(dark) : dark ? "vs-dark" : "vs";
|
|
160
|
-
return /* @__PURE__ */ jsx(DiffEditor, {
|
|
161
|
-
beforeMount: handleBeforeMount,
|
|
162
|
-
className,
|
|
163
|
-
height,
|
|
164
|
-
language,
|
|
165
|
-
loading,
|
|
166
|
-
modified: diffEditorRef.current ? initialValuesRef.current.modified : modified,
|
|
167
|
-
options: mergedOptions,
|
|
168
|
-
original: diffEditorRef.current ? initialValuesRef.current.original : original,
|
|
169
|
-
theme,
|
|
170
|
-
width,
|
|
171
|
-
onMount: handleMount
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
//#endregion
|
|
175
62
|
//#region src/monaco-editor.tsx
|
|
176
63
|
function documentOptions({ placeholder, lineWrapping, showLineNumbers, folding, disableContextMenu, fontSize, lineHeight, tabSize }) {
|
|
177
64
|
const resolved = {
|
|
@@ -326,4 +213,238 @@ function MonacoEditor({ value, onChange, language = "javascript", dark = false,
|
|
|
326
213
|
});
|
|
327
214
|
}
|
|
328
215
|
//#endregion
|
|
329
|
-
|
|
216
|
+
//#region src/monaco-console-editor.tsx
|
|
217
|
+
const EDITOR_ID_KEY = Symbol.for("coldsmirk.inkstone.react.consoleEditorId");
|
|
218
|
+
function nextEditorId() {
|
|
219
|
+
const holder = globalThis;
|
|
220
|
+
const id = holder[EDITOR_ID_KEY] ?? 0;
|
|
221
|
+
holder[EDITOR_ID_KEY] = id + 1;
|
|
222
|
+
return id;
|
|
223
|
+
}
|
|
224
|
+
function caretOf(instance) {
|
|
225
|
+
const model = instance.getModel();
|
|
226
|
+
const selection = instance.getSelection();
|
|
227
|
+
if (model === null || selection === null) return {
|
|
228
|
+
text: "",
|
|
229
|
+
from: 0,
|
|
230
|
+
to: 0
|
|
231
|
+
};
|
|
232
|
+
return {
|
|
233
|
+
text: model.getValue(),
|
|
234
|
+
from: model.getOffsetAt(selection.getStartPosition()),
|
|
235
|
+
to: model.getOffsetAt(selection.getEndPosition())
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function monacoRange(model, span) {
|
|
239
|
+
const from = model.getPositionAt(span.from);
|
|
240
|
+
const to = model.getPositionAt(span.to);
|
|
241
|
+
return {
|
|
242
|
+
startLineNumber: from.lineNumber,
|
|
243
|
+
startColumn: from.column,
|
|
244
|
+
endLineNumber: to.lineNumber,
|
|
245
|
+
endColumn: to.column
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function markRunnable(instance, marked, span, className) {
|
|
249
|
+
const model = instance.getModel();
|
|
250
|
+
if (model === null) return;
|
|
251
|
+
const decorations = span === null ? [] : [{
|
|
252
|
+
range: monacoRange(model, span),
|
|
253
|
+
options: {
|
|
254
|
+
linesDecorationsClassName: className,
|
|
255
|
+
isWholeLine: true
|
|
256
|
+
}
|
|
257
|
+
}];
|
|
258
|
+
if (marked.current !== null) marked.current.set(decorations);
|
|
259
|
+
else if (decorations.length > 0) marked.current = instance.createDecorationsCollection(decorations);
|
|
260
|
+
}
|
|
261
|
+
function MonacoConsoleEditor({ value, onChange, dark = false, height = 200, fontSize, locale, placeholder, ariaLabel, loading, failure, onRun, runActionLabel = "Run", onCaret, onReady, onMount, options, language, namespace, extension, context, beforeMount, register, highlight = null, highlightClassName = `${namespace}-runnable` }) {
|
|
262
|
+
const [path] = useState(() => `file:///${namespace}/${String(nextEditorId())}.${extension}`);
|
|
263
|
+
const bindings = useRef([]);
|
|
264
|
+
const contextRef = useLatest(context);
|
|
265
|
+
const runRef = useLatest(onRun);
|
|
266
|
+
const caretRef = useLatest(onCaret);
|
|
267
|
+
const instance = useRef(null);
|
|
268
|
+
const marked = useRef(null);
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
if (instance.current !== null) markRunnable(instance.current, marked, highlight, highlightClassName);
|
|
271
|
+
}, [highlight, highlightClassName]);
|
|
272
|
+
useEffect(() => () => {
|
|
273
|
+
for (const binding of bindings.current) binding.dispose();
|
|
274
|
+
bindings.current = [];
|
|
275
|
+
}, []);
|
|
276
|
+
const constructionOptions = useMemo(() => {
|
|
277
|
+
return {
|
|
278
|
+
...options,
|
|
279
|
+
ariaLabel
|
|
280
|
+
};
|
|
281
|
+
}, [options, ariaLabel]);
|
|
282
|
+
const fill = height === "fill";
|
|
283
|
+
return /* @__PURE__ */ jsx("div", {
|
|
284
|
+
style: fill ? {
|
|
285
|
+
height: "100%",
|
|
286
|
+
minHeight: 0
|
|
287
|
+
} : { height },
|
|
288
|
+
children: /* @__PURE__ */ jsx(MonacoEditor, {
|
|
289
|
+
showLineNumbers: true,
|
|
290
|
+
beforeMount,
|
|
291
|
+
dark,
|
|
292
|
+
failure,
|
|
293
|
+
fontSize,
|
|
294
|
+
height: fill ? "100%" : height,
|
|
295
|
+
language,
|
|
296
|
+
loading,
|
|
297
|
+
locale,
|
|
298
|
+
options: constructionOptions,
|
|
299
|
+
path,
|
|
300
|
+
placeholder,
|
|
301
|
+
value,
|
|
302
|
+
onChange,
|
|
303
|
+
onMount: (live, monaco) => {
|
|
304
|
+
instance.current = live;
|
|
305
|
+
bindings.current.push(live.onDidChangeCursorSelection(() => caretRef.current?.(caretOf(live))), live.addAction({
|
|
306
|
+
id: `${namespace}.run`,
|
|
307
|
+
label: runActionLabel,
|
|
308
|
+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
|
|
309
|
+
run: (current) => runRef.current?.(caretOf(current))
|
|
310
|
+
}), ...register?.({
|
|
311
|
+
monaco,
|
|
312
|
+
editor: live,
|
|
313
|
+
path,
|
|
314
|
+
context: () => contextRef.current
|
|
315
|
+
}) ?? []);
|
|
316
|
+
markRunnable(live, marked, highlight, highlightClassName);
|
|
317
|
+
onReady?.({
|
|
318
|
+
insert: (text) => {
|
|
319
|
+
const selection = live.getSelection();
|
|
320
|
+
if (selection) live.executeEdits(`${namespace}-insert`, [{
|
|
321
|
+
range: selection,
|
|
322
|
+
text,
|
|
323
|
+
forceMoveMarkers: true
|
|
324
|
+
}]);
|
|
325
|
+
live.focus();
|
|
326
|
+
},
|
|
327
|
+
focus: () => {
|
|
328
|
+
live.focus();
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
onMount?.(live, monaco);
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
//#endregion
|
|
337
|
+
//#region src/monaco-diff-editor.tsx
|
|
338
|
+
function diffDocumentOptions({ lineWrapping, showLineNumbers, readOnly, collapseUnchanged, fontSize, lineHeight }) {
|
|
339
|
+
const resolved = {
|
|
340
|
+
wordWrap: lineWrapping ? "on" : "off",
|
|
341
|
+
wordWrapIndicator: lineWrapping,
|
|
342
|
+
lineNumbers: showLineNumbers ? "on" : "off",
|
|
343
|
+
readOnly,
|
|
344
|
+
hideUnchangedRegions: { enabled: collapseUnchanged }
|
|
345
|
+
};
|
|
346
|
+
if (fontSize !== void 0) resolved.fontSize = fontSize;
|
|
347
|
+
if (lineHeight !== void 0) resolved.lineHeight = lineHeight;
|
|
348
|
+
return resolved;
|
|
349
|
+
}
|
|
350
|
+
function reconcilePane(model, value, reconciling) {
|
|
351
|
+
if (!model) return;
|
|
352
|
+
reconciling.current = true;
|
|
353
|
+
try {
|
|
354
|
+
reconcileModelValue(model, value);
|
|
355
|
+
} finally {
|
|
356
|
+
reconciling.current = false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function MonacoDiffEditor({ original, modified, onChange, language = "javascript", dark = false, locale, readOnly = false, collapseUnchanged = false, lineWrapping = false, showLineNumbers = true, fontSize, lineHeight, height = "100%", width, className, options, hostOptions, loading = "Loading editor…", failure = "Editor failed to load", beforeMount, onMount }) {
|
|
360
|
+
const hostState = useMonacoHost(locale, hostOptions);
|
|
361
|
+
const { state: shikiState, highlighterRef } = useShikiHighlighter();
|
|
362
|
+
const beforeMountRef = useLatest(beforeMount);
|
|
363
|
+
const onMountRef = useLatest(onMount);
|
|
364
|
+
const onChangeRef = useLatest(onChange);
|
|
365
|
+
const originalRef = useLatest(original);
|
|
366
|
+
const modifiedRef = useLatest(modified);
|
|
367
|
+
const diffEditorRef = useRef(null);
|
|
368
|
+
const reconcilingRef = useRef(false);
|
|
369
|
+
const modifiedTransitionRef = useRef(false);
|
|
370
|
+
const [reconcileRevision, requestControlledReconcile] = useControlledReconcileSignal();
|
|
371
|
+
const initialValuesRef = useRef({
|
|
372
|
+
original,
|
|
373
|
+
modified
|
|
374
|
+
});
|
|
375
|
+
const mergedOptions = useMemo(() => {
|
|
376
|
+
return {
|
|
377
|
+
...DEFAULT_MONACO_OPTIONS,
|
|
378
|
+
...diffDocumentOptions({
|
|
379
|
+
lineWrapping,
|
|
380
|
+
showLineNumbers,
|
|
381
|
+
readOnly,
|
|
382
|
+
collapseUnchanged,
|
|
383
|
+
fontSize,
|
|
384
|
+
lineHeight
|
|
385
|
+
}),
|
|
386
|
+
...options
|
|
387
|
+
};
|
|
388
|
+
}, [
|
|
389
|
+
lineWrapping,
|
|
390
|
+
showLineNumbers,
|
|
391
|
+
readOnly,
|
|
392
|
+
collapseUnchanged,
|
|
393
|
+
fontSize,
|
|
394
|
+
lineHeight,
|
|
395
|
+
options
|
|
396
|
+
]);
|
|
397
|
+
useLayoutEffect(() => {
|
|
398
|
+
reconcilePane(diffEditorRef.current?.getOriginalEditor().getModel() ?? null, original, reconcilingRef);
|
|
399
|
+
}, [original]);
|
|
400
|
+
useInsertionEffect(() => {
|
|
401
|
+
const model = diffEditorRef.current?.getModifiedEditor().getModel();
|
|
402
|
+
modifiedTransitionRef.current = model !== null && model !== void 0 && !modelValueMatches(model, modified);
|
|
403
|
+
}, [modified, reconcileRevision]);
|
|
404
|
+
useLayoutEffect(() => {
|
|
405
|
+
try {
|
|
406
|
+
reconcilePane(diffEditorRef.current?.getModifiedEditor().getModel() ?? null, modified, reconcilingRef);
|
|
407
|
+
} finally {
|
|
408
|
+
modifiedTransitionRef.current = false;
|
|
409
|
+
}
|
|
410
|
+
}, [modified, reconcileRevision]);
|
|
411
|
+
if (hostState === "failed") return /* @__PURE__ */ jsx(EditorFace, { children: failure });
|
|
412
|
+
if (hostState === "loading" || shikiState === "loading") return /* @__PURE__ */ jsx(EditorFace, { children: loading });
|
|
413
|
+
const handleBeforeMount = (monaco) => {
|
|
414
|
+
const highlighter = highlighterRef.current;
|
|
415
|
+
if (highlighter) registerShikiHighlighting(monaco, highlighter);
|
|
416
|
+
beforeMountRef.current?.(monaco);
|
|
417
|
+
};
|
|
418
|
+
const handleMount = (diffEditor, monaco) => {
|
|
419
|
+
initialValuesRef.current = {
|
|
420
|
+
original: originalRef.current,
|
|
421
|
+
modified: modifiedRef.current
|
|
422
|
+
};
|
|
423
|
+
diffEditorRef.current = diffEditor;
|
|
424
|
+
const modifiedEditor = diffEditor.getModifiedEditor();
|
|
425
|
+
modifiedEditor.onDidChangeModelContent(() => {
|
|
426
|
+
if (reconcilingRef.current || modifiedTransitionRef.current) return;
|
|
427
|
+
onChangeRef.current?.(modifiedEditor.getValue());
|
|
428
|
+
requestControlledReconcile();
|
|
429
|
+
});
|
|
430
|
+
reconcilePane(diffEditor.getOriginalEditor().getModel(), originalRef.current, reconcilingRef);
|
|
431
|
+
reconcilePane(modifiedEditor.getModel(), modifiedRef.current, reconcilingRef);
|
|
432
|
+
onMountRef.current?.(diffEditor, monaco);
|
|
433
|
+
};
|
|
434
|
+
const theme = shikiState === "ready" ? shikiThemeId(dark) : dark ? "vs-dark" : "vs";
|
|
435
|
+
return /* @__PURE__ */ jsx(DiffEditor, {
|
|
436
|
+
beforeMount: handleBeforeMount,
|
|
437
|
+
className,
|
|
438
|
+
height,
|
|
439
|
+
language,
|
|
440
|
+
loading,
|
|
441
|
+
modified: diffEditorRef.current ? initialValuesRef.current.modified : modified,
|
|
442
|
+
options: mergedOptions,
|
|
443
|
+
original: diffEditorRef.current ? initialValuesRef.current.original : original,
|
|
444
|
+
theme,
|
|
445
|
+
width,
|
|
446
|
+
onMount: handleMount
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
//#endregion
|
|
450
|
+
export { MonacoEditor as a, MonacoConsoleEditor as i, monacoLanguages as n, MonacoDiffEditor as r, DEFAULT_MONACO_OPTIONS$1 as t };
|
|
@@ -1,7 +1,191 @@
|
|
|
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
|
+
import { DEFAULT_MONACO_OPTIONS as DEFAULT_MONACO_OPTIONS$1, MonacoHostOptions, MonacoLanguage, MonacoLanguage as MonacoLanguage$1, MonacoModule, MonacoUiLocale, monacoLanguages } from "@coldsmirk/inkstone-monaco";
|
|
3
3
|
import { BeforeMount, DiffBeforeMount, DiffOnMount, OnMount } from "@monaco-editor/react";
|
|
4
|
-
import { editor } from "monaco-editor";
|
|
4
|
+
import { IDisposable, editor } from "monaco-editor";
|
|
5
|
+
//#region src/monaco-console-editor.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Where the operator is in the document, in offsets — everything a host needs to decide what a
|
|
8
|
+
* run should send, and nothing about the language. `from` and `to` are the selection's own
|
|
9
|
+
* bounds and are equal where nothing is selected: a caret is a selection of nothing.
|
|
10
|
+
*
|
|
11
|
+
* It rides with the text rather than beside it because the two must belong to the same instant.
|
|
12
|
+
* The controlled `value` is one React render behind the keystroke that triggered a run, and a
|
|
13
|
+
* console must run what the operator is looking at.
|
|
14
|
+
*/
|
|
15
|
+
interface ConsoleCaret {
|
|
16
|
+
text: string;
|
|
17
|
+
from: number;
|
|
18
|
+
to: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What a host reaches into a console editor for: inserting at the caret — a schema, key or
|
|
22
|
+
* collection tree is an input device, not a browser — and the focus.
|
|
23
|
+
*/
|
|
24
|
+
interface ConsoleEditorHandle {
|
|
25
|
+
insert: (text: string) => void;
|
|
26
|
+
focus: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* What every console editor takes: `<MonacoConsoleEditor>`, and each language's editor built on
|
|
30
|
+
* it (`<SqlEditor>`, `<RedisEditor>`, `<MongodbEditor>`, `<ElasticsearchEditor>`).
|
|
31
|
+
*/
|
|
32
|
+
interface ConsoleEditorProps<Handle extends ConsoleEditorHandle = ConsoleEditorHandle> {
|
|
33
|
+
value: string;
|
|
34
|
+
onChange: (value: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Render with the dark theme. Like every inkstone editor, a console never reads the OS
|
|
37
|
+
* `prefers-color-scheme`; wire this to the app's color-scheme state.
|
|
38
|
+
*
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
dark?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Editor height in px, or `"fill"` to consume the parent's resolved height (the parent must be
|
|
44
|
+
* a `min-height: 0` flex item). Monaco's loading placeholder is `height: 100%`, so a height
|
|
45
|
+
* that resolves to nothing collapses the field to one line while the editor loads.
|
|
46
|
+
*
|
|
47
|
+
* @default 200
|
|
48
|
+
*/
|
|
49
|
+
height?: number | "fill";
|
|
50
|
+
/**
|
|
51
|
+
* Editor font size in px. Omit to keep the inkstone default.
|
|
52
|
+
*/
|
|
53
|
+
fontSize?: number;
|
|
54
|
+
/**
|
|
55
|
+
* UI locale for Monaco's built-in chrome — page-global and locked by the first editor to
|
|
56
|
+
* mount, exactly as on `<MonacoEditor>`.
|
|
57
|
+
*/
|
|
58
|
+
locale?: MonacoUiLocale;
|
|
59
|
+
placeholder?: string;
|
|
60
|
+
/**
|
|
61
|
+
* The editor's accessible name, set on Monaco's own input surface — several consoles on one
|
|
62
|
+
* page are indistinguishable to assistive tech without one.
|
|
63
|
+
*/
|
|
64
|
+
ariaLabel: string;
|
|
65
|
+
/**
|
|
66
|
+
* Shown while the Monaco host loads / when it fails to load — passed through to
|
|
67
|
+
* `<MonacoEditor>`, whose English defaults apply when omitted.
|
|
68
|
+
*/
|
|
69
|
+
loading?: ReactNode;
|
|
70
|
+
failure?: ReactNode;
|
|
71
|
+
/**
|
|
72
|
+
* Ctrl/Cmd-Enter — the run key, bound inside the editor because that is where the operator's
|
|
73
|
+
* hands are. It is handed the editor's live caret, from which the host works out what to send.
|
|
74
|
+
* Read at every press, so it always runs against the connection the *current* render is bound
|
|
75
|
+
* to.
|
|
76
|
+
*/
|
|
77
|
+
onRun?: (caret: ConsoleCaret) => void;
|
|
78
|
+
/**
|
|
79
|
+
* The label the run action shows in Monaco's own chrome (command palette, context menu).
|
|
80
|
+
*/
|
|
81
|
+
runActionLabel?: string;
|
|
82
|
+
/**
|
|
83
|
+
* The caret moved, or the text under it changed. The host tracks it to show what a run would
|
|
84
|
+
* send; the run key does not depend on that tracking having caught up.
|
|
85
|
+
*/
|
|
86
|
+
onCaret?: (caret: ConsoleCaret) => void;
|
|
87
|
+
onReady?: (handle: Handle) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Runs after the editor's own mount wiring, with the live editor and the monaco module — the
|
|
90
|
+
* escape hatch for host-specific concerns (focus shields, extra actions).
|
|
91
|
+
*/
|
|
92
|
+
onMount?: (instance: editor.IStandaloneCodeEditor, monaco: MonacoModule) => void;
|
|
93
|
+
/**
|
|
94
|
+
* Extra construction options, merged under the editor's own (`ariaLabel`): the escape hatch a
|
|
95
|
+
* host reaches for a construction option no prop names — `overflowWidgetsDomNode`, where the
|
|
96
|
+
* page's own layers would otherwise stand over the editor's floating widgets (hover,
|
|
97
|
+
* completion, parameter hints) left inside it. Merged on its identity, so keep it stable: a
|
|
98
|
+
* new object is applied to the live editor again.
|
|
99
|
+
*/
|
|
100
|
+
options?: editor.IStandaloneEditorConstructionOptions;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* What a console editor hands its language as the editor mounts.
|
|
104
|
+
*/
|
|
105
|
+
interface ConsoleRegistration<Context> {
|
|
106
|
+
monaco: MonacoModule;
|
|
107
|
+
editor: editor.IStandaloneCodeEditor;
|
|
108
|
+
/**
|
|
109
|
+
* The model's path: what a registration that answers for one model compares a request's
|
|
110
|
+
* model against.
|
|
111
|
+
*/
|
|
112
|
+
path: string;
|
|
113
|
+
/**
|
|
114
|
+
* The `context` prop as the latest committed render passed it. A registration happens once,
|
|
115
|
+
* at mount, so whatever it reaches that can change — an assist, a wording — is read through
|
|
116
|
+
* this at the moment it is needed and never captured: a completion after a connection switch
|
|
117
|
+
* can never answer from the connection a previous render was bound to.
|
|
118
|
+
*/
|
|
119
|
+
context: () => Context;
|
|
120
|
+
}
|
|
121
|
+
interface MonacoConsoleEditorProps<Context> extends ConsoleEditorProps {
|
|
122
|
+
/**
|
|
123
|
+
* The Monaco language id the model is registered under.
|
|
124
|
+
*/
|
|
125
|
+
language: string;
|
|
126
|
+
/**
|
|
127
|
+
* The name everything this editor puts into Monaco goes under: its model's directory
|
|
128
|
+
* (`file:///<namespace>/<n>.<extension>`), its run action (`<namespace>.run`), the source of
|
|
129
|
+
* its inserts (`<namespace>-insert`) and the runnable mark's default class
|
|
130
|
+
* (`<namespace>-runnable`).
|
|
131
|
+
*/
|
|
132
|
+
namespace: string;
|
|
133
|
+
/**
|
|
134
|
+
* The extension the model's path ends in.
|
|
135
|
+
*/
|
|
136
|
+
extension: string;
|
|
137
|
+
/**
|
|
138
|
+
* Whatever the language's registrations read that can change between renders, handed to
|
|
139
|
+
* `register` as a getter (`ConsoleRegistration.context`) — `undefined` where they read
|
|
140
|
+
* nothing.
|
|
141
|
+
*/
|
|
142
|
+
context: Context;
|
|
143
|
+
/**
|
|
144
|
+
* Runs before the editor is created, after inkstone's own pre-mount setup: where a language
|
|
145
|
+
* defines its grammar.
|
|
146
|
+
*/
|
|
147
|
+
beforeMount?: (monaco: MonacoModule) => void;
|
|
148
|
+
/**
|
|
149
|
+
* What the language registers for this editor — providers, markers, keybindings — once, as it
|
|
150
|
+
* mounts. The disposables go with the editor: Monaco's registry outlives it, and a provider
|
|
151
|
+
* left behind would answer for a model that no longer exists.
|
|
152
|
+
*/
|
|
153
|
+
register?: (registration: ConsoleRegistration<Context>) => IDisposable[];
|
|
154
|
+
/**
|
|
155
|
+
* The label the run action shows in Monaco's own chrome (command palette, context menu).
|
|
156
|
+
*
|
|
157
|
+
* @default "Run"
|
|
158
|
+
*/
|
|
159
|
+
runActionLabel?: string;
|
|
160
|
+
/**
|
|
161
|
+
* The span to mark as the one a run would send. Null leaves the gutter clean, which is the
|
|
162
|
+
* right answer while there is nothing to choose between.
|
|
163
|
+
*/
|
|
164
|
+
highlight?: {
|
|
165
|
+
from: number;
|
|
166
|
+
to: number;
|
|
167
|
+
} | null;
|
|
168
|
+
/**
|
|
169
|
+
* The class the mark's line decoration carries. The host owns its stylesheet — a left-margin
|
|
170
|
+
* bar reads well, because it says which span without touching a single colour the syntax
|
|
171
|
+
* already assigned.
|
|
172
|
+
*
|
|
173
|
+
* @default `${namespace}-runnable`
|
|
174
|
+
*/
|
|
175
|
+
highlightClassName?: string;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* A console's editing surface: Monaco under the caller's language, the run key handing the live
|
|
179
|
+
* caret over, the caret reported as it moves, a handle that inserts at the caret and focuses, an
|
|
180
|
+
* optional mark on the span a run would send, and the language's own registrations made once as
|
|
181
|
+
* the editor mounts and disposed with it. The language editors of `@coldsmirk/inkstone-sql`,
|
|
182
|
+
* `-redis`, `-mongodb` and `-elasticsearch` are this component and their language's wiring.
|
|
183
|
+
*
|
|
184
|
+
* The editor draws no chrome of its own: it is a band of its host's surface, and the separation
|
|
185
|
+
* is the host's hairline rules — a border here would dress a pane up as a form input.
|
|
186
|
+
*/
|
|
187
|
+
declare function MonacoConsoleEditor<Context>({ value, onChange, dark, height, fontSize, locale, placeholder, ariaLabel, loading, failure, onRun, runActionLabel, onCaret, onReady, onMount, options, language, namespace, extension, context, beforeMount, register, highlight, highlightClassName }: MonacoConsoleEditorProps<Context>): ReactNode;
|
|
188
|
+
//#endregion
|
|
5
189
|
//#region src/monaco-diff-editor.d.ts
|
|
6
190
|
interface MonacoDiffEditorProps {
|
|
7
191
|
/**
|
|
@@ -304,4 +488,4 @@ interface MonacoEditorProps {
|
|
|
304
488
|
*/
|
|
305
489
|
declare function MonacoEditor({ value, onChange, language, dark, locale, placeholder, lineWrapping, showLineNumbers, folding, disableContextMenu, disableFind, disableCommandPalette, fontSize, lineHeight, tabSize, path, height, width, className, options, hostOptions, loading, failure, beforeMount, onMount, onFocus, onBlur }: MonacoEditorProps): ReactNode;
|
|
306
490
|
//#endregion
|
|
307
|
-
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 };
|
|
491
|
+
export { MonacoEditorProps as a, ConsoleCaret as c, ConsoleRegistration as d, MonacoConsoleEditor as f, MonacoEditor as i, ConsoleEditorHandle as l, MonacoLanguage$1 as n, MonacoDiffEditor as o, MonacoConsoleEditorProps as p, monacoLanguages as r, MonacoDiffEditorProps as s, DEFAULT_MONACO_OPTIONS$1 as t, ConsoleEditorProps as u };
|
package/dist/monaco.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
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-
|
|
2
|
-
export { DEFAULT_MONACO_OPTIONS, MonacoDiffEditor, type MonacoDiffEditorProps, MonacoEditor, type MonacoEditorProps, type MonacoLanguage, monacoLanguages };
|
|
1
|
+
import { a as MonacoEditorProps, c as ConsoleCaret, d as ConsoleRegistration, f as MonacoConsoleEditor, i as MonacoEditor, l as ConsoleEditorHandle, n as MonacoLanguage, o as MonacoDiffEditor, p as MonacoConsoleEditorProps, r as monacoLanguages, s as MonacoDiffEditorProps, t as DEFAULT_MONACO_OPTIONS, u as ConsoleEditorProps } from "./monaco-DTLlEEfk.js";
|
|
2
|
+
export { type ConsoleCaret, type ConsoleEditorHandle, type ConsoleEditorProps, type ConsoleRegistration, DEFAULT_MONACO_OPTIONS, MonacoConsoleEditor, type MonacoConsoleEditorProps, MonacoDiffEditor, type MonacoDiffEditorProps, MonacoEditor, type MonacoEditorProps, type MonacoLanguage, monacoLanguages };
|
package/dist/monaco.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as DEFAULT_MONACO_OPTIONS, i as
|
|
1
|
+
export { t as DEFAULT_MONACO_OPTIONS, i as MonacoConsoleEditor, r as MonacoDiffEditor, a as MonacoEditor, n as monacoLanguages } from "./monaco-CHi4aRfZ.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coldsmirk/inkstone-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Drop-in React code editors: a preconfigured Monaco <MonacoEditor> (offline workers, Shiki catppuccin themes, dark switching, opt-in zh-CN UI) and a controlled CodeMirror 6 <CodeMirrorEditor> (catppuccin themes, dark switching, sizing).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@monaco-editor/react": "^4.7.0",
|
|
51
|
-
"@coldsmirk/inkstone-codemirror": "^0.
|
|
52
|
-
"@coldsmirk/inkstone-core": "^0.
|
|
53
|
-
"@coldsmirk/inkstone-monaco": "^0.
|
|
51
|
+
"@coldsmirk/inkstone-codemirror": "^0.33.0",
|
|
52
|
+
"@coldsmirk/inkstone-core": "^0.33.0",
|
|
53
|
+
"@coldsmirk/inkstone-monaco": "^0.33.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@codemirror/autocomplete": "^6.20.3",
|