@conduction/nextcloud-vue 0.1.0-beta.11 → 0.1.0-beta.12

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.
Files changed (78) hide show
  1. package/dist/nextcloud-vue.cjs +67614 -0
  2. package/dist/nextcloud-vue.cjs.js +13518 -13617
  3. package/dist/nextcloud-vue.cjs.js.map +1 -1
  4. package/dist/nextcloud-vue.cjs.map +1 -0
  5. package/dist/nextcloud-vue.css +1796 -1800
  6. package/dist/nextcloud-vue.esm.js +13518 -13617
  7. package/dist/nextcloud-vue.esm.js.map +1 -1
  8. package/package.json +3 -2
  9. package/src/components/CnActionsBar/CnActionsBar.vue +254 -254
  10. package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +570 -570
  11. package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -217
  12. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -121
  13. package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +422 -422
  14. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -247
  15. package/src/components/CnCard/CnCard.vue +415 -415
  16. package/src/components/CnCardGrid/CnCardGrid.vue +156 -156
  17. package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
  18. package/src/components/CnChartWidget/CnChartWidget.vue +346 -346
  19. package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
  20. package/src/components/CnContextMenu/CnContextMenu.vue +142 -142
  21. package/src/components/CnCopyDialog/CnCopyDialog.vue +266 -266
  22. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +229 -229
  23. package/src/components/CnDashboardPage/CnDashboardPage.vue +397 -397
  24. package/src/components/CnDataTable/CnDataTable.vue +362 -362
  25. package/src/components/CnDeleteDialog/CnDeleteDialog.vue +177 -177
  26. package/src/components/CnDetailCard/CnDetailCard.vue +225 -225
  27. package/src/components/CnDetailGrid/CnDetailGrid.vue +256 -256
  28. package/src/components/CnDetailPage/CnDetailPage.vue +432 -432
  29. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +234 -234
  30. package/src/components/CnFilterBar/CnFilterBar.vue +153 -153
  31. package/src/components/CnFormDialog/CnFormDialog.vue +1047 -1047
  32. package/src/components/CnIcon/CnIcon.vue +89 -89
  33. package/src/components/CnIndexPage/CnIndexPage.vue +981 -980
  34. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +536 -536
  35. package/src/components/CnInfoWidget/CnInfoWidget.vue +219 -219
  36. package/src/components/CnItemCard/CnItemCard.vue +134 -134
  37. package/src/components/CnJsonViewer/CnJsonViewer.vue +312 -312
  38. package/src/components/CnKpiGrid/CnKpiGrid.vue +93 -93
  39. package/src/components/CnMassActionBar/CnMassActionBar.vue +161 -161
  40. package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +327 -327
  41. package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +245 -245
  42. package/src/components/CnMassExportDialog/CnMassExportDialog.vue +191 -191
  43. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +494 -494
  44. package/src/components/CnNoteCard/CnNoteCard.vue +149 -149
  45. package/src/components/CnNotesCard/CnNotesCard.vue +416 -416
  46. package/src/components/CnObjectCard/CnObjectCard.vue +294 -294
  47. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +854 -854
  48. package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +289 -289
  49. package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +369 -369
  50. package/src/components/CnObjectSidebar/CnFilesTab.vue +287 -287
  51. package/src/components/CnObjectSidebar/CnNotesTab.vue +250 -250
  52. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +255 -255
  53. package/src/components/CnObjectSidebar/CnTagsTab.vue +259 -259
  54. package/src/components/CnObjectSidebar/CnTasksTab.vue +483 -483
  55. package/src/components/CnPageHeader/CnPageHeader.vue +61 -61
  56. package/src/components/CnPagination/CnPagination.vue +253 -253
  57. package/src/components/CnProgressBar/CnProgressBar.vue +262 -262
  58. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +793 -793
  59. package/src/components/CnRowActions/CnRowActions.vue +95 -95
  60. package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -226
  61. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +788 -788
  62. package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -305
  63. package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -1398
  64. package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -236
  65. package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
  66. package/src/components/CnSettingsSection/CnSettingsSection.vue +267 -267
  67. package/src/components/CnStatsBlock/CnStatsBlock.vue +437 -437
  68. package/src/components/CnStatsPanel/CnStatsPanel.vue +321 -321
  69. package/src/components/CnStatusBadge/CnStatusBadge.vue +90 -90
  70. package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +545 -545
  71. package/src/components/CnTableWidget/CnTableWidget.vue +333 -333
  72. package/src/components/CnTasksCard/CnTasksCard.vue +374 -374
  73. package/src/components/CnTileWidget/CnTileWidget.vue +159 -159
  74. package/src/components/CnTimelineStages/CnTimelineStages.vue +294 -294
  75. package/src/components/CnUserActionMenu/CnUserActionMenu.vue +436 -436
  76. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +313 -313
  77. package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -180
  78. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +248 -248
@@ -1,312 +1,312 @@
1
- <!--
2
- CnJsonViewer — Syntax-highlighted code viewer/editor powered by CodeMirror.
3
-
4
- Supports multiple languages (JSON, XML, HTML, plain text) with optional
5
- auto-detection. Use `readOnly` for display-only mode.
6
- -->
7
- <template>
8
- <div class="cn-json-viewer">
9
- <div :class="['cn-json-viewer__codemirror', isDark ? 'cn-json-viewer__codemirror--dark' : 'cn-json-viewer__codemirror--light']">
10
- <CodeMirror
11
- v-model="localValue"
12
- :basic="true"
13
- :placeholder="resolvedLanguage === 'json' ? '{ &quot;key&quot;: &quot;value&quot; }' : ''"
14
- :dark="isDark"
15
- :readonly="readOnly"
16
- :linter="linterExtension"
17
- :lang="langExtension"
18
- :extensions="editorExtensions"
19
- :tab-size="2"
20
- :style="{ height }" />
21
- <NcButton
22
- v-if="!readOnly && resolvedLanguage === 'json'"
23
- class="cn-json-viewer__format-btn"
24
- type="secondary"
25
- size="small"
26
- @click="formatJson">
27
- Format JSON
28
- </NcButton>
29
- </div>
30
- <span v-if="shouldShowError" class="cn-json-viewer__error">
31
- {{ resolvedErrorText }}
32
- </span>
33
- </div>
34
- </template>
35
-
36
- <script>
37
- import { NcButton } from '@nextcloud/vue'
38
- import CodeMirror from 'vue-codemirror6'
39
- import { githubLight, githubDark } from '@uiw/codemirror-theme-github'
40
- import { json as jsonLang, jsonParseLinter as jsonLinter } from '@codemirror/lang-json'
41
- import { xml as xmlLang } from '@codemirror/lang-xml'
42
- import { html as htmlLang } from '@codemirror/lang-html'
43
- import { getTheme } from '../../utils/getTheme.js'
44
-
45
- /**
46
- * CnJsonViewer — Syntax-highlighted code viewer/editor.
47
- *
48
- * Wraps CodeMirror 6 with support for JSON, XML, HTML, and plain text.
49
- * Includes syntax highlighting, and optional formatting/validation for JSON.
50
- * Use `readOnly` for display-only mode.
51
- *
52
- * @example Read-only JSON display (default)
53
- * <CnJsonViewer :value="jsonString" :read-only="true" />
54
- *
55
- * @example Auto-detect language from content
56
- * <CnJsonViewer :value="responseBody" :read-only="true" language="auto" />
57
- *
58
- * @example Explicit XML mode
59
- * <CnJsonViewer :value="xmlString" :read-only="true" language="xml" />
60
- *
61
- * @example Editable JSON with custom height
62
- * <CnJsonViewer :value="jsonString" height="500px" @update:value="onUpdate" />
63
- */
64
- export default {
65
- name: 'CnJsonViewer',
66
-
67
- components: {
68
- NcButton,
69
- CodeMirror,
70
- },
71
-
72
- props: {
73
- /** JSON string to display or edit */
74
- value: { type: String, default: '' },
75
- /** When true, the editor is non-editable and hides format button and validation */
76
- readOnly: { type: Boolean, default: false },
77
- /** CSS height for the editor container */
78
- height: { type: String, default: '300px' },
79
- /**
80
- * Content language for syntax highlighting.
81
- * - 'auto': Auto-detect from content (JSON → xml → text) (default)
82
- * - 'json': JSON with validation and formatting
83
- * - 'xml': XML/HTML tag highlighting
84
- * - 'html': Alias for XML highlighting
85
- * - 'text': Plain text, no highlighting
86
- */
87
- language: {
88
- type: String,
89
- default: 'auto',
90
- validator: (v) => ['json', 'xml', 'html', 'text', 'auto'].includes(v),
91
- },
92
- /**
93
- * Custom text for the error banner rendered below the editor.
94
- * - `null` (default): the built-in "Invalid JSON format" banner renders
95
- * whenever `language === 'json'` and the content fails to parse.
96
- * - Any string: the caller owns the banner — it renders when this
97
- * string is non-empty, and is hidden when empty. Use this to surface
98
- * a richer parse error (e.g. the exception message).
99
- */
100
- errorText: { type: String, default: null },
101
- },
102
-
103
- data() {
104
- return {
105
- githubLight,
106
- githubDark,
107
- }
108
- },
109
-
110
- computed: {
111
- localValue: {
112
- get() { return this.value },
113
- set(v) { this.$emit('update:value', v) },
114
- },
115
- isDark: {
116
- get() { return getTheme() === 'dark' },
117
- },
118
- theme: {
119
- get() { return this.isDark ? githubDark : githubLight },
120
- },
121
- /**
122
- * Resolve 'auto' language to a concrete language based on content.
123
- * @return {string} Resolved language: 'json', 'xml', or 'text'
124
- */
125
- resolvedLanguage() {
126
- if (this.language !== 'auto') {
127
- return this.language
128
- }
129
- const trimmed = (this.value || '').trim()
130
- if (!trimmed) return 'text'
131
- try {
132
- JSON.parse(trimmed)
133
- return 'json'
134
- } catch {
135
- // not JSON
136
- }
137
- if (trimmed.charAt(0) === '<' && trimmed.includes('>')) {
138
- // Detect HTML by doctype or common HTML tags
139
- if (/<!doctype\s+html/i.test(trimmed) || /<(?:html|head|body|div|span|p|a|script|style|link|meta|form|input|button|table|ul|ol|li|h[1-6]|img|nav|header|footer|main|section|article)\b/i.test(trimmed)) {
140
- return 'html'
141
- }
142
- return 'xml'
143
- }
144
- return 'text'
145
- },
146
- /**
147
- * CodeMirror language extension based on resolved language.
148
- * @return {object|null} Language extension or null for plain text
149
- */
150
- langExtension() {
151
- switch (this.resolvedLanguage) {
152
- case 'json':
153
- return jsonLang()
154
- case 'html':
155
- return htmlLang()
156
- case 'xml':
157
- return xmlLang()
158
- case 'text':
159
- default:
160
- return null
161
- }
162
- },
163
- /**
164
- * CodeMirror linter extension (only active for JSON in edit mode).
165
- * @return {object|null} Linter extension or null
166
- */
167
- linterExtension() {
168
- if (this.readOnly) return null
169
- if (this.resolvedLanguage === 'json') return jsonLinter()
170
- return null
171
- },
172
- /**
173
- * Combined CodeMirror extensions array.
174
- * @return {Array} Extensions including theme and optional language
175
- */
176
- editorExtensions() {
177
- const exts = [this.theme]
178
- if (this.langExtension) exts.push(this.langExtension)
179
- return exts
180
- },
181
- /**
182
- * Error text displayed in the banner. Caller-provided `errorText` wins;
183
- * otherwise falls back to the built-in "Invalid JSON format" message.
184
- * @return {string} Message to show.
185
- */
186
- resolvedErrorText() {
187
- if (this.errorText !== null) return this.errorText
188
- return 'Invalid JSON format'
189
- },
190
- /**
191
- * Whether to show the error banner.
192
- * - If `errorText` is supplied, the caller controls visibility via its
193
- * emptiness.
194
- * - Otherwise, show iff the content is editable JSON and fails to parse.
195
- * @return {boolean} Visibility flag.
196
- */
197
- shouldShowError() {
198
- if (this.errorText !== null) return this.errorText !== ''
199
- return !this.readOnly
200
- && this.resolvedLanguage === 'json'
201
- && !this.isValidJson(this.value)
202
- },
203
- },
204
-
205
- methods: {
206
- /**
207
- * Format the current JSON value with 2-space indentation.
208
- * Emits 'update:value' with the formatted string and 'format' with the parsed object.
209
- */
210
- formatJson() {
211
- try {
212
- if (this.value) {
213
- const parsed = JSON.parse(this.value)
214
- this.$emit('update:value', JSON.stringify(parsed, null, 2))
215
- this.$emit('format', parsed)
216
- }
217
- } catch {
218
- // Keep invalid JSON as-is
219
- }
220
- },
221
-
222
- /**
223
- * Check if a string is valid JSON.
224
- * @param {string} str - String to validate
225
- * @return {boolean} True if valid JSON
226
- */
227
- isValidJson(str) {
228
- if (!str || !str.trim()) return false
229
- try {
230
- JSON.parse(str)
231
- return true
232
- } catch {
233
- return false
234
- }
235
- },
236
- },
237
- }
238
- </script>
239
-
240
- <style scoped>
241
- .cn-json-viewer {
242
- display: flex;
243
- flex-direction: column;
244
- gap: 8px;
245
- }
246
-
247
- .cn-json-viewer__codemirror {
248
- position: relative;
249
- }
250
-
251
- .cn-json-viewer__codemirror :deep(.cm-editor) {
252
- height: 100%;
253
- outline: none !important;
254
- }
255
-
256
- .cn-json-viewer__codemirror :deep(.cm-scroller) {
257
- overflow: auto;
258
- }
259
-
260
- .cn-json-viewer__codemirror :deep(.cm-content) {
261
- border-radius: 0 !important;
262
- border: none !important;
263
- }
264
-
265
- .cn-json-viewer__codemirror--light > .vue-codemirror {
266
- border: 1px dotted silver;
267
- }
268
-
269
- .cn-json-viewer__codemirror--dark > .vue-codemirror {
270
- border: 1px dotted grey;
271
- }
272
-
273
- /* Text cursor */
274
- .cn-json-viewer__codemirror :deep(.cm-content) * {
275
- cursor: text !important;
276
- }
277
-
278
- /* PATCH SELECTION COLOR - default selection system does not work */
279
- /* Selection background — CodeMirror uses .cm-selectionBackground instead of ::selection */
280
- .cn-json-viewer__codemirror--light :deep(.cm-selectionBackground) {
281
- background: #3390ff !important;
282
- }
283
-
284
- .cn-json-viewer__codemirror--light :deep(.cm-selectionBackground) + .cm-selectionBackground,
285
- .cn-json-viewer__codemirror--light :deep(.cm-line ::selection) {
286
- color: white !important;
287
- }
288
-
289
- .cn-json-viewer__codemirror--dark :deep(.cm-selectionBackground) {
290
- background: #3390ff !important;
291
- }
292
-
293
- /* Selected text color */
294
- .cn-json-viewer__codemirror--light :deep(.cm-content ::selection) {
295
- background: #3390ff !important;
296
- color: white !important;
297
- }
298
-
299
- .cn-json-viewer__codemirror--dark :deep(.cm-content ::selection) {
300
- background: #3390ff !important;
301
- color: white !important;
302
- }
303
-
304
- .cn-json-viewer__format-btn {
305
- margin-top: 8px;
306
- }
307
-
308
- .cn-json-viewer__error {
309
- color: var(--color-error);
310
- font-size: 14px;
311
- }
312
- </style>
1
+ <!--
2
+ CnJsonViewer — Syntax-highlighted code viewer/editor powered by CodeMirror.
3
+
4
+ Supports multiple languages (JSON, XML, HTML, plain text) with optional
5
+ auto-detection. Use `readOnly` for display-only mode.
6
+ -->
7
+ <template>
8
+ <div class="cn-json-viewer">
9
+ <div :class="['cn-json-viewer__codemirror', isDark ? 'cn-json-viewer__codemirror--dark' : 'cn-json-viewer__codemirror--light']">
10
+ <CodeMirror
11
+ v-model="localValue"
12
+ :basic="true"
13
+ :placeholder="resolvedLanguage === 'json' ? '{ &quot;key&quot;: &quot;value&quot; }' : ''"
14
+ :dark="isDark"
15
+ :readonly="readOnly"
16
+ :linter="linterExtension"
17
+ :lang="langExtension"
18
+ :extensions="editorExtensions"
19
+ :tab-size="2"
20
+ :style="{ height }" />
21
+ <NcButton
22
+ v-if="!readOnly && resolvedLanguage === 'json'"
23
+ class="cn-json-viewer__format-btn"
24
+ type="secondary"
25
+ size="small"
26
+ @click="formatJson">
27
+ Format JSON
28
+ </NcButton>
29
+ </div>
30
+ <span v-if="shouldShowError" class="cn-json-viewer__error">
31
+ {{ resolvedErrorText }}
32
+ </span>
33
+ </div>
34
+ </template>
35
+
36
+ <script>
37
+ import { NcButton } from '@nextcloud/vue'
38
+ import CodeMirror from 'vue-codemirror6'
39
+ import { githubLight, githubDark } from '@uiw/codemirror-theme-github'
40
+ import { json as jsonLang, jsonParseLinter as jsonLinter } from '@codemirror/lang-json'
41
+ import { xml as xmlLang } from '@codemirror/lang-xml'
42
+ import { html as htmlLang } from '@codemirror/lang-html'
43
+ import { getTheme } from '../../utils/getTheme.js'
44
+
45
+ /**
46
+ * CnJsonViewer — Syntax-highlighted code viewer/editor.
47
+ *
48
+ * Wraps CodeMirror 6 with support for JSON, XML, HTML, and plain text.
49
+ * Includes syntax highlighting, and optional formatting/validation for JSON.
50
+ * Use `readOnly` for display-only mode.
51
+ *
52
+ * @example Read-only JSON display (default)
53
+ * <CnJsonViewer :value="jsonString" :read-only="true" />
54
+ *
55
+ * @example Auto-detect language from content
56
+ * <CnJsonViewer :value="responseBody" :read-only="true" language="auto" />
57
+ *
58
+ * @example Explicit XML mode
59
+ * <CnJsonViewer :value="xmlString" :read-only="true" language="xml" />
60
+ *
61
+ * @example Editable JSON with custom height
62
+ * <CnJsonViewer :value="jsonString" height="500px" @update:value="onUpdate" />
63
+ */
64
+ export default {
65
+ name: 'CnJsonViewer',
66
+
67
+ components: {
68
+ NcButton,
69
+ CodeMirror,
70
+ },
71
+
72
+ props: {
73
+ /** JSON string to display or edit */
74
+ value: { type: String, default: '' },
75
+ /** When true, the editor is non-editable and hides format button and validation */
76
+ readOnly: { type: Boolean, default: false },
77
+ /** CSS height for the editor container */
78
+ height: { type: String, default: '300px' },
79
+ /**
80
+ * Content language for syntax highlighting.
81
+ * - 'auto': Auto-detect from content (JSON → xml → text) (default)
82
+ * - 'json': JSON with validation and formatting
83
+ * - 'xml': XML/HTML tag highlighting
84
+ * - 'html': Alias for XML highlighting
85
+ * - 'text': Plain text, no highlighting
86
+ */
87
+ language: {
88
+ type: String,
89
+ default: 'auto',
90
+ validator: (v) => ['json', 'xml', 'html', 'text', 'auto'].includes(v),
91
+ },
92
+ /**
93
+ * Custom text for the error banner rendered below the editor.
94
+ * - `null` (default): the built-in "Invalid JSON format" banner renders
95
+ * whenever `language === 'json'` and the content fails to parse.
96
+ * - Any string: the caller owns the banner — it renders when this
97
+ * string is non-empty, and is hidden when empty. Use this to surface
98
+ * a richer parse error (e.g. the exception message).
99
+ */
100
+ errorText: { type: String, default: null },
101
+ },
102
+
103
+ data() {
104
+ return {
105
+ githubLight,
106
+ githubDark,
107
+ }
108
+ },
109
+
110
+ computed: {
111
+ localValue: {
112
+ get() { return this.value },
113
+ set(v) { this.$emit('update:value', v) },
114
+ },
115
+ isDark: {
116
+ get() { return getTheme() === 'dark' },
117
+ },
118
+ theme: {
119
+ get() { return this.isDark ? githubDark : githubLight },
120
+ },
121
+ /**
122
+ * Resolve 'auto' language to a concrete language based on content.
123
+ * @return {string} Resolved language: 'json', 'xml', or 'text'
124
+ */
125
+ resolvedLanguage() {
126
+ if (this.language !== 'auto') {
127
+ return this.language
128
+ }
129
+ const trimmed = (this.value || '').trim()
130
+ if (!trimmed) return 'text'
131
+ try {
132
+ JSON.parse(trimmed)
133
+ return 'json'
134
+ } catch {
135
+ // not JSON
136
+ }
137
+ if (trimmed.charAt(0) === '<' && trimmed.includes('>')) {
138
+ // Detect HTML by doctype or common HTML tags
139
+ if (/<!doctype\s+html/i.test(trimmed) || /<(?:html|head|body|div|span|p|a|script|style|link|meta|form|input|button|table|ul|ol|li|h[1-6]|img|nav|header|footer|main|section|article)\b/i.test(trimmed)) {
140
+ return 'html'
141
+ }
142
+ return 'xml'
143
+ }
144
+ return 'text'
145
+ },
146
+ /**
147
+ * CodeMirror language extension based on resolved language.
148
+ * @return {object|null} Language extension or null for plain text
149
+ */
150
+ langExtension() {
151
+ switch (this.resolvedLanguage) {
152
+ case 'json':
153
+ return jsonLang()
154
+ case 'html':
155
+ return htmlLang()
156
+ case 'xml':
157
+ return xmlLang()
158
+ case 'text':
159
+ default:
160
+ return null
161
+ }
162
+ },
163
+ /**
164
+ * CodeMirror linter extension (only active for JSON in edit mode).
165
+ * @return {object|null} Linter extension or null
166
+ */
167
+ linterExtension() {
168
+ if (this.readOnly) return null
169
+ if (this.resolvedLanguage === 'json') return jsonLinter()
170
+ return null
171
+ },
172
+ /**
173
+ * Combined CodeMirror extensions array.
174
+ * @return {Array} Extensions including theme and optional language
175
+ */
176
+ editorExtensions() {
177
+ const exts = [this.theme]
178
+ if (this.langExtension) exts.push(this.langExtension)
179
+ return exts
180
+ },
181
+ /**
182
+ * Error text displayed in the banner. Caller-provided `errorText` wins;
183
+ * otherwise falls back to the built-in "Invalid JSON format" message.
184
+ * @return {string} Message to show.
185
+ */
186
+ resolvedErrorText() {
187
+ if (this.errorText !== null) return this.errorText
188
+ return 'Invalid JSON format'
189
+ },
190
+ /**
191
+ * Whether to show the error banner.
192
+ * - If `errorText` is supplied, the caller controls visibility via its
193
+ * emptiness.
194
+ * - Otherwise, show iff the content is editable JSON and fails to parse.
195
+ * @return {boolean} Visibility flag.
196
+ */
197
+ shouldShowError() {
198
+ if (this.errorText !== null) return this.errorText !== ''
199
+ return !this.readOnly
200
+ && this.resolvedLanguage === 'json'
201
+ && !this.isValidJson(this.value)
202
+ },
203
+ },
204
+
205
+ methods: {
206
+ /**
207
+ * Format the current JSON value with 2-space indentation.
208
+ * Emits 'update:value' with the formatted string and 'format' with the parsed object.
209
+ */
210
+ formatJson() {
211
+ try {
212
+ if (this.value) {
213
+ const parsed = JSON.parse(this.value)
214
+ this.$emit('update:value', JSON.stringify(parsed, null, 2))
215
+ this.$emit('format', parsed)
216
+ }
217
+ } catch {
218
+ // Keep invalid JSON as-is
219
+ }
220
+ },
221
+
222
+ /**
223
+ * Check if a string is valid JSON.
224
+ * @param {string} str - String to validate
225
+ * @return {boolean} True if valid JSON
226
+ */
227
+ isValidJson(str) {
228
+ if (!str || !str.trim()) return false
229
+ try {
230
+ JSON.parse(str)
231
+ return true
232
+ } catch {
233
+ return false
234
+ }
235
+ },
236
+ },
237
+ }
238
+ </script>
239
+
240
+ <style scoped>
241
+ .cn-json-viewer {
242
+ display: flex;
243
+ flex-direction: column;
244
+ gap: 8px;
245
+ }
246
+
247
+ .cn-json-viewer__codemirror {
248
+ position: relative;
249
+ }
250
+
251
+ .cn-json-viewer__codemirror :deep(.cm-editor) {
252
+ height: 100%;
253
+ outline: none !important;
254
+ }
255
+
256
+ .cn-json-viewer__codemirror :deep(.cm-scroller) {
257
+ overflow: auto;
258
+ }
259
+
260
+ .cn-json-viewer__codemirror :deep(.cm-content) {
261
+ border-radius: 0 !important;
262
+ border: none !important;
263
+ }
264
+
265
+ .cn-json-viewer__codemirror--light > .vue-codemirror {
266
+ border: 1px dotted silver;
267
+ }
268
+
269
+ .cn-json-viewer__codemirror--dark > .vue-codemirror {
270
+ border: 1px dotted grey;
271
+ }
272
+
273
+ /* Text cursor */
274
+ .cn-json-viewer__codemirror :deep(.cm-content) * {
275
+ cursor: text !important;
276
+ }
277
+
278
+ /* PATCH SELECTION COLOR - default selection system does not work */
279
+ /* Selection background — CodeMirror uses .cm-selectionBackground instead of ::selection */
280
+ .cn-json-viewer__codemirror--light :deep(.cm-selectionBackground) {
281
+ background: #3390ff !important;
282
+ }
283
+
284
+ .cn-json-viewer__codemirror--light :deep(.cm-selectionBackground) + .cm-selectionBackground,
285
+ .cn-json-viewer__codemirror--light :deep(.cm-line ::selection) {
286
+ color: white !important;
287
+ }
288
+
289
+ .cn-json-viewer__codemirror--dark :deep(.cm-selectionBackground) {
290
+ background: #3390ff !important;
291
+ }
292
+
293
+ /* Selected text color */
294
+ .cn-json-viewer__codemirror--light :deep(.cm-content ::selection) {
295
+ background: #3390ff !important;
296
+ color: white !important;
297
+ }
298
+
299
+ .cn-json-viewer__codemirror--dark :deep(.cm-content ::selection) {
300
+ background: #3390ff !important;
301
+ color: white !important;
302
+ }
303
+
304
+ .cn-json-viewer__format-btn {
305
+ margin-top: 8px;
306
+ }
307
+
308
+ .cn-json-viewer__error {
309
+ color: var(--color-error);
310
+ font-size: 14px;
311
+ }
312
+ </style>