@conduction/nextcloud-vue 0.1.0-beta.14 → 0.1.0-beta.16

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 (46) hide show
  1. package/dist/nextcloud-vue.cjs.js +7282 -3443
  2. package/dist/nextcloud-vue.cjs.js.map +1 -1
  3. package/dist/nextcloud-vue.css +719 -100
  4. package/dist/nextcloud-vue.esm.js +7120 -3300
  5. package/dist/nextcloud-vue.esm.js.map +1 -1
  6. package/package.json +3 -2
  7. package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +36 -3
  8. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +34 -19
  9. package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +312 -36
  10. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +983 -64
  11. package/src/components/CnAdvancedFormDialog/index.js +3 -0
  12. package/src/components/CnAppLoading/CnAppLoading.vue +93 -0
  13. package/src/components/CnAppLoading/index.js +3 -0
  14. package/src/components/CnAppNav/CnAppNav.vue +269 -0
  15. package/src/components/CnAppNav/index.js +3 -0
  16. package/src/components/CnAppRoot/CnAppRoot.vue +201 -0
  17. package/src/components/CnAppRoot/index.js +3 -0
  18. package/src/components/CnColorPicker/CnColorPicker.vue +251 -0
  19. package/src/components/CnColorPicker/index.js +1 -0
  20. package/src/components/CnContextMenu/CnContextMenu.vue +41 -4
  21. package/src/components/CnDashboardPage/CnDashboardPage.vue +8 -0
  22. package/src/components/CnDependencyMissing/CnDependencyMissing.vue +152 -0
  23. package/src/components/CnDependencyMissing/index.js +3 -0
  24. package/src/components/CnDetailPage/CnDetailPage.vue +27 -16
  25. package/src/components/CnIndexPage/CnIndexPage.vue +36 -6
  26. package/src/components/CnPageRenderer/CnPageRenderer.vue +278 -0
  27. package/src/components/CnPageRenderer/index.js +4 -0
  28. package/src/components/CnPageRenderer/pageTypes.js +37 -0
  29. package/src/components/CnRowActions/CnRowActions.vue +44 -3
  30. package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +4 -0
  31. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +103 -74
  32. package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +30 -2
  33. package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +16 -12
  34. package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +9 -4
  35. package/src/components/index.js +7 -1
  36. package/src/composables/index.js +2 -0
  37. package/src/composables/useAppManifest.js +115 -0
  38. package/src/composables/useAppStatus.js +107 -0
  39. package/src/css/CnSchemaFormDialog.css +22 -0
  40. package/src/index.js +24 -2
  41. package/src/schemas/app-manifest.schema.json +153 -0
  42. package/src/types/index.d.ts +9 -0
  43. package/src/types/manifest.d.ts +88 -0
  44. package/src/utils/index.js +1 -1
  45. package/src/utils/schema.js +157 -2
  46. package/src/utils/validateManifest.js +113 -0
@@ -36,13 +36,19 @@
36
36
  @click.stop />
37
37
  </div>
38
38
  <div v-else class="cn-schema-form__name-display-container">
39
- <AlertOutline v-if="isPropertyModified(row._key)"
39
+ <LockOutline v-if="row._inherited"
40
+ :size="16"
41
+ class="cn-schema-form__lock-icon"
42
+ :title="t('nextcloud-vue', 'Inherited from parent schema (read-only)')" />
43
+ <AlertOutline v-else-if="isPropertyModified(row._key)"
40
44
  :size="16"
41
45
  class="cn-schema-form__warning-icon"
42
46
  :title="t('nextcloud-vue', 'Property has been modified. Changes will only take effect after the schema is saved.')" />
43
47
  <div class="cn-schema-form__name-with-chips">
44
48
  <span class="cn-schema-form__property-name">{{ row._key }}</span>
45
49
  <div class="cn-schema-form__inline-chips">
50
+ <span v-if="row._inherited"
51
+ class="cn-schema-form__property-chip cn-schema-form__chip-inherited">{{ t('nextcloud-vue', 'Inherited') }}</span>
46
52
  <span v-if="isPropertyRequired(schema, row._key)"
47
53
  class="cn-schema-form__property-chip cn-schema-form__chip-primary">{{ t('nextcloud-vue', 'Required') }}</span>
48
54
  <span v-if="row.immutable"
@@ -80,6 +86,7 @@
80
86
 
81
87
  <template #row-actions="{ row }">
82
88
  <CnSchemaPropertyActions
89
+ v-if="!row._inherited"
83
90
  :property-key="row._key"
84
91
  :property="schema.properties[row._key]"
85
92
  :schema-item="schema"
@@ -111,6 +118,7 @@ import CnSchemaPropertyActions from './CnSchemaPropertyActions.vue'
111
118
 
112
119
  import Plus from 'vue-material-design-icons/Plus.vue'
113
120
  import AlertOutline from 'vue-material-design-icons/AlertOutline.vue'
121
+ import LockOutline from 'vue-material-design-icons/LockOutline.vue'
114
122
 
115
123
  /**
116
124
  * CnSchemaPropertiesTab — Properties table tab for CnSchemaFormDialog.
@@ -135,6 +143,7 @@ export default {
135
143
  CnSchemaPropertyActions,
136
144
  Plus,
137
145
  AlertOutline,
146
+ LockOutline,
138
147
  },
139
148
  props: {
140
149
  /** The full schema item (needs .properties, .required) */
@@ -161,6 +170,8 @@ export default {
161
170
  sortedUserGroups: { type: Array, default: () => [] },
162
171
  /** Whether groups are loading */
163
172
  loadingGroups: { type: Boolean, default: false },
173
+ /** Properties inherited from parent schemas (allOf) — shown as locked/read-only rows */
174
+ inheritedProperties: { type: Object, default: () => ({}) },
164
175
  },
165
176
  data() {
166
177
  return {
@@ -195,11 +206,24 @@ export default {
195
206
  })
196
207
  },
197
208
  propertyRows() {
198
- return this.sortedProperties.map(([key, prop]) => ({
209
+ const ownProperties = this.schema.properties || {}
210
+ const inheritedRows = Object.entries(this.inheritedProperties || {})
211
+ .filter(([key]) => !(key in ownProperties))
212
+ .map(([key, prop]) => ({
213
+ _id: `inherited_${key}`,
214
+ _key: key,
215
+ _inherited: true,
216
+ ...prop,
217
+ }))
218
+
219
+ const ownRows = this.sortedProperties.map(([key, prop]) => ({
199
220
  _id: this.getStablePropertyId(key),
200
221
  _key: key,
222
+ _inherited: false,
201
223
  ...prop,
202
224
  }))
225
+
226
+ return [...inheritedRows, ...ownRows]
203
227
  },
204
228
  },
205
229
  watch: {
@@ -258,6 +282,9 @@ export default {
258
282
  },
259
283
 
260
284
  getRowClass(row) {
285
+ if (row._inherited) {
286
+ return 'cn-schema-form__inherited-row'
287
+ }
261
288
  const classes = []
262
289
  if (this.selectedProperty === row._key) {
263
290
  classes.push('cn-schema-form__selected-row')
@@ -273,6 +300,7 @@ export default {
273
300
  },
274
301
 
275
302
  onRowClick(row) {
303
+ if (row._inherited) return
276
304
  if (this.selectedProperty === row._key) return
277
305
  this.$emit('update:selected-property', row._key)
278
306
  },
@@ -265,6 +265,7 @@
265
265
 
266
266
  <script>
267
267
  import _ from 'lodash'
268
+ import { translate as t } from '@nextcloud/l10n'
268
269
  import {
269
270
  NcButton,
270
271
  NcCheckboxRadioSwitch,
@@ -316,6 +317,8 @@ export default {
316
317
  hasAnyPermissions: { type: Boolean, default: false },
317
318
  /** Whether schema has restrictive permissions */
318
319
  isRestrictiveSchema: { type: Boolean, default: false },
320
+ /** Properties inherited from parent schemas (allOf) */
321
+ inheritedProperties: { type: Object, default: () => ({}) },
319
322
  },
320
323
  data() {
321
324
  return {
@@ -352,10 +355,10 @@ export default {
352
355
  },
353
356
 
354
357
  propertyOptions() {
355
- const schemaProps = Object.keys(this.schemaItem.properties || {}).map(key => ({
356
- id: key,
357
- label: key,
358
- }))
358
+ const ownKeys = Object.keys(this.schemaItem.properties || {}).filter(k => k !== '')
359
+ const inheritedKeys = Object.keys(this.inheritedProperties || {}).filter(k => k !== '')
360
+ const allKeys = [...new Set([...inheritedKeys, ...ownKeys])]
361
+ const schemaProps = allKeys.map(key => ({ id: key, label: key }))
359
362
  const systemProps = [
360
363
  { id: '_organisation', label: t('nextcloud-vue', '_organisation (system)') },
361
364
  { id: '_owner', label: t('nextcloud-vue', '_owner (system)') },
@@ -396,6 +399,7 @@ export default {
396
399
  },
397
400
  },
398
401
  methods: {
402
+ t,
399
403
  capitalize: _.capitalize,
400
404
 
401
405
  availablePropertyOptions(action, ruleIdx) {
@@ -510,10 +514,11 @@ export default {
510
514
  },
511
515
 
512
516
  removeCondition(action, originalIndex, propKey) {
513
- const match = this.schema.authorization[action][originalIndex].match
514
- if (match) {
515
- this.$delete(match, propKey)
516
- }
517
+ const rule = this.schema.authorization[action][originalIndex]
518
+ if (!rule.match) return
519
+ const updated = { ...rule.match }
520
+ delete updated[propKey]
521
+ this.$set(rule, 'match', updated)
517
522
  },
518
523
 
519
524
  // ─── Add-condition form state ─────────────────────────────────────
@@ -581,10 +586,9 @@ export default {
581
586
  if (!conditionValue && conditionValue !== false) return
582
587
 
583
588
  const rule = this.schema.authorization[action][originalIndex]
584
- if (!rule.match) {
585
- this.$set(rule, 'match', {})
586
- }
587
- this.$set(rule.match, property, { [operator]: conditionValue })
589
+ // Replace the entire match object so Vue 2's property-level dep on `rule.match`
590
+ // fires and the condition table v-for updates correctly.
591
+ this.$set(rule, 'match', { ...(rule.match || {}), [property]: { [operator]: conditionValue } })
588
592
 
589
593
  this.cancelAddCondition()
590
594
  },
@@ -146,8 +146,9 @@ export default {
146
146
  validator: (tabs) => tabs.length > 0 && tabs.every(t => t.id && t.title),
147
147
  },
148
148
  /**
149
- * Existing item for edit mode. Pass null or undefined for create mode.
150
- * The component only checks for truthiness to determine create vs edit mode.
149
+ * Existing item for edit mode. Pass null or undefined for plain create
150
+ * mode. A non-null object without an `id` is also treated as create mode
151
+ * — useful for pre-filled payloads (extend, clone, duplicate flows).
151
152
  *
152
153
  * @type {object|null}
153
154
  */
@@ -287,12 +288,16 @@ export default {
287
288
  },
288
289
  computed: {
289
290
  /**
290
- * Whether the dialog is in create mode (no existing item).
291
+ * Whether the dialog is in create mode (no existing persisted item).
292
+ *
293
+ * An item is considered "existing" only when it has an `id`. A non-null
294
+ * item without an `id` (e.g. a pre-filled payload for extend/clone flows)
295
+ * is treated as create mode.
291
296
  *
292
297
  * @return {boolean}
293
298
  */
294
299
  isCreateMode() {
295
- return !this.item
300
+ return !this.item || !this.item.id
296
301
  },
297
302
  /**
298
303
  * Resolved dialog title. Uses dialogTitle prop if provided,
@@ -18,7 +18,7 @@ export { CnMassActionBar } from './CnMassActionBar/index.js'
18
18
  export { CnDeleteDialog } from './CnDeleteDialog/index.js'
19
19
  export { CnCopyDialog } from './CnCopyDialog/index.js'
20
20
  export { CnFormDialog } from './CnFormDialog/index.js'
21
- export { CnAdvancedFormDialog } from './CnAdvancedFormDialog/index.js'
21
+ export { CnAdvancedFormDialog, CnPropertiesTab, CnMetadataTab, CnPropertyValueCell } from './CnAdvancedFormDialog/index.js'
22
22
  export { CnMassDeleteDialog } from './CnMassDeleteDialog/index.js'
23
23
  export { CnMassCopyDialog } from './CnMassCopyDialog/index.js'
24
24
  export { CnKpiGrid } from './CnKpiGrid/index.js'
@@ -46,6 +46,7 @@ export { CnDetailCard } from './CnDetailCard/index.js'
46
46
  export { CnCard } from './CnCard/index.js'
47
47
  export { CnStatsPanel } from './CnStatsPanel/index.js'
48
48
  export { CnJsonViewer } from './CnJsonViewer/index.js'
49
+ export { CnColorPicker } from './CnColorPicker/index.js'
49
50
  export { CnDetailGrid } from './CnDetailGrid/index.js'
50
51
  export { CnProgressBar } from './CnProgressBar/index.js'
51
52
  export { CnChartWidget } from './CnChartWidget/index.js'
@@ -55,3 +56,8 @@ export { CnTableWidget } from './CnTableWidget/index.js'
55
56
  export { CnNoteCard } from './CnNoteCard/index.js'
56
57
  export { CnObjectDataWidget } from './CnObjectDataWidget/index.js'
57
58
  export { CnObjectMetadataWidget } from './CnObjectMetadataWidget/index.js'
59
+ export { CnPageRenderer, defaultPageTypes } from './CnPageRenderer/index.js'
60
+ export { CnAppNav } from './CnAppNav/index.js'
61
+ export { CnAppLoading } from './CnAppLoading/index.js'
62
+ export { CnDependencyMissing } from './CnDependencyMissing/index.js'
63
+ export { CnAppRoot } from './CnAppRoot/index.js'
@@ -3,3 +3,5 @@ export { useDetailView } from './useDetailView.js'
3
3
  export { useSubResource } from './useSubResource.js'
4
4
  export { useDashboardView } from './useDashboardView.js'
5
5
  export { useContextMenu } from './useContextMenu.js'
6
+ export { useAppManifest } from './useAppManifest.js'
7
+ export { useAppStatus } from './useAppStatus.js'
@@ -0,0 +1,115 @@
1
+ import { ref } from 'vue'
2
+ import axios from '@nextcloud/axios'
3
+ import { generateUrl } from '@nextcloud/router'
4
+ import { validateManifest } from '../utils/validateManifest.js'
5
+
6
+ /**
7
+ * Composable that loads and validates a Conduction app manifest.
8
+ *
9
+ * The composable implements the three-phase flow specified in
10
+ * REQ-JMR-002 of the json-manifest-renderer capability:
11
+ *
12
+ * 1. Synchronous bundled load — `bundledManifest` is the immediate value.
13
+ * 2. Async backend merge — fetches `/index.php/apps/{appId}/api/manifest`
14
+ * and deep-merges any 200 response over the bundled manifest. 4xx /
15
+ * 5xx / network errors are silently ignored so apps work without a
16
+ * backend endpoint.
17
+ * 3. Validation — the merged result is validated against
18
+ * `app-manifest.schema.json`. On failure, the bundled manifest is
19
+ * kept and a `console.warn` is emitted with the error list.
20
+ *
21
+ * The returned manifest is reactive, so the future "app builder" backend
22
+ * can hot-swap the manifest without a page reload.
23
+ *
24
+ * @param {string} appId Nextcloud app ID. Used to build the default
25
+ * backend endpoint URL via `@nextcloud/router`.
26
+ * @param {object} bundledManifest The manifest shipped with the app (the
27
+ * default value, available synchronously).
28
+ * @param {object} [options] Configuration options.
29
+ * @param {string} [options.endpoint] Override the backend fetch URL.
30
+ * Useful for tests and alternative-host deployments.
31
+ * @param {Function} [options.fetcher] Override the fetch function. Must
32
+ * return a promise resolving to `{ status: number, data: object }`.
33
+ * Defaults to `axios.get` from `@nextcloud/axios` (which inherits the
34
+ * Nextcloud CSRF token automatically).
35
+ * @return {{ manifest: import('vue').Ref<object>, isLoading: import('vue').Ref<boolean>, validationErrors: import('vue').Ref<string[]|null> }}
36
+ *
37
+ * @example Basic usage (Composition API)
38
+ * const { manifest, isLoading } = useAppManifest('decidesk', bundled)
39
+ *
40
+ * @example Inside an Options API component
41
+ * export default {
42
+ * setup() {
43
+ * return useAppManifest('decidesk', bundled)
44
+ * },
45
+ * }
46
+ *
47
+ * @example Custom endpoint and fetcher (e.g. for tests)
48
+ * useAppManifest('decidesk', bundled, {
49
+ * endpoint: '/custom/manifest/url',
50
+ * fetcher: (url) => Promise.resolve({ status: 200, data: { ... } }),
51
+ * })
52
+ */
53
+ export function useAppManifest(appId, bundledManifest, options = {}) {
54
+ const manifest = ref(bundledManifest)
55
+ const isLoading = ref(true)
56
+ const validationErrors = ref(null)
57
+
58
+ const endpoint = options.endpoint ?? generateUrl(`/apps/${appId}/api/manifest`)
59
+ const fetcher = options.fetcher ?? ((url) => axios.get(url))
60
+
61
+ ;(async () => {
62
+ try {
63
+ const response = await fetcher(endpoint)
64
+ if (!response || response.status !== 200 || !response.data) {
65
+ return
66
+ }
67
+ const merged = deepMerge(bundledManifest, response.data)
68
+ const result = validateManifest(merged)
69
+ if (!result.valid) {
70
+ validationErrors.value = result.errors
71
+ // eslint-disable-next-line no-console
72
+ console.warn(
73
+ '[useAppManifest] Backend manifest failed schema validation; falling back to bundled manifest.',
74
+ result.errors,
75
+ )
76
+ return
77
+ }
78
+ manifest.value = merged
79
+ } catch (err) {
80
+ // Silent fallback on 404, network errors, non-200 responses.
81
+ // Apps without a backend endpoint should keep working.
82
+ } finally {
83
+ isLoading.value = false
84
+ }
85
+ })()
86
+
87
+ return { manifest, isLoading, validationErrors }
88
+ }
89
+
90
+ /**
91
+ * Deep-merge `source` into `target`, returning a new object. Plain
92
+ * objects are merged recursively; arrays are replaced (not concatenated)
93
+ * to match typical deep-merge semantics expected by manifest overrides.
94
+ *
95
+ * @param {object} target Base object.
96
+ * @param {object} source Object whose values take precedence.
97
+ * @return {object} New merged object.
98
+ */
99
+ function deepMerge(target, source) {
100
+ if (!isPlainObject(target)) return source
101
+ if (!isPlainObject(source)) return source
102
+ const out = { ...target }
103
+ for (const key of Object.keys(source)) {
104
+ if (isPlainObject(source[key]) && isPlainObject(target[key])) {
105
+ out[key] = deepMerge(target[key], source[key])
106
+ } else {
107
+ out[key] = source[key]
108
+ }
109
+ }
110
+ return out
111
+ }
112
+
113
+ function isPlainObject(value) {
114
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
115
+ }
@@ -0,0 +1,107 @@
1
+ import { ref } from 'vue'
2
+ import { getCapabilities } from '@nextcloud/capabilities'
3
+
4
+ /**
5
+ * Per-`appId` cache of status results. Populated lazily on first call.
6
+ *
7
+ * The Vue refs themselves are stored in the cache; subsequent calls for
8
+ * the same `appId` return the same refs so all consumers share state.
9
+ *
10
+ * Module-level lifetime — survives until the page is reloaded.
11
+ */
12
+ const cache = new Map()
13
+
14
+ /**
15
+ * Composable that reports whether a given Nextcloud app is installed
16
+ * and enabled.
17
+ *
18
+ * Implements REQ-JMR-012 of the json-manifest-renderer capability:
19
+ * - generic over `appId` so callers can check `openregister`,
20
+ * `opencatalogi`, or any other Nextcloud app
21
+ * - results are cached per `appId` for the page lifetime; repeated
22
+ * calls reuse the same refs without re-checking
23
+ * - on error the composable falls back to `{ installed: false,
24
+ * enabled: false }` and logs a `console.warn`, so a failed
25
+ * lookup never crashes the app shell
26
+ *
27
+ * Detection order:
28
+ * 1. `OC.appswebroots[appId]` — a global object Nextcloud injects on
29
+ * every authenticated page load that contains a key for every app
30
+ * enabled for the current user. This is the most reliable signal:
31
+ * it doesn't require the target app to opt into the capabilities
32
+ * API, and it reflects per-user enablement.
33
+ * 2. `getCapabilities()[appId]` — falls back to the capabilities
34
+ * bootstrap when `OC` is not available (rare; mostly for tests).
35
+ * Apps that implement `\OCP\Capabilities\ICapability` advertise
36
+ * themselves here.
37
+ *
38
+ * Most Conduction / OpenRegister-backed apps do NOT register a
39
+ * capability key, which is why the appswebroots check has to come
40
+ * first — capabilities alone would report every Conduction app as
41
+ * "missing" even when they are installed and enabled.
42
+ *
43
+ * @param {string} appId Nextcloud app id (e.g. `"openregister"`).
44
+ * @return {{ installed: import('vue').Ref<boolean>, enabled: import('vue').Ref<boolean>, loading: import('vue').Ref<boolean> }}
45
+ *
46
+ * @example
47
+ * const { installed, enabled, loading } = useAppStatus('openregister')
48
+ * // After loading.value flips to false, installed.value is the answer.
49
+ */
50
+ export function useAppStatus(appId) {
51
+ if (cache.has(appId)) {
52
+ return cache.get(appId)
53
+ }
54
+
55
+ const installed = ref(false)
56
+ const enabled = ref(false)
57
+ const loading = ref(true)
58
+
59
+ const result = { installed, enabled, loading }
60
+ cache.set(appId, result)
61
+
62
+ try {
63
+ // Primary check: the global `OC.appswebroots` map. NC populates
64
+ // this with one key per app enabled for the current user. Tests
65
+ // running outside a real Nextcloud page won't have it, in which
66
+ // case we fall through to the capabilities check.
67
+ const ocAppsWebRoots = (typeof OC !== 'undefined' && OC && OC.appswebroots) || null
68
+ if (ocAppsWebRoots && Object.prototype.hasOwnProperty.call(ocAppsWebRoots, appId)) {
69
+ installed.value = true
70
+ enabled.value = true
71
+ return result
72
+ }
73
+
74
+ // Secondary check: capabilities API. Only apps that implement
75
+ // `ICapability` advertise themselves here.
76
+ const capabilities = getCapabilities()
77
+ if (
78
+ capabilities
79
+ && typeof capabilities === 'object'
80
+ && Object.prototype.hasOwnProperty.call(capabilities, appId)
81
+ ) {
82
+ installed.value = true
83
+ enabled.value = true
84
+ }
85
+ } catch (err) {
86
+ // eslint-disable-next-line no-console
87
+ console.warn(
88
+ `[useAppStatus] Failed to determine status for "${appId}":`,
89
+ err,
90
+ )
91
+ // installed and enabled stay false
92
+ } finally {
93
+ loading.value = false
94
+ }
95
+
96
+ return result
97
+ }
98
+
99
+ /**
100
+ * Test-only helper to reset the per-`appId` cache. Not exported from
101
+ * the package barrel — only the test suite imports it directly.
102
+ *
103
+ * @internal
104
+ */
105
+ export function __resetAppStatusCacheForTests() {
106
+ cache.clear()
107
+ }
@@ -173,6 +173,17 @@
173
173
  border-left: 3px solid var(--color-primary);
174
174
  }
175
175
 
176
+ .cn-schema-form__viewTable tbody tr.cn-schema-form__inherited-row {
177
+ background-color: var(--color-background-dark);
178
+ border-left: 3px solid var(--color-text-maxcontrast);
179
+ cursor: default;
180
+ opacity: 0.8;
181
+ }
182
+
183
+ .cn-schema-form__viewTable tbody tr.cn-schema-form__inherited-row:hover {
184
+ background-color: var(--color-background-dark);
185
+ }
186
+
176
187
  /* Form editor */
177
188
  .cn-schema-form__form-editor {
178
189
  display: flex;
@@ -193,6 +204,11 @@
193
204
  flex-shrink: 0;
194
205
  }
195
206
 
207
+ .cn-schema-form__lock-icon {
208
+ color: var(--color-text-maxcontrast);
209
+ flex-shrink: 0;
210
+ }
211
+
196
212
  /* Table column widths */
197
213
  .cn-schema-form__tableColumnActions {
198
214
  width: 150px;
@@ -512,6 +528,12 @@
512
528
  color: var(--color-success-text);
513
529
  }
514
530
 
531
+ .cn-schema-form__chip-inherited {
532
+ background-color: var(--color-background-dark);
533
+ color: var(--color-text-maxcontrast);
534
+ border: 1px solid var(--color-border);
535
+ }
536
+
515
537
  .cn-schema-form__properties-warning {
516
538
  margin-top: 15px;
517
539
  }
package/src/index.js CHANGED
@@ -1,6 +1,17 @@
1
1
  // CSS — auto-imported so consumers get styles with components
2
2
  import './css/index.css'
3
3
 
4
+ // Re-export every Nc* component from @nextcloud/vue so consumer apps
5
+ // can import all Nextcloud-Vue + Conduction components from a single
6
+ // barrel, per ADR-004: "NEVER import from @nextcloud/vue directly —
7
+ // use @conduction/nextcloud-vue which re-exports all". Wildcard form
8
+ // is intentional so the barrel stays in sync without per-component
9
+ // edits when @nextcloud/vue adds new components. check-docs.js skips
10
+ // `export *` (its regex only matches the named-export form), so no
11
+ // docs are required for these pass-through re-exports — the source
12
+ // of truth is the upstream @nextcloud/vue documentation.
13
+ export * from '@nextcloud/vue'
14
+
4
15
  // Components
5
16
  export {
6
17
  CnDataTable,
@@ -24,6 +35,9 @@ export {
24
35
  CnCopyDialog,
25
36
  CnFormDialog,
26
37
  CnAdvancedFormDialog,
38
+ CnPropertiesTab,
39
+ CnMetadataTab,
40
+ CnPropertyValueCell,
27
41
  CnMassDeleteDialog,
28
42
  CnMassCopyDialog,
29
43
  CnKpiGrid,
@@ -48,6 +62,7 @@ export {
48
62
  CnCard,
49
63
  CnStatsPanel,
50
64
  CnJsonViewer,
65
+ CnColorPicker,
51
66
  CnDetailGrid,
52
67
  CnProgressBar,
53
68
  CnChartWidget,
@@ -60,6 +75,12 @@ export {
60
75
  CnNoteCard,
61
76
  CnObjectDataWidget,
62
77
  CnObjectMetadataWidget,
78
+ CnPageRenderer,
79
+ defaultPageTypes,
80
+ CnAppNav,
81
+ CnAppLoading,
82
+ CnDependencyMissing,
83
+ CnAppRoot,
63
84
  registerIcons,
64
85
  } from './components/index.js'
65
86
 
@@ -84,12 +105,13 @@ export {
84
105
  } from './store/plugins/index.js'
85
106
 
86
107
  // Composables
87
- export { useListView, useDetailView, useSubResource, useDashboardView, useContextMenu } from './composables/index.js'
108
+ export { useListView, useDetailView, useSubResource, useDashboardView, useContextMenu, useAppManifest, useAppStatus } from './composables/index.js'
88
109
 
89
110
  // Localization
90
111
  export { registerTranslations } from './l10n/index.js'
91
112
 
92
113
  // Utilities
93
114
  export { buildHeaders, buildQueryString, parseResponseError, networkError, genericError } from './utils/index.js'
94
- export { columnsFromSchema, formatValue, filtersFromSchema, fieldsFromSchema } from './utils/index.js'
115
+ export { columnsFromSchema, formatValue, filtersFromSchema, fieldsFromSchema, validateValue } from './utils/index.js'
116
+ export { validateManifest } from './utils/validateManifest.js'
95
117
  export { filterWidgetsByVisibility, isWidgetVisible, getCurrentUserId, getCurrentUserGroups, resetVisibilityCache } from './utils/index.js'