@appscode/design-system 2.17.70-alpha-1 → 2.17.70-alpha-4
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/package.json
CHANGED
package/vue-components/index.ts
CHANGED
|
@@ -50,7 +50,6 @@ import AcDropdownMenu from "./v3/dropdown/DropdownMenu.vue";
|
|
|
50
50
|
|
|
51
51
|
import AcEditor from "./v3/editor/Editor.vue";
|
|
52
52
|
import AcFilteredFileEditor from "./v3/editor/FilteredFileEditor.vue";
|
|
53
|
-
import AcLightweightEditor from "./v3/editor/LightweightEditor.vue";
|
|
54
53
|
import AcMonacoEditor from "./v3/editor/MonacoEditor.vue";
|
|
55
54
|
import AcResourceKeyValueEditor from "./v3/editor/ResourceKeyValueEditor.vue";
|
|
56
55
|
|
|
@@ -233,7 +232,6 @@ export {
|
|
|
233
232
|
AcDropdownMenu,
|
|
234
233
|
AcEditor,
|
|
235
234
|
AcFilteredFileEditor,
|
|
236
|
-
AcLightweightEditor,
|
|
237
235
|
AcMonacoEditor,
|
|
238
236
|
AcResourceKeyValueEditor,
|
|
239
237
|
AcFooterArea,
|
|
@@ -106,3 +106,19 @@ onMounted(() => {
|
|
|
106
106
|
</div>
|
|
107
107
|
</ac-input-text>
|
|
108
108
|
</template>
|
|
109
|
+
|
|
110
|
+
<style lang="scss">
|
|
111
|
+
.ac-single-input {
|
|
112
|
+
input.ac-input:read-only {
|
|
113
|
+
background-color: #eeeeee !important;
|
|
114
|
+
cursor: not-allowed !important;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
label.show-label::after {
|
|
118
|
+
background-color: #eeeeee !important;
|
|
119
|
+
}
|
|
120
|
+
:hover {
|
|
121
|
+
border-color: #cbd5e1 !important;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
</style>
|
|
@@ -4,7 +4,7 @@ import { computed, defineAsyncComponent, ref } from "vue";
|
|
|
4
4
|
import GridIcon from "../icons/GridIcon.vue";
|
|
5
5
|
|
|
6
6
|
export interface Props {
|
|
7
|
-
currentApp?: "console" | "db" | "platform" | "billing" | "selfhost" | "learn" | "grafana";
|
|
7
|
+
currentApp?: "console" | "db" | "platform" | "billing" | "selfhost" | "learn" | "grafana" | "observe";
|
|
8
8
|
baseUrl?: string;
|
|
9
9
|
activeOrganization?: string;
|
|
10
10
|
rootDomain?: string;
|
|
@@ -41,6 +41,13 @@ const appList = [
|
|
|
41
41
|
port: "3005",
|
|
42
42
|
sub_title: "Analyze your activities",
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
name: "observe",
|
|
46
|
+
icon_url: "https://cdn.appscode.com/images/products/observe/logos/observe.png",
|
|
47
|
+
title: "Observe",
|
|
48
|
+
port: "5992",
|
|
49
|
+
sub_title: "Observe your system",
|
|
50
|
+
},
|
|
44
51
|
{
|
|
45
52
|
name: "selfhost",
|
|
46
53
|
icon_url: "https://cdn.appscode.com/images/products/selfhost/logos/selfhost.svg",
|
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div :id="id" ref="editorRef" class="lightweight-editor" :style="style"></div>
|
|
3
|
-
</template>
|
|
4
|
-
|
|
5
|
-
<script setup lang="ts">
|
|
6
|
-
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
|
|
7
|
-
import { EditorState } from "@codemirror/state";
|
|
8
|
-
import { EditorView, keymap, lineNumbers, drawSelection, dropCursor } from "@codemirror/view";
|
|
9
|
-
import { foldGutter, syntaxHighlighting, defaultHighlightStyle, indentUnit } from "@codemirror/language";
|
|
10
|
-
import { defaultKeymap, insertTab, history, historyKeymap } from "@codemirror/commands";
|
|
11
|
-
import { search, searchKeymap as searchKeys, highlightSelectionMatches } from "@codemirror/search";
|
|
12
|
-
import { json } from "@codemirror/lang-json";
|
|
13
|
-
import { yaml } from "@codemirror/lang-yaml";
|
|
14
|
-
import { linter, lintGutter } from "@codemirror/lint";
|
|
15
|
-
import Ajv from "ajv";
|
|
16
|
-
import * as yamlParser from "js-yaml";
|
|
17
|
-
|
|
18
|
-
interface Props {
|
|
19
|
-
id?: string;
|
|
20
|
-
modelValue?: string;
|
|
21
|
-
language?: "json" | "yaml";
|
|
22
|
-
width?: string | number;
|
|
23
|
-
height?: string | number;
|
|
24
|
-
schema?: Record<string, any>;
|
|
25
|
-
enableSearch?: boolean;
|
|
26
|
-
readOnly?: boolean;
|
|
27
|
-
validate?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
31
|
-
id: "",
|
|
32
|
-
modelValue: "",
|
|
33
|
-
language: "yaml",
|
|
34
|
-
width: "100%",
|
|
35
|
-
schema: undefined,
|
|
36
|
-
height: "300px",
|
|
37
|
-
enableSearch: true,
|
|
38
|
-
readOnly: false,
|
|
39
|
-
validate: true,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const emit = defineEmits<{
|
|
43
|
-
"update:modelValue": [value: string];
|
|
44
|
-
"validation-error": [errors: any[]];
|
|
45
|
-
change: [value: string];
|
|
46
|
-
}>();
|
|
47
|
-
|
|
48
|
-
const editorRef = ref<HTMLElement>();
|
|
49
|
-
let editorView: EditorView | null = null;
|
|
50
|
-
let isUpdatingFromWithin = false;
|
|
51
|
-
const ajv = new Ajv({ allErrors: true });
|
|
52
|
-
|
|
53
|
-
const style = computed(() => ({
|
|
54
|
-
width: typeof props.width === "number" ? `${props.width}px` : props.width,
|
|
55
|
-
height: typeof props.height === "number" ? `${props.height}px` : props.height,
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
// Create linter for validation
|
|
59
|
-
const createLinter = () => {
|
|
60
|
-
return linter((view) => {
|
|
61
|
-
const diagnostics: any[] = [];
|
|
62
|
-
const content = view.state.doc.toString();
|
|
63
|
-
|
|
64
|
-
// Skip validation for empty content
|
|
65
|
-
if (!content.trim() || !props.validate) {
|
|
66
|
-
emit("validation-error", []);
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
let parsedContent: any;
|
|
72
|
-
|
|
73
|
-
// Parse content based on language for syntax validation
|
|
74
|
-
if (props.language === "json") {
|
|
75
|
-
parsedContent = JSON.parse(content);
|
|
76
|
-
} else {
|
|
77
|
-
parsedContent = yamlParser.load(content);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// If schema is provided, validate against it
|
|
81
|
-
if (props.schema && parsedContent !== null) {
|
|
82
|
-
const validate = ajv.compile(props.schema);
|
|
83
|
-
const valid = validate(parsedContent);
|
|
84
|
-
|
|
85
|
-
if (!valid && validate.errors) {
|
|
86
|
-
// Process all schema validation errors
|
|
87
|
-
validate.errors.forEach((error, index) => {
|
|
88
|
-
const errorPath = error.instancePath || error.schemaPath || "";
|
|
89
|
-
const line = getLineFromPath(content, errorPath);
|
|
90
|
-
const lineInfo = view.state.doc.line(Math.min(line, view.state.doc.lines));
|
|
91
|
-
|
|
92
|
-
// Try to find the specific position of the error within the line
|
|
93
|
-
const { from, to } = getErrorPosition(lineInfo, error, content);
|
|
94
|
-
|
|
95
|
-
diagnostics.push({
|
|
96
|
-
from,
|
|
97
|
-
to,
|
|
98
|
-
severity: "error",
|
|
99
|
-
message: `Schema validation: ${error.message}`,
|
|
100
|
-
source: `schema-validation-${index}`,
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Emit validation errors
|
|
105
|
-
emit("validation-error", validate.errors);
|
|
106
|
-
} else {
|
|
107
|
-
emit("validation-error", []);
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
// No schema errors if parsing succeeded
|
|
111
|
-
emit("validation-error", []);
|
|
112
|
-
}
|
|
113
|
-
} catch (parseError: any) {
|
|
114
|
-
// Handle syntax/parse errors
|
|
115
|
-
const line = getLineFromError(parseError);
|
|
116
|
-
const lineInfo = view.state.doc.line(Math.min(line, view.state.doc.lines));
|
|
117
|
-
|
|
118
|
-
// For syntax errors, try to find the specific position if possible
|
|
119
|
-
let from = lineInfo.from;
|
|
120
|
-
let to = lineInfo.to;
|
|
121
|
-
|
|
122
|
-
if (parseError.mark && parseError.mark.column !== undefined) {
|
|
123
|
-
// YAML errors often have column information
|
|
124
|
-
from = lineInfo.from + parseError.mark.column;
|
|
125
|
-
to = Math.min(from + 10, lineInfo.to); // Highlight a small range
|
|
126
|
-
} else if (parseError.message.includes("position")) {
|
|
127
|
-
// JSON errors might have position info
|
|
128
|
-
const posMatch = parseError.message.match(/position (\d+)/);
|
|
129
|
-
if (posMatch) {
|
|
130
|
-
const pos = parseInt(posMatch[1]);
|
|
131
|
-
from = Math.min(pos, content.length);
|
|
132
|
-
to = Math.min(from + 5, content.length);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
diagnostics.push({
|
|
137
|
-
from,
|
|
138
|
-
to,
|
|
139
|
-
severity: "error",
|
|
140
|
-
message: `Syntax error: ${parseError.message}`,
|
|
141
|
-
source: "syntax-error",
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
emit("validation-error", [{ message: `Syntax error: ${parseError.message}`, instancePath: "/" }]);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return diagnostics;
|
|
148
|
-
});
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
// Helper function to get line number from JSON path
|
|
152
|
-
const getLineFromPath = (content: string, path: string): number => {
|
|
153
|
-
const lines = content.split("\n");
|
|
154
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
155
|
-
|
|
156
|
-
if (pathParts.length === 0) return 1;
|
|
157
|
-
|
|
158
|
-
// Try to find the property/key mentioned in the path
|
|
159
|
-
const searchTerms = pathParts.slice(-2); // Get last two parts for better matching
|
|
160
|
-
|
|
161
|
-
for (let i = 0; i < lines.length; i++) {
|
|
162
|
-
const line = lines[i];
|
|
163
|
-
// Check if line contains any of the search terms
|
|
164
|
-
if (
|
|
165
|
-
searchTerms.some(
|
|
166
|
-
(term) =>
|
|
167
|
-
line.includes(`"${term}"`) || line.includes(`${term}:`) || line.includes(`'${term}'`) || line.includes(term),
|
|
168
|
-
)
|
|
169
|
-
) {
|
|
170
|
-
return i + 1;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return 1;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// Helper function to get specific error position within a line
|
|
177
|
-
const getErrorPosition = (lineInfo: any, error: any, content: string) => {
|
|
178
|
-
const lineText = content.slice(lineInfo.from, lineInfo.to);
|
|
179
|
-
|
|
180
|
-
// Try to find the specific field/property that has the error
|
|
181
|
-
if (error.instancePath) {
|
|
182
|
-
const pathParts = error.instancePath.split("/").filter(Boolean);
|
|
183
|
-
const fieldName = pathParts[pathParts.length - 1];
|
|
184
|
-
|
|
185
|
-
if (fieldName) {
|
|
186
|
-
// Look for the field name in the line
|
|
187
|
-
const fieldMatch = lineText.match(new RegExp(`["']?${fieldName}["']?\\s*:`));
|
|
188
|
-
if (fieldMatch) {
|
|
189
|
-
const matchStart = lineInfo.from + fieldMatch.index!;
|
|
190
|
-
const matchEnd = matchStart + fieldMatch[0].length;
|
|
191
|
-
return { from: matchStart, to: matchEnd };
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// If we can't find the specific field, highlight the whole line
|
|
197
|
-
return { from: lineInfo.from, to: lineInfo.to };
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
// Helper function to get line number from parse error
|
|
201
|
-
const getLineFromError = (error: any): number => {
|
|
202
|
-
if (error.mark && error.mark.line) {
|
|
203
|
-
return error.mark.line + 1;
|
|
204
|
-
}
|
|
205
|
-
// Try to extract line number from error message
|
|
206
|
-
const lineMatch = error.message.match(/line (\d+)/i);
|
|
207
|
-
if (lineMatch) {
|
|
208
|
-
return parseInt(lineMatch[1]);
|
|
209
|
-
}
|
|
210
|
-
return 1;
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// Create editor extensions
|
|
214
|
-
const createExtensions = () => {
|
|
215
|
-
const extensions = [
|
|
216
|
-
history(),
|
|
217
|
-
lineNumbers(),
|
|
218
|
-
foldGutter(),
|
|
219
|
-
drawSelection(),
|
|
220
|
-
dropCursor(),
|
|
221
|
-
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
222
|
-
indentUnit.of(" "), // Set indent to 2 spaces
|
|
223
|
-
keymap.of([
|
|
224
|
-
{ key: "Tab", run: insertTab },
|
|
225
|
-
...historyKeymap,
|
|
226
|
-
...defaultKeymap,
|
|
227
|
-
...(props.enableSearch ? searchKeys : []),
|
|
228
|
-
]),
|
|
229
|
-
EditorView.updateListener.of((update) => {
|
|
230
|
-
if (update.docChanged) {
|
|
231
|
-
isUpdatingFromWithin = true;
|
|
232
|
-
const newValue = update.state.doc.toString();
|
|
233
|
-
emit("update:modelValue", newValue);
|
|
234
|
-
emit("change", newValue);
|
|
235
|
-
// Reset flag after Vue has processed the update
|
|
236
|
-
nextTick(() => {
|
|
237
|
-
isUpdatingFromWithin = false;
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}),
|
|
241
|
-
];
|
|
242
|
-
|
|
243
|
-
// Add language support
|
|
244
|
-
if (props.language === "json") {
|
|
245
|
-
extensions.push(json());
|
|
246
|
-
} else {
|
|
247
|
-
extensions.push(yaml());
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Add search if enabled
|
|
251
|
-
if (props.enableSearch) {
|
|
252
|
-
extensions.push(search(), highlightSelectionMatches());
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Add linting - always enabled for syntax validation
|
|
256
|
-
extensions.push(lintGutter(), createLinter());
|
|
257
|
-
|
|
258
|
-
// Add read-only state - always add editable extension
|
|
259
|
-
extensions.push(EditorView.editable.of(!props.readOnly));
|
|
260
|
-
if (props.readOnly) {
|
|
261
|
-
extensions.push(EditorState.readOnly.of(true));
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return extensions;
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
const initializeEditor = async () => {
|
|
268
|
-
if (!editorRef.value) return;
|
|
269
|
-
|
|
270
|
-
const state = EditorState.create({
|
|
271
|
-
doc: props.modelValue,
|
|
272
|
-
extensions: createExtensions(),
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
editorView = new EditorView({
|
|
276
|
-
state,
|
|
277
|
-
parent: editorRef.value,
|
|
278
|
-
});
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
const updateEditor = () => {
|
|
282
|
-
if (!editorView) return;
|
|
283
|
-
|
|
284
|
-
// Skip update if the change is coming from within the editor
|
|
285
|
-
if (isUpdatingFromWithin) return;
|
|
286
|
-
|
|
287
|
-
const currentValue = editorView.state.doc.toString();
|
|
288
|
-
if (props.modelValue !== currentValue) {
|
|
289
|
-
editorView.dispatch({
|
|
290
|
-
changes: {
|
|
291
|
-
from: 0,
|
|
292
|
-
to: editorView.state.doc.length,
|
|
293
|
-
insert: props.modelValue,
|
|
294
|
-
},
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const recreateEditor = async () => {
|
|
300
|
-
if (editorView) {
|
|
301
|
-
editorView.destroy();
|
|
302
|
-
editorView = null;
|
|
303
|
-
}
|
|
304
|
-
await nextTick();
|
|
305
|
-
initializeEditor();
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
// Expose methods
|
|
309
|
-
const getEditor = () => editorView;
|
|
310
|
-
const focus = () => editorView?.focus();
|
|
311
|
-
const setValue = (value: string) => {
|
|
312
|
-
if (!editorView) return;
|
|
313
|
-
editorView.dispatch({
|
|
314
|
-
changes: {
|
|
315
|
-
from: 0,
|
|
316
|
-
to: editorView.state.doc.length,
|
|
317
|
-
insert: value,
|
|
318
|
-
},
|
|
319
|
-
});
|
|
320
|
-
};
|
|
321
|
-
const getValue = () => editorView?.state.doc.toString() || "";
|
|
322
|
-
|
|
323
|
-
defineExpose({
|
|
324
|
-
getEditor,
|
|
325
|
-
focus,
|
|
326
|
-
setValue,
|
|
327
|
-
getValue,
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
// Watchers
|
|
331
|
-
watch(() => props.modelValue, updateEditor);
|
|
332
|
-
watch(() => [props.language, props.schema, props.readOnly], recreateEditor);
|
|
333
|
-
|
|
334
|
-
onMounted(() => {
|
|
335
|
-
initializeEditor();
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
onUnmounted(() => {
|
|
339
|
-
if (editorView) {
|
|
340
|
-
editorView.destroy();
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
</script>
|