@xh/hoist 79.0.0-SNAPSHOT.1765824728801 → 79.0.0-SNAPSHOT.1765824964847
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/CHANGELOG.md +0 -26
- package/admin/tabs/cluster/instances/connpool/ConnPoolMonitorPanel.ts +1 -1
- package/admin/tabs/cluster/instances/services/DetailsPanel.ts +1 -1
- package/admin/tabs/cluster/objects/DetailPanel.ts +1 -1
- package/build/types/core/HoistBase.d.ts +1 -1
- package/build/types/desktop/cmp/input/CodeInput.d.ts +21 -9
- package/build/types/desktop/cmp/input/JsonInput.d.ts +3 -15
- package/core/HoistBase.ts +7 -1
- package/desktop/cmp/filechooser/FileChooserModel.ts +2 -1
- package/desktop/cmp/input/CodeInput.scss +15 -21
- package/desktop/cmp/input/CodeInput.ts +193 -248
- package/desktop/cmp/input/JsonInput.ts +22 -126
- package/package.json +2 -9
- package/tsconfig.tsbuildinfo +1 -1
- package/build/types/desktop/cmp/input/impl/one-dark.d.ts +0 -23
- package/desktop/cmp/input/impl/one-dark.ts +0 -163
|
@@ -4,39 +4,6 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {autocompletion} from '@codemirror/autocomplete';
|
|
8
|
-
import {defaultKeymap, history, historyKeymap, indentWithTab} from '@codemirror/commands';
|
|
9
|
-
import {
|
|
10
|
-
defaultHighlightStyle,
|
|
11
|
-
foldGutter,
|
|
12
|
-
foldKeymap,
|
|
13
|
-
indentOnInput,
|
|
14
|
-
LanguageDescription,
|
|
15
|
-
LanguageSupport,
|
|
16
|
-
syntaxHighlighting
|
|
17
|
-
} from '@codemirror/language';
|
|
18
|
-
import {linter, lintGutter} from '@codemirror/lint';
|
|
19
|
-
import {highlightSelectionMatches, search} from '@codemirror/search';
|
|
20
|
-
import {
|
|
21
|
-
Compartment,
|
|
22
|
-
EditorState,
|
|
23
|
-
Extension,
|
|
24
|
-
RangeSetBuilder,
|
|
25
|
-
StateEffect,
|
|
26
|
-
StateField
|
|
27
|
-
} from '@codemirror/state';
|
|
28
|
-
import {
|
|
29
|
-
Decoration,
|
|
30
|
-
DecorationSet,
|
|
31
|
-
EditorView,
|
|
32
|
-
highlightActiveLine,
|
|
33
|
-
highlightActiveLineGutter,
|
|
34
|
-
keymap,
|
|
35
|
-
lineNumbers,
|
|
36
|
-
ViewPlugin,
|
|
37
|
-
ViewUpdate
|
|
38
|
-
} from '@codemirror/view';
|
|
39
|
-
import {oneDark} from './impl/one-dark';
|
|
40
7
|
import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
|
|
41
8
|
import {box, div, filler, fragment, frame, hbox, label, span, vbox} from '@xh/hoist/cmp/layout';
|
|
42
9
|
import {hoistCmp, HoistProps, LayoutProps, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
@@ -46,15 +13,31 @@ import {textInput} from '@xh/hoist/desktop/cmp/input/TextInput';
|
|
|
46
13
|
import {modalSupport} from '@xh/hoist/desktop/cmp/modalsupport/ModalSupport';
|
|
47
14
|
import {ModalSupportModel} from '@xh/hoist/desktop/cmp/modalsupport/ModalSupportModel';
|
|
48
15
|
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
16
|
+
import '@xh/hoist/desktop/register';
|
|
49
17
|
import {Icon} from '@xh/hoist/icon';
|
|
18
|
+
import {textArea} from '@xh/hoist/kit/blueprint';
|
|
50
19
|
import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
20
|
+
import {wait} from '@xh/hoist/promise';
|
|
51
21
|
import {withDefault} from '@xh/hoist/utils/js';
|
|
52
22
|
import {getLayoutProps} from '@xh/hoist/utils/react';
|
|
53
23
|
import classNames from 'classnames';
|
|
54
|
-
import
|
|
24
|
+
import * as codemirror from 'codemirror';
|
|
25
|
+
import 'codemirror/addon/fold/brace-fold.js';
|
|
26
|
+
import 'codemirror/addon/fold/foldcode.js';
|
|
27
|
+
import 'codemirror/addon/fold/foldgutter.css';
|
|
28
|
+
import 'codemirror/addon/fold/foldgutter.js';
|
|
29
|
+
import 'codemirror/addon/lint/lint.css';
|
|
30
|
+
import 'codemirror/addon/lint/lint.js';
|
|
31
|
+
import 'codemirror/addon/scroll/simplescrollbars.css';
|
|
32
|
+
import 'codemirror/addon/scroll/simplescrollbars.js';
|
|
33
|
+
import 'codemirror/addon/search/searchcursor.js';
|
|
34
|
+
import 'codemirror/addon/selection/mark-selection.js';
|
|
35
|
+
import 'codemirror/lib/codemirror.css';
|
|
36
|
+
import 'codemirror/theme/dracula.css';
|
|
37
|
+
import {compact, defaultsDeep, isEqual, isFunction} from 'lodash';
|
|
55
38
|
import {ReactElement} from 'react';
|
|
39
|
+
import {findDOMNode} from 'react-dom';
|
|
56
40
|
import './CodeInput.scss';
|
|
57
|
-
import {languages} from '@codemirror/language-data';
|
|
58
41
|
|
|
59
42
|
export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps {
|
|
60
43
|
/** True to focus the control on render. */
|
|
@@ -63,6 +46,12 @@ export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps
|
|
|
63
46
|
/** False to not commit on every change/keystroke, default true. */
|
|
64
47
|
commitOnChange?: boolean;
|
|
65
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Configuration object with any properties supported by the CodeMirror API.
|
|
51
|
+
* @see {@link https://codemirror.net/doc/manual.html#api_configuration|CodeMirror Docs}
|
|
52
|
+
*/
|
|
53
|
+
editorProps?: PlainObject;
|
|
54
|
+
|
|
66
55
|
/**
|
|
67
56
|
* True to enable case-insensitive searching within the input. Default false, except in
|
|
68
57
|
* fullscreen mode, where search will be shown unless explicitly *disabled*. Note that
|
|
@@ -83,10 +72,10 @@ export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps
|
|
|
83
72
|
|
|
84
73
|
/**
|
|
85
74
|
* A CodeMirror language mode - default none (plain-text). See the CodeMirror docs
|
|
86
|
-
* ({@link https://
|
|
87
|
-
*
|
|
75
|
+
* ({@link https://codemirror.net/mode/}) regarding available modes.
|
|
76
|
+
* Applications must import any mode they wish to enable.
|
|
88
77
|
*/
|
|
89
|
-
|
|
78
|
+
mode?: string;
|
|
90
79
|
|
|
91
80
|
/**
|
|
92
81
|
* True to prevent user modification of editor contents, while still allowing user to
|
|
@@ -112,15 +101,6 @@ export interface CodeInputProps extends HoistProps, HoistInputProps, LayoutProps
|
|
|
112
101
|
* action buttons show only when the input focused and float in the bottom-right corner.
|
|
113
102
|
*/
|
|
114
103
|
showToolbar?: boolean;
|
|
115
|
-
|
|
116
|
-
/** False (default) to highlight active line in input. */
|
|
117
|
-
highlightActiveLine?: boolean;
|
|
118
|
-
|
|
119
|
-
/** True (default) to add line numbers to gutter. */
|
|
120
|
-
lineNumbers?: boolean | PlainObject;
|
|
121
|
-
|
|
122
|
-
/** False (default) to add line numbers to gutter. */
|
|
123
|
-
lineWrapping?: boolean | PlainObject;
|
|
124
104
|
}
|
|
125
105
|
|
|
126
106
|
/**
|
|
@@ -151,17 +131,17 @@ class CodeInputModel extends HoistInputModel {
|
|
|
151
131
|
@managed
|
|
152
132
|
modalSupportModel: ModalSupportModel = new ModalSupportModel();
|
|
153
133
|
|
|
154
|
-
editor
|
|
134
|
+
/** A CodeMirror editor instance. */
|
|
135
|
+
editor: any;
|
|
155
136
|
|
|
156
137
|
// Support for internal search feature.
|
|
157
138
|
cursor = null;
|
|
158
139
|
@bindable query: string = '';
|
|
159
140
|
@observable currentMatchIdx: number = -1;
|
|
160
|
-
@observable.ref matches
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
private themeCompartment = new Compartment();
|
|
141
|
+
@observable.ref matches = [];
|
|
142
|
+
get matchCount(): number {
|
|
143
|
+
return this.matches.length;
|
|
144
|
+
}
|
|
165
145
|
|
|
166
146
|
get fullScreen(): boolean {
|
|
167
147
|
return this.modalSupportModel.isModal;
|
|
@@ -234,7 +214,8 @@ class CodeInputModel extends HoistInputModel {
|
|
|
234
214
|
}
|
|
235
215
|
|
|
236
216
|
override blur() {
|
|
237
|
-
this.editor?.
|
|
217
|
+
this.editor?.execCommand('undoSelection');
|
|
218
|
+
this.editor?.getInputField().blur();
|
|
238
219
|
}
|
|
239
220
|
|
|
240
221
|
override focus() {
|
|
@@ -242,34 +223,12 @@ class CodeInputModel extends HoistInputModel {
|
|
|
242
223
|
}
|
|
243
224
|
|
|
244
225
|
override select() {
|
|
245
|
-
|
|
246
|
-
this.editor.dispatch({selection: {anchor: 0, head: this.editor.state.doc.length}});
|
|
226
|
+
this.editor?.execCommand('selectAll');
|
|
247
227
|
}
|
|
248
228
|
|
|
249
229
|
constructor() {
|
|
250
230
|
super();
|
|
251
231
|
makeObservable(this);
|
|
252
|
-
|
|
253
|
-
this.highlightField = StateField.define<DecorationSet>({
|
|
254
|
-
create: () => Decoration.none,
|
|
255
|
-
update: (deco, tr) => {
|
|
256
|
-
deco = deco.map(tr.changes);
|
|
257
|
-
if (tr.effects.some(e => e.is(this.updateMatchesEffect))) {
|
|
258
|
-
const builder = new RangeSetBuilder<Decoration>();
|
|
259
|
-
this.matches.forEach(match => {
|
|
260
|
-
builder.add(
|
|
261
|
-
match.from,
|
|
262
|
-
match.to,
|
|
263
|
-
Decoration.mark({class: 'xh-code-input--highlight'})
|
|
264
|
-
);
|
|
265
|
-
});
|
|
266
|
-
deco = builder.finish();
|
|
267
|
-
}
|
|
268
|
-
return deco;
|
|
269
|
-
},
|
|
270
|
-
provide: f => EditorView.decorations.from(f)
|
|
271
|
-
});
|
|
272
|
-
|
|
273
232
|
this.addReaction({
|
|
274
233
|
track: () => this.modalSupportModel.isModal,
|
|
275
234
|
run: () => this.focus(),
|
|
@@ -281,61 +240,103 @@ class CodeInputModel extends HoistInputModel {
|
|
|
281
240
|
this.addReaction({
|
|
282
241
|
track: () => XH.darkTheme,
|
|
283
242
|
run: () => {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
effects: this.themeCompartment.reconfigure(this.getThemeExtension())
|
|
287
|
-
});
|
|
243
|
+
const {editor} = this;
|
|
244
|
+
if (editor) editor.setOption('theme', XH.darkTheme ? 'dracula' : 'default');
|
|
288
245
|
}
|
|
289
246
|
});
|
|
290
247
|
|
|
291
248
|
this.addReaction({
|
|
292
249
|
track: () => this.renderValue,
|
|
293
|
-
run:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
250
|
+
run: value => {
|
|
251
|
+
const {editor} = this;
|
|
252
|
+
if (editor && editor.getValue() != value) {
|
|
253
|
+
// CodeMirror will throw on null value.
|
|
254
|
+
editor.setValue(value == null ? '' : value);
|
|
298
255
|
}
|
|
299
256
|
}
|
|
300
257
|
});
|
|
301
258
|
|
|
302
259
|
this.addReaction({
|
|
303
260
|
track: () => this.componentProps.readonly || this.componentProps.disabled,
|
|
304
|
-
run:
|
|
305
|
-
|
|
306
|
-
this.editor.dispatch({
|
|
307
|
-
effects: StateEffect.appendConfig.of(EditorView.editable.of(!readOnly))
|
|
308
|
-
});
|
|
261
|
+
run: editorReadOnly => {
|
|
262
|
+
this.editor.setOption('readOnly', editorReadOnly);
|
|
309
263
|
}
|
|
310
264
|
});
|
|
311
265
|
|
|
312
266
|
this.addReaction({
|
|
313
267
|
track: () => this.query,
|
|
314
268
|
run: query => {
|
|
315
|
-
if (query?.trim())
|
|
316
|
-
|
|
269
|
+
if (query?.trim()) {
|
|
270
|
+
this.findAll();
|
|
271
|
+
} else {
|
|
272
|
+
this.clearSearchResults();
|
|
273
|
+
}
|
|
317
274
|
},
|
|
318
275
|
debounce: 300
|
|
319
276
|
});
|
|
320
277
|
}
|
|
321
278
|
|
|
322
|
-
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
this.editor = new EditorView({state, parent: container});
|
|
279
|
+
manageCodeEditor = textAreaComp => {
|
|
280
|
+
if (textAreaComp) {
|
|
281
|
+
this.editor = this.createCodeEditor(textAreaComp);
|
|
282
|
+
this.preserveSearchResults();
|
|
283
|
+
}
|
|
328
284
|
};
|
|
329
285
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
286
|
+
createCodeEditor(textAreaComp) {
|
|
287
|
+
const editorSpec = defaultsDeep(this.componentProps.editorProps, this.createDefaults());
|
|
288
|
+
|
|
289
|
+
const taDom = findDOMNode(textAreaComp),
|
|
290
|
+
editor = codemirror.fromTextArea(taDom, editorSpec);
|
|
291
|
+
|
|
292
|
+
editor.on('change', this.handleEditorChange);
|
|
293
|
+
return editor;
|
|
334
294
|
}
|
|
335
295
|
|
|
336
|
-
|
|
296
|
+
createDefaults() {
|
|
297
|
+
const {disabled, readonly, mode, linter, autoFocus} = this.componentProps;
|
|
298
|
+
let gutters = ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'];
|
|
299
|
+
if (linter) gutters.push('CodeMirror-lint-markers');
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
mode,
|
|
303
|
+
theme: XH.darkTheme ? 'dracula' : 'default',
|
|
304
|
+
lineWrapping: false,
|
|
305
|
+
lineNumbers: true,
|
|
306
|
+
autoCloseBrackets: true,
|
|
307
|
+
extraKeys: {
|
|
308
|
+
'Cmd-P': this.onAutoFormat,
|
|
309
|
+
'Ctrl-P': this.onAutoFormat
|
|
310
|
+
},
|
|
311
|
+
foldGutter: true,
|
|
312
|
+
scrollbarStyle: 'simple',
|
|
313
|
+
readOnly: disabled || readonly,
|
|
314
|
+
gutters,
|
|
315
|
+
lint: linter ? {getAnnotations: linter} : false,
|
|
316
|
+
autoFocus
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
onChange = ev => {
|
|
321
|
+
this.noteValueChange(ev.target.value);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
handleEditorChange = editor => {
|
|
325
|
+
this.noteValueChange(editor.getValue());
|
|
326
|
+
if (this.cursor) this.clearSearchResults();
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
onAutoFormat = () => {
|
|
330
|
+
if (!isFunction(this.componentProps.formatter)) return;
|
|
331
|
+
|
|
332
|
+
const editor = this.editor,
|
|
333
|
+
val = this.tryPrettyPrint(editor.getValue());
|
|
334
|
+
editor.setValue(val);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
tryPrettyPrint(str) {
|
|
337
338
|
try {
|
|
338
|
-
return this.componentProps.formatter
|
|
339
|
+
return this.componentProps.formatter(str);
|
|
339
340
|
} catch (e) {
|
|
340
341
|
return str;
|
|
341
342
|
}
|
|
@@ -343,168 +344,106 @@ class CodeInputModel extends HoistInputModel {
|
|
|
343
344
|
|
|
344
345
|
toggleFullScreen() {
|
|
345
346
|
this.modalSupportModel.toggleIsModal();
|
|
347
|
+
|
|
348
|
+
// 'Nudge' the mouse wheel to trigger CodeMirror to update scrollbar state
|
|
349
|
+
const scrollEvent = d => new window.WheelEvent('mousewheel', {deltaX: d, deltaY: d});
|
|
350
|
+
wait().then(() => {
|
|
351
|
+
this.editor.getScrollerElement().dispatchEvent(scrollEvent(2));
|
|
352
|
+
this.editor.getScrollerElement().dispatchEvent(scrollEvent(-2));
|
|
353
|
+
});
|
|
346
354
|
}
|
|
347
355
|
|
|
356
|
+
//------------------------
|
|
357
|
+
// Local Searching
|
|
358
|
+
//------------------------
|
|
348
359
|
@action
|
|
349
360
|
findAll() {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
this.editor.dispatch({
|
|
366
|
-
selection: {anchor: match.from, head: match.to},
|
|
367
|
-
scrollIntoView: true
|
|
361
|
+
this.clearSearchResults();
|
|
362
|
+
if (!this.query?.trim()) return;
|
|
363
|
+
|
|
364
|
+
this.cursor = this.editor.getSearchCursor(this.query, 0, true);
|
|
365
|
+
|
|
366
|
+
const {cursor, editor} = this,
|
|
367
|
+
newMatches = [];
|
|
368
|
+
|
|
369
|
+
while (cursor.findNext()) {
|
|
370
|
+
const anchor = cursor.from(),
|
|
371
|
+
head = cursor.to();
|
|
372
|
+
newMatches.push({
|
|
373
|
+
anchor,
|
|
374
|
+
head,
|
|
375
|
+
textMarker: editor.markText(anchor, head, {className: 'xh-code-input--highlight'})
|
|
368
376
|
});
|
|
369
377
|
}
|
|
378
|
+
|
|
379
|
+
this.matches = newMatches;
|
|
380
|
+
if (newMatches.length) {
|
|
381
|
+
this.findNext();
|
|
382
|
+
} else {
|
|
383
|
+
this.currentMatchIdx = -1;
|
|
384
|
+
}
|
|
370
385
|
}
|
|
371
386
|
|
|
372
387
|
@action
|
|
373
388
|
findNext() {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
389
|
+
const {editor, query, cursor, matchCount} = this;
|
|
390
|
+
if (!cursor || !matchCount) return;
|
|
391
|
+
|
|
392
|
+
if (cursor.findNext(query)) {
|
|
393
|
+
this.handleCursorMatchUpdate();
|
|
394
|
+
} else {
|
|
395
|
+
// Loop around
|
|
396
|
+
this.cursor = editor.getSearchCursor(query, 0, true);
|
|
397
|
+
this.findNext();
|
|
398
|
+
}
|
|
381
399
|
}
|
|
382
400
|
|
|
383
401
|
@action
|
|
384
402
|
findPrevious() {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
403
|
+
const {editor, query, cursor, matches, matchCount} = this;
|
|
404
|
+
if (!cursor || !matchCount) return;
|
|
405
|
+
|
|
406
|
+
if (cursor.findPrevious(query)) {
|
|
407
|
+
this.handleCursorMatchUpdate();
|
|
408
|
+
} else {
|
|
409
|
+
// Loop around
|
|
410
|
+
this.cursor = editor.getSearchCursor(query, matches[matchCount - 1].head, true);
|
|
411
|
+
this.findPrevious();
|
|
412
|
+
}
|
|
393
413
|
}
|
|
394
414
|
|
|
395
415
|
@action
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
416
|
+
handleCursorMatchUpdate() {
|
|
417
|
+
const {editor, cursor, matches} = this,
|
|
418
|
+
from = cursor.from(),
|
|
419
|
+
to = cursor.to();
|
|
420
|
+
editor.scrollIntoView({from, to}, 50);
|
|
421
|
+
editor.setSelection(from, to);
|
|
422
|
+
this.currentMatchIdx = matches.findIndex(match => isEqual(match.anchor, from));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
preserveSearchResults() {
|
|
426
|
+
const {matches, editor} = this;
|
|
427
|
+
matches.forEach(match => {
|
|
428
|
+
match.textMarker = editor.markText(match.anchor, match.head, {
|
|
429
|
+
className: 'xh-code-input--highlight'
|
|
430
|
+
});
|
|
431
|
+
});
|
|
399
432
|
}
|
|
400
433
|
|
|
401
434
|
@action
|
|
402
435
|
clearSearchResults() {
|
|
403
|
-
this.
|
|
436
|
+
this.cursor = null;
|
|
404
437
|
this.currentMatchIdx = -1;
|
|
405
|
-
this.
|
|
438
|
+
this.matches.forEach(match => match.textMarker.clear());
|
|
439
|
+
this.matches = [];
|
|
406
440
|
}
|
|
407
441
|
|
|
408
442
|
override destroy() {
|
|
409
|
-
|
|
443
|
+
// Cleanup editor component as per CodeMirror docs.
|
|
444
|
+
if (this.editor) this.editor.toTextArea();
|
|
410
445
|
super.destroy();
|
|
411
446
|
}
|
|
412
|
-
|
|
413
|
-
//------------------------
|
|
414
|
-
// Implementation
|
|
415
|
-
//------------------------
|
|
416
|
-
private async getExtensionsAsync(): Promise<Extension[]> {
|
|
417
|
-
const {
|
|
418
|
-
autoFocus,
|
|
419
|
-
language,
|
|
420
|
-
readonly,
|
|
421
|
-
highlightActiveLine: propsHighlightActiveLine,
|
|
422
|
-
linter: propsLinter,
|
|
423
|
-
lineNumbers: propsLineNumbers = true,
|
|
424
|
-
lineWrapping: propsLineWrapping = false
|
|
425
|
-
} = this.componentProps,
|
|
426
|
-
extensions = [
|
|
427
|
-
// Theme
|
|
428
|
-
this.themeCompartment.of(this.getThemeExtension()),
|
|
429
|
-
// Editor state
|
|
430
|
-
EditorView.editable.of(!readonly),
|
|
431
|
-
EditorView.updateListener.of((update: ViewUpdate) => {
|
|
432
|
-
if (update.docChanged) this.noteValueChange(update.state.doc.toString());
|
|
433
|
-
}),
|
|
434
|
-
// Search & custom highlight
|
|
435
|
-
search(),
|
|
436
|
-
syntaxHighlighting(defaultHighlightStyle),
|
|
437
|
-
highlightSelectionMatches(),
|
|
438
|
-
this.highlightField,
|
|
439
|
-
// Editor UI
|
|
440
|
-
foldGutter(),
|
|
441
|
-
lintGutter(),
|
|
442
|
-
indentOnInput(),
|
|
443
|
-
autocompletion(),
|
|
444
|
-
history(),
|
|
445
|
-
// Linter
|
|
446
|
-
propsLinter
|
|
447
|
-
? linter(async view => {
|
|
448
|
-
const text = view.state.doc.toString();
|
|
449
|
-
return await propsLinter(text);
|
|
450
|
-
})
|
|
451
|
-
: [],
|
|
452
|
-
// Key bindings
|
|
453
|
-
keymap.of([
|
|
454
|
-
...defaultKeymap,
|
|
455
|
-
...historyKeymap,
|
|
456
|
-
...foldKeymap,
|
|
457
|
-
indentWithTab,
|
|
458
|
-
{
|
|
459
|
-
key: 'Mod-p',
|
|
460
|
-
run: () => {
|
|
461
|
-
this.onAutoFormat();
|
|
462
|
-
return true;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
])
|
|
466
|
-
];
|
|
467
|
-
|
|
468
|
-
if (propsLineWrapping) extensions.push(EditorView.lineWrapping);
|
|
469
|
-
if (propsLineNumbers) {
|
|
470
|
-
isObject(propsLineNumbers)
|
|
471
|
-
? extensions.push(lineNumbers(propsLineNumbers))
|
|
472
|
-
: extensions.push(lineNumbers());
|
|
473
|
-
}
|
|
474
|
-
if (propsHighlightActiveLine)
|
|
475
|
-
extensions.push(highlightActiveLine(), highlightActiveLineGutter());
|
|
476
|
-
if (autoFocus) extensions.push(this.autofocusExtension);
|
|
477
|
-
if (language) extensions.push(await this.getLanguageExtensionAsync(language));
|
|
478
|
-
|
|
479
|
-
return extensions.filter(it => !isEmpty(it));
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
private getThemeExtension() {
|
|
483
|
-
const lightTheme = EditorView.theme({}, {dark: false});
|
|
484
|
-
return XH.darkTheme ? oneDark : lightTheme;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
private async getLanguageExtensionAsync(lang: string): Promise<LanguageSupport> {
|
|
488
|
-
try {
|
|
489
|
-
const langDesc: LanguageDescription | undefined = find(
|
|
490
|
-
languages,
|
|
491
|
-
it => includes(it.alias, lang) || it.name.toLowerCase() === lang.toLowerCase()
|
|
492
|
-
);
|
|
493
|
-
if (!langDesc) return null;
|
|
494
|
-
return await langDesc.load();
|
|
495
|
-
} catch (err) {
|
|
496
|
-
console.error(`Failed to load language: ${lang}`, err);
|
|
497
|
-
return null;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
private autofocusExtension = ViewPlugin.fromClass(
|
|
502
|
-
class {
|
|
503
|
-
constructor(view: EditorView) {
|
|
504
|
-
queueMicrotask(() => view.focus());
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
);
|
|
508
447
|
}
|
|
509
448
|
|
|
510
449
|
const cmp = hoistCmp.factory<CodeInputModel>(({model, className, ...props}, ref) => {
|
|
@@ -532,7 +471,11 @@ const inputCmp = hoistCmp.factory<CodeInputModel>(({model, ...props}, ref) =>
|
|
|
532
471
|
items: [
|
|
533
472
|
div({
|
|
534
473
|
className: 'xh-code-input__inner-wrapper',
|
|
535
|
-
|
|
474
|
+
item: textArea({
|
|
475
|
+
value: model.renderValue || '',
|
|
476
|
+
inputRef: model.manageCodeEditor,
|
|
477
|
+
onChange: model.onChange
|
|
478
|
+
})
|
|
536
479
|
}),
|
|
537
480
|
model.showToolbar ? toolbarCmp() : actionButtonsCmp()
|
|
538
481
|
],
|
|
@@ -553,9 +496,7 @@ const toolbarCmp = hoistCmp.factory<CodeInputModel>(({model}) => {
|
|
|
553
496
|
});
|
|
554
497
|
|
|
555
498
|
const searchInputCmp = hoistCmp.factory<CodeInputModel>(({model}) => {
|
|
556
|
-
const {query, currentMatchIdx,
|
|
557
|
-
matchCount = matches.length;
|
|
558
|
-
|
|
499
|
+
const {query, cursor, currentMatchIdx, matchCount, fullScreen} = model;
|
|
559
500
|
return fragment(
|
|
560
501
|
// Frame wrapper added due to issues with textInput not supporting all layout props as it should.
|
|
561
502
|
frame({
|
|
@@ -564,16 +505,20 @@ const searchInputCmp = hoistCmp.factory<CodeInputModel>(({model}) => {
|
|
|
564
505
|
item: textInput({
|
|
565
506
|
width: null,
|
|
566
507
|
flex: 1,
|
|
567
|
-
model,
|
|
508
|
+
model: this,
|
|
568
509
|
bind: 'query',
|
|
569
510
|
leftIcon: Icon.search(),
|
|
570
511
|
enableClear: true,
|
|
571
512
|
commitOnChange: true,
|
|
572
513
|
onKeyDown: e => {
|
|
573
514
|
if (e.key !== 'Enter') return;
|
|
574
|
-
if (!
|
|
575
|
-
|
|
576
|
-
else
|
|
515
|
+
if (!cursor) {
|
|
516
|
+
model.findAll();
|
|
517
|
+
} else if (e.shiftKey) {
|
|
518
|
+
model.findPrevious();
|
|
519
|
+
} else {
|
|
520
|
+
model.findNext();
|
|
521
|
+
}
|
|
577
522
|
}
|
|
578
523
|
})
|
|
579
524
|
}),
|