@xh/hoist 79.0.0-SNAPSHOT.1765828486265 → 79.0.0-SNAPSHOT.1765833120742
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 +26 -0
- 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 +9 -21
- package/build/types/desktop/cmp/input/JsonInput.d.ts +15 -3
- package/build/types/desktop/cmp/input/impl/one-dark.d.ts +23 -0
- package/core/HoistBase.ts +1 -7
- package/desktop/cmp/filechooser/FileChooserModel.ts +1 -2
- package/desktop/cmp/input/CodeInput.scss +21 -15
- package/desktop/cmp/input/CodeInput.ts +263 -194
- package/desktop/cmp/input/JsonInput.ts +126 -22
- package/desktop/cmp/input/impl/one-dark.ts +163 -0
- package/package.json +13 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -4,15 +4,27 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {hoistCmp} from '@xh/hoist/core';
|
|
7
|
+
import {hoistCmp, PlainObject} from '@xh/hoist/core';
|
|
8
8
|
import '@xh/hoist/desktop/register';
|
|
9
9
|
import {fmtJson} from '@xh/hoist/format';
|
|
10
|
-
import
|
|
11
|
-
import 'codemirror/mode/javascript/javascript';
|
|
10
|
+
import Ajv, {Options, SchemaObject, ValidateFunction} from 'ajv';
|
|
12
11
|
import {codeInput, CodeInputProps} from './CodeInput';
|
|
13
|
-
import {jsonlint} from './impl/jsonlint';
|
|
12
|
+
import {jsonlint} from './impl/jsonlint.js';
|
|
14
13
|
|
|
15
|
-
export
|
|
14
|
+
export interface JsonInputProps extends CodeInputProps {
|
|
15
|
+
/**
|
|
16
|
+
* JSON Schema object used to validate the input JSON. Accepts any valid JSON Schema keywords
|
|
17
|
+
* supported by AJV, such as `type`, `properties`, `required`, and `additionalProperties`.
|
|
18
|
+
* @see https://ajv.js.org/json-schema.html
|
|
19
|
+
*/
|
|
20
|
+
jsonSchema?: SchemaObject;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Configuration object with any properties supported by the AJV API.
|
|
24
|
+
* @see {@link https://ajv.js.org/options.html}
|
|
25
|
+
*/
|
|
26
|
+
ajvProps?: Options;
|
|
27
|
+
}
|
|
16
28
|
|
|
17
29
|
/**
|
|
18
30
|
* Code-editor style input for editing and validating JSON, powered by CodeMirror.
|
|
@@ -21,11 +33,13 @@ export const [JsonInput, jsonInput] = hoistCmp.withFactory<JsonInputProps>({
|
|
|
21
33
|
displayName: 'JsonInput',
|
|
22
34
|
className: 'xh-json-input',
|
|
23
35
|
render(props, ref) {
|
|
36
|
+
const {jsonSchema, ajvProps, ...rest} = props;
|
|
37
|
+
|
|
24
38
|
return codeInput({
|
|
25
|
-
linter:
|
|
39
|
+
linter: jsonLinterWrapper(jsonSchema, ajvProps),
|
|
26
40
|
formatter: fmtJson,
|
|
27
|
-
|
|
28
|
-
...
|
|
41
|
+
language: 'json',
|
|
42
|
+
...rest,
|
|
29
43
|
ref
|
|
30
44
|
});
|
|
31
45
|
}
|
|
@@ -33,24 +47,114 @@ export const [JsonInput, jsonInput] = hoistCmp.withFactory<JsonInputProps>({
|
|
|
33
47
|
(JsonInput as any).hasLayoutSupport = true;
|
|
34
48
|
|
|
35
49
|
//----------------------
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
if (!
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
// JSON Linter helper
|
|
51
|
+
//----------------------
|
|
52
|
+
function jsonLinterWrapper(jsonSchema?: PlainObject, ajvProps?: Options) {
|
|
53
|
+
// No schema → only use JSONLint
|
|
54
|
+
if (!jsonSchema) return jsonLintOnly;
|
|
55
|
+
|
|
56
|
+
const ajv = new Ajv({...ajvProps}),
|
|
57
|
+
validate = ajv.compile(jsonSchema);
|
|
58
|
+
|
|
59
|
+
return (text: string) => {
|
|
60
|
+
const annotations: any[] = [];
|
|
61
|
+
|
|
62
|
+
if (!text.trim()) return annotations;
|
|
63
|
+
|
|
64
|
+
runJsonLint(text, annotations);
|
|
65
|
+
if (annotations.length) return annotations;
|
|
66
|
+
|
|
67
|
+
runAjvValidation(text, validate, annotations);
|
|
68
|
+
|
|
69
|
+
return annotations;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Run JSONLint and append errors to annotations */
|
|
74
|
+
function runJsonLint(text: string, annotations: any[]) {
|
|
75
|
+
jsonlint.parseError = (message, hash) => {
|
|
76
|
+
const {first_line, first_column, last_line, last_column} = hash.loc;
|
|
77
|
+
annotations.push({
|
|
78
|
+
from: indexFromLineCol(text, first_line, first_column),
|
|
79
|
+
to: indexFromLineCol(text, last_line, last_column),
|
|
80
|
+
message,
|
|
81
|
+
severity: 'error'
|
|
48
82
|
});
|
|
49
83
|
};
|
|
50
84
|
|
|
51
85
|
try {
|
|
52
86
|
jsonlint.parse(text);
|
|
53
|
-
} catch
|
|
87
|
+
} catch {
|
|
88
|
+
// intentionally ignored: parseError handles reporting
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Run AJV schema validation and append errors to annotations */
|
|
93
|
+
function runAjvValidation(text: string, validate: ValidateFunction, annotations: any[]) {
|
|
94
|
+
let data: any;
|
|
95
|
+
try {
|
|
96
|
+
data = JSON.parse(text);
|
|
97
|
+
} catch {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const valid = validate(data);
|
|
102
|
+
if (valid || !validate.errors) return;
|
|
103
|
+
|
|
104
|
+
validate.errors.forEach(err => {
|
|
105
|
+
const {from, to} = getErrorPosition(err, text),
|
|
106
|
+
message = formatAjvMessage(err);
|
|
107
|
+
|
|
108
|
+
annotations.push({from, to, message, severity: 'error'});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Determine text positions for AJV error highlighting */
|
|
113
|
+
function getErrorPosition(err: any, text: string): {from: number; to: number} {
|
|
114
|
+
let from = 0,
|
|
115
|
+
to = 0,
|
|
116
|
+
key: string;
|
|
117
|
+
|
|
118
|
+
if (err.keyword === 'additionalProperties' && err.params?.additionalProperty) {
|
|
119
|
+
key = err.params.additionalProperty;
|
|
120
|
+
} else {
|
|
121
|
+
const parts = (err.instancePath || '').split('/').filter(Boolean);
|
|
122
|
+
key = parts[parts.length - 1];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (key) {
|
|
126
|
+
const idx = text.indexOf(`"${key}"`);
|
|
127
|
+
if (idx >= 0) {
|
|
128
|
+
from = idx;
|
|
129
|
+
to = idx + key.length + 2;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {from, to};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Format AJV error messages nicely */
|
|
137
|
+
function formatAjvMessage(err: any): string {
|
|
138
|
+
const path = err.instancePath || '(root)';
|
|
139
|
+
if (err.keyword === 'additionalProperties' && err.params?.additionalProperty) {
|
|
140
|
+
return `Unexpected property "${err.params.additionalProperty}"`;
|
|
141
|
+
}
|
|
142
|
+
return `${path} ${err.message}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** JSONLint-only linter (used when no jsonSchema prop) */
|
|
146
|
+
function jsonLintOnly(text: string) {
|
|
147
|
+
const annotations: any[] = [];
|
|
148
|
+
if (!text) return annotations;
|
|
149
|
+
|
|
150
|
+
runJsonLint(text, annotations);
|
|
151
|
+
return annotations;
|
|
152
|
+
}
|
|
54
153
|
|
|
55
|
-
|
|
154
|
+
/** Convert line/col to string index */
|
|
155
|
+
function indexFromLineCol(text: string, line: number, col: number): number {
|
|
156
|
+
const lines = text.split('\n');
|
|
157
|
+
let idx = 0;
|
|
158
|
+
for (let i = 0; i < line - 1; i++) idx += lines[i].length + 1;
|
|
159
|
+
return idx + col;
|
|
56
160
|
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {EditorView} from '@codemirror/view';
|
|
2
|
+
import {Extension} from '@codemirror/state';
|
|
3
|
+
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language';
|
|
4
|
+
import {tags as t} from '@lezer/highlight';
|
|
5
|
+
|
|
6
|
+
// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors
|
|
7
|
+
|
|
8
|
+
const chalky = '#e5c07b',
|
|
9
|
+
coral = '#e06c75',
|
|
10
|
+
cyan = '#56b6c2',
|
|
11
|
+
invalid = '#ffffff',
|
|
12
|
+
ivory = '#abb2bf',
|
|
13
|
+
stone = '#7d8799', // Brightened compared to original to increase contrast
|
|
14
|
+
malibu = '#61afef',
|
|
15
|
+
sage = '#98c379',
|
|
16
|
+
whiskey = '#d19a66',
|
|
17
|
+
violet = '#c678dd',
|
|
18
|
+
darkBackground = '#21252b',
|
|
19
|
+
highlightBackground = '#2c313a',
|
|
20
|
+
background = '#282c34',
|
|
21
|
+
tooltipBackground = '#353a42',
|
|
22
|
+
selection = '#3E4451',
|
|
23
|
+
cursor = '#528bff';
|
|
24
|
+
|
|
25
|
+
/// The colors used in the theme, as CSS color strings.
|
|
26
|
+
export const color = {
|
|
27
|
+
chalky,
|
|
28
|
+
coral,
|
|
29
|
+
cyan,
|
|
30
|
+
invalid,
|
|
31
|
+
ivory,
|
|
32
|
+
stone,
|
|
33
|
+
malibu,
|
|
34
|
+
sage,
|
|
35
|
+
whiskey,
|
|
36
|
+
violet,
|
|
37
|
+
darkBackground,
|
|
38
|
+
highlightBackground,
|
|
39
|
+
background,
|
|
40
|
+
tooltipBackground,
|
|
41
|
+
selection,
|
|
42
|
+
cursor
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/// The editor theme styles for One Dark.
|
|
46
|
+
export const oneDarkTheme = EditorView.theme(
|
|
47
|
+
{
|
|
48
|
+
'&': {
|
|
49
|
+
color: ivory,
|
|
50
|
+
backgroundColor: background
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
'.cm-content': {
|
|
54
|
+
caretColor: cursor
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
'.cm-cursor, .cm-dropCursor': {borderLeftColor: cursor},
|
|
58
|
+
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
|
|
59
|
+
{backgroundColor: selection},
|
|
60
|
+
|
|
61
|
+
'.cm-panels': {backgroundColor: darkBackground, color: ivory},
|
|
62
|
+
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
|
|
63
|
+
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
|
|
64
|
+
|
|
65
|
+
'.cm-searchMatch': {
|
|
66
|
+
backgroundColor: '#72a1ff59',
|
|
67
|
+
outline: '1px solid #457dff'
|
|
68
|
+
},
|
|
69
|
+
'.cm-searchMatch.cm-searchMatch-selected': {
|
|
70
|
+
backgroundColor: '#6199ff2f'
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
'.cm-activeLine': {backgroundColor: '#6699ff0b'},
|
|
74
|
+
'.cm-selectionMatch': {backgroundColor: '#aafe661a'},
|
|
75
|
+
|
|
76
|
+
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
|
77
|
+
backgroundColor: '#bad0f847'
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
'.cm-gutters': {
|
|
81
|
+
backgroundColor: background,
|
|
82
|
+
color: stone,
|
|
83
|
+
border: 'none'
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
'.cm-activeLineGutter': {
|
|
87
|
+
backgroundColor: highlightBackground
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
'.cm-foldPlaceholder': {
|
|
91
|
+
backgroundColor: 'transparent',
|
|
92
|
+
border: 'none',
|
|
93
|
+
color: '#ddd'
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
'.cm-tooltip': {
|
|
97
|
+
border: 'none',
|
|
98
|
+
backgroundColor: tooltipBackground
|
|
99
|
+
},
|
|
100
|
+
'.cm-tooltip .cm-tooltip-arrow:before': {
|
|
101
|
+
borderTopColor: 'transparent',
|
|
102
|
+
borderBottomColor: 'transparent'
|
|
103
|
+
},
|
|
104
|
+
'.cm-tooltip .cm-tooltip-arrow:after': {
|
|
105
|
+
borderTopColor: tooltipBackground,
|
|
106
|
+
borderBottomColor: tooltipBackground
|
|
107
|
+
},
|
|
108
|
+
'.cm-tooltip-autocomplete': {
|
|
109
|
+
'& > ul > li[aria-selected]': {
|
|
110
|
+
backgroundColor: highlightBackground,
|
|
111
|
+
color: ivory
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
{dark: true}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
/// The highlighting style for code in the One Dark theme.
|
|
119
|
+
export const oneDarkHighlightStyle = HighlightStyle.define([
|
|
120
|
+
{tag: t.keyword, color: violet},
|
|
121
|
+
{tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: coral},
|
|
122
|
+
{tag: [t.function(t.variableName), t.labelName], color: malibu},
|
|
123
|
+
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey},
|
|
124
|
+
{tag: [t.definition(t.name), t.separator], color: ivory},
|
|
125
|
+
{
|
|
126
|
+
tag: [
|
|
127
|
+
t.typeName,
|
|
128
|
+
t.className,
|
|
129
|
+
t.number,
|
|
130
|
+
t.changed,
|
|
131
|
+
t.annotation,
|
|
132
|
+
t.modifier,
|
|
133
|
+
t.self,
|
|
134
|
+
t.namespace
|
|
135
|
+
],
|
|
136
|
+
color: chalky
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
tag: [
|
|
140
|
+
t.operator,
|
|
141
|
+
t.operatorKeyword,
|
|
142
|
+
t.url,
|
|
143
|
+
t.escape,
|
|
144
|
+
t.regexp,
|
|
145
|
+
t.link,
|
|
146
|
+
t.special(t.string)
|
|
147
|
+
],
|
|
148
|
+
color: cyan
|
|
149
|
+
},
|
|
150
|
+
{tag: [t.meta, t.comment], color: stone},
|
|
151
|
+
{tag: t.strong, fontWeight: 'bold'},
|
|
152
|
+
{tag: t.emphasis, fontStyle: 'italic'},
|
|
153
|
+
{tag: t.strikethrough, textDecoration: 'line-through'},
|
|
154
|
+
{tag: t.link, color: stone, textDecoration: 'underline'},
|
|
155
|
+
{tag: t.heading, fontWeight: 'bold', color: coral},
|
|
156
|
+
{tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey},
|
|
157
|
+
{tag: [t.processingInstruction, t.string, t.inserted], color: sage},
|
|
158
|
+
{tag: t.invalid, color: invalid}
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
/// Extension to enable the One Dark theme (both the editor theme and
|
|
162
|
+
/// the highlight style).
|
|
163
|
+
export const oneDark: Extension = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "79.0.0-SNAPSHOT.
|
|
3
|
+
"version": "79.0.0-SNAPSHOT.1765833120742",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": "github:xh/hoist-react",
|
|
6
6
|
"homepage": "https://xh.io",
|
|
@@ -33,6 +33,16 @@
|
|
|
33
33
|
"@blueprintjs/core": "^5.10.5",
|
|
34
34
|
"@blueprintjs/datetime": "^5.3.7",
|
|
35
35
|
"@blueprintjs/datetime2": "^2.3.7",
|
|
36
|
+
"@codemirror/commands": "6.9.0",
|
|
37
|
+
"@codemirror/lang-css": "^6.3.1",
|
|
38
|
+
"@codemirror/lang-html": "^6.4.11",
|
|
39
|
+
"@codemirror/lang-javascript": "^6.2.4",
|
|
40
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
41
|
+
"@codemirror/lang-sql": "^6.10.0",
|
|
42
|
+
"@codemirror/language": "6.11.3",
|
|
43
|
+
"@codemirror/lint": "6.9.0",
|
|
44
|
+
"@codemirror/search": "6.5.11",
|
|
45
|
+
"@codemirror/view": "6.38.6",
|
|
36
46
|
"@fortawesome/fontawesome-pro": "^6.6.0",
|
|
37
47
|
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
|
38
48
|
"@fortawesome/pro-light-svg-icons": "^6.6.0",
|
|
@@ -43,9 +53,10 @@
|
|
|
43
53
|
"@onsenui/fastclick": "~1.1.1",
|
|
44
54
|
"@popperjs/core": "~2.11.0",
|
|
45
55
|
"@seznam/compose-react-refs": "~1.0.5",
|
|
56
|
+
"ajv": "~8.17.1",
|
|
46
57
|
"classnames": "~2.5.1",
|
|
47
58
|
"clipboard-copy": "~4.0.1",
|
|
48
|
-
"codemirror": "~
|
|
59
|
+
"codemirror": "~6.0.2",
|
|
49
60
|
"core-js": "3.x",
|
|
50
61
|
"debounce-promise": "~3.1.0",
|
|
51
62
|
"dompurify": "~3.3.0",
|