@conduction/nextcloud-vue 2.77.0 → 2.79.0
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/dist/bundled-packages.json +0 -2
- package/dist/esm/bundled-packages.json +0 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +113 -104
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js +23 -0
- package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +2 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +117 -0
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +122 -97
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +37 -0
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +2 -2
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +168 -5
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +84 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue.js +1 -1
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js +45 -2
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js.map +1 -1
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +61 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
- package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +8 -2
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +4 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +31 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +3 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +36 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +35 -23
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js +6 -2
- package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js.map +1 -1
- package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +30 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/composables/useAppManifest.js +8 -3
- package/dist/esm/composables/useAppManifest.js.map +1 -1
- package/dist/esm/composables/useFormDraft.js +11 -1
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/composables/useObjectPresence.js +15 -4
- package/dist/esm/composables/useObjectPresence.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +68 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +113 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +526 -16
- package/dist/esm/utils/badgeVariants.js +32 -0
- package/dist/esm/utils/badgeVariants.js.map +1 -0
- package/dist/esm/utils/boardDate.js +193 -0
- package/dist/esm/utils/boardDate.js.map +1 -0
- package/dist/esm/utils/dynamicProperties.js +5 -1
- package/dist/esm/utils/dynamicProperties.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +2569 -811
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +526 -16
- package/l10n/en.json +68 -1
- package/l10n/nl.json +113 -2
- package/package.json +1 -1
- package/scripts/build-validators.js +26 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +48 -39
- package/src/components/CnAiCompanion/CnAiCompanion.vue +23 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
- package/src/components/CnBoardView/CnBoardView.vue +2 -1
- package/src/components/CnCellRenderer/CnCellRenderer.md +1 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +169 -10
- package/src/components/CnChartWidget/CnChartWidget.vue +39 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +248 -8
- package/src/components/CnDataTable/CnDataTable.md +2 -0
- package/src/components/CnDataTable/CnDataTable.vue +88 -4
- package/src/components/CnDateRangePicker/CnDateRangePicker.vue +44 -1
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +61 -1
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
- package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
- package/src/components/CnFilesBrowser/index.js +1 -0
- package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
- package/src/components/CnFormDialog/CnFormDialog.vue +8 -2
- package/src/components/CnFormPage/CnFormPage.vue +4 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +33 -3
- package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
- package/src/components/CnStatWidget/CnStatWidget.vue +52 -2
- package/src/components/CnStatusBadge/CnStatusBadge.vue +6 -2
- package/src/components/CnTabsWidget/CnTabsWidget.vue +30 -1
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
- package/src/components/index.js +1 -1
- package/src/composables/__tests__/useObjectPresence.spec.js +24 -0
- package/src/composables/useAppManifest.js +8 -3
- package/src/composables/useFormDraft.js +11 -1
- package/src/composables/useObjectPresence.js +15 -4
- package/src/css/badge.css +31 -0
- package/src/css/kpi-card.css +51 -0
- package/src/css/look-board-detail.css +10 -0
- package/src/css/look-board-index.css +128 -14
- package/src/css/look-board.css +85 -0
- package/src/css/table.css +37 -0
- package/src/index.js +4 -0
- package/src/schemas/app-manifest-v2.schema.json +50 -5
- package/src/utils/badgeVariants.js +29 -0
- package/src/utils/boardDate.js +189 -0
- package/src/utils/dynamicProperties.js +5 -1
- package/src/utils/validateManifestV2.compiled.js +57 -2
- package/dist/esm/_virtual/equal.js +0 -4
- package/dist/esm/_virtual/equal.js.map +0 -1
- package/dist/esm/_virtual/ucs2length.js +0 -4
- package/dist/esm/_virtual/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
- package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
- package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
- package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
- package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFormDialog.vue3.js","sources":["../../../../src/components/CnFormDialog/CnFormDialog.vue?vue&type=template&id=c3eeb0a2&lang.js"],"sourcesContent":["<template>\n\t<CnDialog\n\t\t:look=\"look\"\n\t\t:width=\"width\"\n\t\tdefaultWidth=\"form\"\n\t\t:eyebrow=\"eyebrow\"\n\t\t:subtitle=\"subtitle\"\n\t\t:name=\"resolvedTitle\"\n\t\t:size=\"size\"\n\t\t:noClose=\"loading\"\n\t\t@closing=\"$emit('close')\">\n\t\t<!-- Result phase -->\n\t\t<div v-if=\"result !== null\"\n\t\t\tclass=\"cn-form-dialog__result\"\n\t\t\tdata-testid=\"cn-modal\"\n\t\t\tdata-testid-modal=\"cn-form-dialog\"\n\t\t\tdata-testid-phase=\"result\">\n\t\t\t<NcNoteCard v-if=\"result.success\" type=\"success\">\n\t\t\t\t{{ resolvedSuccessText }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard v-if=\"result.error\" type=\"error\">\n\t\t\t\t{{ result.error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"uploadFailedNames.length > 0 && Object.keys(heldFiles).length > 0\"\n\t\t\t\t:disabled=\"loading\"\n\t\t\t\tdata-testid=\"cn-form-dialog-retry-uploads\"\n\t\t\t\t@click=\"retryUploads\">\n\t\t\t\t{{ t('nextcloud-vue', 'Retry') }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<!-- Progress of a file uploading after the save. -->\n\t\t<p v-if=\"uploadProgress\"\n\t\t\tclass=\"cn-form-dialog__upload-progress\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-form-dialog-upload-progress\">\n\t\t\t{{ t('nextcloud-vue', 'Uploading {file}', { file: uploadProgress.file }) }}\n\t\t\t<progress v-if=\"uploadProgress.total > 0\" :max=\"uploadProgress.total\" :value=\"uploadProgress.loaded\" />\n\t\t</p>\n\n\t\t<!-- Form phase -->\n\t\t<div v-else\n\t\t\tclass=\"cn-form-dialog__form\"\n\t\t\t:class=\"{ 'cn-form-dialog__form--two-column': columns === 2 }\"\n\t\t\tdata-testid=\"cn-modal\"\n\t\t\tdata-testid-modal=\"cn-form-dialog\"\n\t\t\tdata-testid-phase=\"form\">\n\t\t\t<!-- The draft this form was left in. OFFERED, never applied: a form\n\t\t\t that fills itself is indistinguishable from one the server\n\t\t\t prefilled, and somebody submits last week's answers. -->\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"draftOffer !== null\"\n\t\t\t\ttype=\"info\"\n\t\t\t\tdata-testid=\"cn-form-dialog-draft-offer\">\n\t\t\t\t{{ t('nextcloud-vue', 'You have unsaved changes from an earlier visit.') }}\n\t\t\t\t<div class=\"cn-form-dialog__draft-actions\">\n\t\t\t\t\t<NcButton data-testid=\"cn-form-dialog-draft-restore\" @click=\"restoreDraft\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Restore') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton data-testid=\"cn-form-dialog-draft-discard\" @click=\"discardDraft\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Discard') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</NcNoteCard>\n\n\t\t\t<!-- Form-level error (e.g. server validation) — keeps the form visible so the user can fix the data -->\n\t\t\t<NcNoteCard v-if=\"formError\" type=\"error\" data-testid=\"cn-form-dialog-error\">\n\t\t\t\t{{ formError }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<!-- @slot form Replace the entire auto-generated form. -->\n\t\t\t<!-- @binding {object[]} fields The resolved, visible field definitions. -->\n\t\t\t<!-- @binding {object} form-data The live form values, keyed by field. -->\n\t\t\t<!-- @binding {object} errors The current validation errors, keyed by field. -->\n\t\t\t<!-- @binding {Function} update-field Write one field: `updateField(key, value)`. -->\n\t\t\t<slot\n\t\t\t\tv-if=\"$slots.form\"\n\t\t\t\tname=\"form\"\n\t\t\t\t:fields=\"resolvedFields\"\n\t\t\t\t:formData=\"formData\"\n\t\t\t\t:errors=\"errors\"\n\t\t\t\t:updateField=\"updateField\" />\n\n\t\t\t<!-- Auto-generated form -->\n\t\t\t<template v-else>\n\t\t\t\t<!-- @slot before-fields Content above the first auto-generated field. Use it for introductory text or an input the schema does not describe. -->\n\t\t\t\t<slot name=\"before-fields\" />\n\n\t\t\t\t<!-- `data-cn-field` is the only per-field identity this form\n\t\t\t\t exposes to a test. Without it an assertion about a field can\n\t\t\t\t only match on the label TEXT — which is the thing under test\n\t\t\t\t the moment labels are translated, so the assertion passes by\n\t\t\t\t construction in whichever language it was written in. -->\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"field in visibleFields\"\n\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t:data-cn-field=\"field.key\"\n\t\t\t\t\tclass=\"cn-form-dialog__field\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'cn-form-dialog__field--wide': fieldSpansBothColumns(field),\n\t\t\t\t\t\t'cn-form-field--half': isBoardLook && field.width === 'half',\n\t\t\t\t\t\t'cn-form-field--invalid': isBoardLook && !!errors[field.key],\n\t\t\t\t\t}\">\n\t\t\t\t\t<!-- Board look: the label sits above the control, the error between\n\t\t\t\t\t them; the control and the hint keep their place below. -->\n\t\t\t\t\t<CnFormField\n\t\t\t\t\t\tv-if=\"showsOuterLabel(field)\"\n\t\t\t\t\t\t:controlId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t:text=\"field.label\"\n\t\t\t\t\t\t:optional=\"!field.required\"\n\t\t\t\t\t\t:optionalLabel=\"optionalLabel\"\n\t\t\t\t\t\t:error=\"errors[field.key] || ''\"\n\t\t\t\t\t\t:errorId=\"errorIdFor(field)\" />\n\t\t\t\t\t<!-- @slot field-{key} Replace one auto-generated field with your own control. -->\n\t\t\t\t\t<!-- @binding {object} field The field definition. -->\n\t\t\t\t\t<!-- @binding {*} value The field's current value. -->\n\t\t\t\t\t<!-- @binding {string} error The field's validation error, if any. -->\n\t\t\t\t\t<!-- @binding {Function} update-field Write the value: `updateField(key, value)`. -->\n\t\t\t\t\t<slot\n\t\t\t\t\t\tv-if=\"$slots['field-' + field.key]\"\n\t\t\t\t\t\t:name=\"'field-' + field.key\"\n\t\t\t\t\t\t:field=\"field\"\n\t\t\t\t\t\t:value=\"formData[field.key]\"\n\t\t\t\t\t\t:error=\"errors[field.key]\"\n\t\t\t\t\t\t:updateField=\"updateField\" />\n\n\t\t\t\t\t<!-- referenceType (AD-18): render the integration's\n\t\t\t\t\t single-entity widget instead of a plain input. -->\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"resolveReferenceWidget(field)\"\n\t\t\t\t\t\tv-else-if=\"resolveReferenceWidget(field)\"\n\t\t\t\t\t\tv-bind=\"referenceWidgetProps(field)\"\n\t\t\t\t\t\t@input=\"value => updateField(field.key, value)\" />\n\n\t\t\t\t\t<!-- referenceSemanticType (ADR-048): a field that declares a\n\t\t\t\t\t canonical semantic-type URI whose provider schema is NOT\n\t\t\t\t\t installed renders DISABLED with a mouse-over tooltip. When\n\t\t\t\t\t a provider IS installed the field is transformed upstream\n\t\t\t\t\t (applySemanticResolution → a `$ref` reference) so it flows\n\t\t\t\t\t through the normal select branch below; only the\n\t\t\t\t\t unresolved/loading case lands here. -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"field.referenceSemanticType && !isSemanticResolved(field)\"\n\t\t\t\t\t\tclass=\"cn-form-dialog__semantic-unresolved\"\n\t\t\t\t\t\t:title=\"semanticUnavailableText(field)\">\n\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t:helperText=\"isSemanticLoading(field) ? '' : semanticUnavailableText(field)\"\n\t\t\t\t\t\t\t:disabled=\"true\"\n\t\t\t\t\t\t\t:loading=\"isSemanticLoading(field)\"\n\t\t\t\t\t\t\t:placeholder=\"field.description\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Concept-scheme choice whose options could not be loaded: the\n\t\t\t\t\t plain text input it had before, with a line saying so. -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"field.codeList && codedFailed[field.key]\"\n\t\t\t\t\t\tclass=\"cn-form-dialog__coded-fallback\">\n\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t:modelValue=\"codedFallbackText(field)\"\n\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t:helperText=\"t('nextcloud-vue', 'The list of choices could not be loaded. Enter the value by hand.')\"\n\t\t\t\t\t\t\t@update:modelValue=\"onCodedFallbackInput(field, $event)\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Auto-generated field -->\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t<!-- Text / Email / URL. The helper line lives outside the\n\t\t\t\t\t\t input (rather than on `helper-text`) so an over-long\n\t\t\t\t\t\t description can hang its info popover off it. -->\n\t\t\t\t\t\t<template v-if=\"field.widget === 'text' || field.widget === 'email' || field.widget === 'url'\">\n\t\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t:type=\"field.widget === 'email' ? 'email' : field.widget === 'url' ? 'url' : 'text'\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</template>\n\n\t\t\t\t\t\t<!-- Number -->\n\t\t\t\t\t\t<template v-else-if=\"field.widget === 'number'\">\n\t\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value !== '' ? Number(value) : null)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</template>\n\n\t\t\t\t\t\t<!-- Textarea -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'textarea'\" class=\"cn-form-dialog__textarea-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t:id=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\tclass=\"cn-form-dialog__textarea\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:value=\"formData[field.key] || ''\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\trows=\"4\"\n\t\t\t\t\t\t\t\t@input=\"updateField(field.key, $event.target.value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Object reference with inline create (`x-allow-create`):\n\t\t\t\t\t\t a select-OR-create picker over the referenced schema.\n\t\t\t\t\t\t Checked before the plain select branch so create-enabled\n\t\t\t\t\t\t references get CnResourceSelect; plain references keep the\n\t\t\t\t\t\t read-only NcSelect below. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-else-if=\"(isReferenceField(field) || isReferenceArrayField(field)) && field.allowCreate\"\n\t\t\t\t\t\t\tclass=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<CnResourceSelect\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:register=\"referenceRegister(field)\"\n\t\t\t\t\t\t\t\t:schema=\"String(field.reference.schema)\"\n\t\t\t\t\t\t\t\t:labelField=\"referenceLabelField(field)\"\n\t\t\t\t\t\t\t\t:multiple=\"!!field.reference.multiple\"\n\t\t\t\t\t\t\t\t:modelValue=\"referenceModelValue(field)\"\n\t\t\t\t\t\t\t\t:clearable=\"!field.required || !!field.reference.multiple\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:preload=\"true\"\n\t\t\t\t\t\t\t\t:createHandler=\"term => openNestedCreate(field, term)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => onReferenceSelected(field, value)\"\n\t\t\t\t\t\t\t\t@create=\"obj => onReferenceCreated(field, obj)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Enum toggle (`widget: \"switch\"` on a 2-value enum):\n\t\t\t\t\t\t renders a switch mapping off→enum[0], on→last enum value. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'switch'\" class=\"cn-form-dialog__switch-wrapper\">\n\t\t\t\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t\t\t\t:modelValue=\"isSwitchOn(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, switchValueFor(field, value))\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Select (enum / $ref object reference / single Nextcloud user, supports async function).\n\t\t\t\t\t\t A single Nextcloud-user field (`widget: \"user\"`) renders here too:\n\t\t\t\t\t\t `:user-select=\"isUserField(field)\"` flips this NcSelect into NC's native\n\t\t\t\t\t\t user picker, and the async load/search/select machinery (getEffective*,\n\t\t\t\t\t\t onAsyncSearch) resolves real users — mirroring how `user-multiselect`\n\t\t\t\t\t\t shares the multiselect branch below. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'select' || field.widget === 'user' || field.widget === 'group'\" class=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<!-- A `widget: \"user\"` field renders NcSelectUsers, everything\n\t\t\t\t\t\t\t else NcSelect. It used to be one NcSelect with\n\t\t\t\t\t\t\t `:user-select=\"isUserField(field)\"`, and @nextcloud/vue 9\n\t\t\t\t\t\t\t REMOVED that prop, so a user field had silently become a\n\t\t\t\t\t\t\t plain select. NcSelectUsers wraps NcSelect and merges the\n\t\t\t\t\t\t\t rest of these bindings through, so the async load, search\n\t\t\t\t\t\t\t and select machinery is unchanged either way. -->\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"isUserField(field) ? 'NcSelectUsers' : 'NcSelect'\"\n\t\t\t\t\t\t\t\tv-cn-select-aria=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:options=\"getEffectiveOptions(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"getEffectiveSelectedOption(field)\"\n\t\t\t\t\t\t\t\t:clearable=\"!field.required\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:loading=\"isFieldLoading(field)\"\n\t\t\t\t\t\t\t\t:filterable=\"!isAsyncEnum(field)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"onEffectiveSelectChange(field, $event)\"\n\t\t\t\t\t\t\t\t@search=\"isAsyncEnum(field) ? onAsyncSearch(field, $event) : undefined\">\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-option']\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<!-- @slot field-{key}-option Render one dropdown option for a select, multiselect or tags field. -->\n\t\t\t\t\t\t\t\t\t<!-- @binding {object} option The option being rendered (`{ id, label }`). -->\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<!-- NcSelect's own option splits a label in two spans\n\t\t\t\t\t\t\t\t (NcEllipsisedOption, keeping its last characters\n\t\t\t\t\t\t\t\t visible), and in a narrow dropdown the halves broke\n\t\t\t\t\t\t\t\t onto two lines mid-word: \"Agricu lture\". A plain\n\t\t\t\t\t\t\t\t label wraps at spaces only. NcSelectUsers keeps its\n\t\t\t\t\t\t\t\t avatar rendering. -->\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-else-if=\"!isUserField(field)\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-selected-option']\"\n\t\t\t\t\t\t\t\t\t#selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<!-- @slot field-{key}-selected-option Render the chosen option of a select, multiselect or tags field. -->\n\t\t\t\t\t\t\t\t\t<!-- @binding {object} option The selected option (`{ id, label }`). -->\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-selected-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Multiselect (array enum items / $ref array / Nextcloud users, supports async function) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'multiselect' || field.widget === 'user-multiselect' || field.widget === 'group-multiselect'\" class=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\t\tv-cn-select-aria=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:options=\"getEffectiveArrayOptions(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"getEffectiveSelectedArrayOptions(field)\"\n\t\t\t\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:loading=\"isFieldLoading(field)\"\n\t\t\t\t\t\t\t\t:filterable=\"!isAsyncItemsEnum(field)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"onEffectiveMultiSelectChange(field, $event)\"\n\t\t\t\t\t\t\t\t@search=\"isAsyncItemsEnum(field) ? onAsyncSearch(field, $event) : undefined\">\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-option']\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-selected-option']\"\n\t\t\t\t\t\t\t\t\t#selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-selected-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</NcSelect>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Tags (array, freeform, supports async suggestions) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'tags'\" class=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<!-- TODO: restore `:options` to `asyncState[field.key]?.options` once on Vue 3 (buble doesn't support optional chaining) -->\n\t\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\t\tv-cn-select-aria=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] || []\"\n\t\t\t\t\t\t\t\t:options=\"isFieldAsync(field) ? ((asyncState[field.key] && asyncState[field.key].options) || []) : []\"\n\t\t\t\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t\t\t\t:taggable=\"true\"\n\t\t\t\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:loading=\"isFieldLoading(field)\"\n\t\t\t\t\t\t\t\t:filterable=\"!isFieldAsync(field)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"updateField(field.key, $event)\"\n\t\t\t\t\t\t\t\t@search=\"isFieldAsync(field) ? onAsyncSearch(field, $event) : undefined\">\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-option']\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-selected-option']\"\n\t\t\t\t\t\t\t\t\t#selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-selected-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</NcSelect>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Checkbox / Switch (boolean) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'checkbox'\" class=\"cn-form-dialog__switch-wrapper\">\n\t\t\t\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t\t\t\t:modelValue=\"!!formData[field.key]\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Date / Datetime (NcTextField's type validator rejects\n\t\t\t\t\t\t 'date'/'datetime-local', so use NcDateTimePickerNative) -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-else-if=\"field.widget === 'date' || field.widget === 'datetime'\"\n\t\t\t\t\t\t\tclass=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<NcDateTimePickerNative\n\t\t\t\t\t\t\t\t:id=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:type=\"field.widget === 'datetime' ? 'datetime-local' : 'date'\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:hideLabel=\"true\"\n\t\t\t\t\t\t\t\t:modelValue=\"dateValueFor(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"date => onDateFieldInput(field, date)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- JSON (type: 'object'|'array'|... with widget: 'json'): parses on input, stores parsed value in formData -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'json'\" class=\"cn-form-dialog__json-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<CnJsonViewer\n\t\t\t\t\t\t\t\t:value=\"jsonStringFor(field)\"\n\t\t\t\t\t\t\t\tlanguage=\"json\"\n\t\t\t\t\t\t\t\t:readOnly=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:errorText=\"jsonErrors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:value=\"value => onJsonFieldInput(field, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Code (freeform editor, stored as raw string; optional `field.language` chooses highlighting) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'code'\" class=\"cn-form-dialog__json-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<CnJsonViewer\n\t\t\t\t\t\t\t\t:value=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:language=\"field.language || 'auto'\"\n\t\t\t\t\t\t\t\t:readOnly=\"field.readOnly\"\n\t\t\t\t\t\t\t\t@update:value=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- File (widget: 'file'): one file or several, small ones inline, big ones uploaded after save, optional camera. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'file'\" class=\"cn-form-dialog__file-wrapper\">\n\t\t\t\t\t\t\t<CnFileField\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key]\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:accept=\"field.file ? field.file.accept : ''\"\n\t\t\t\t\t\t\t\t:multiple=\"!!(field.file && field.file.multiple)\"\n\t\t\t\t\t\t\t\t:capture=\"field.file ? field.file.capture : ''\"\n\t\t\t\t\t\t\t\tv-bind=\"fileLimits(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:helperText=\"errors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Duration (widget: 'duration'): number + unit over an ISO 8601 string. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'duration'\" class=\"cn-form-dialog__duration-wrapper\">\n\t\t\t\t\t\t\t<CnDurationField\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : null\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Child records (widget: 'child-records'): another schema's records as an editable table, saved after the parent. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'child-records' && field.childRecords\" class=\"cn-form-dialog__child-records-wrapper\">\n\t\t\t\t\t\t\t<CnChildRecordsField\n\t\t\t\t\t\t\t\t:modelValue=\"Array.isArray(formData[field.key]) ? formData[field.key] : []\"\n\t\t\t\t\t\t\t\t:config=\"field.childRecords\"\n\t\t\t\t\t\t\t\t:register=\"register\"\n\t\t\t\t\t\t\t\t:parentId=\"item ? (item.id || item.uuid || '') : ''\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"errors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\"\n\t\t\t\t\t\t\t\t@loaded=\"rows => { childOriginals[field.key] = rows }\"\n\t\t\t\t\t\t\t\t@validity=\"problems => { childProblems[field.key] = problems }\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Sub-objects (widget: 'sub-objects'): an array of objects as an editable table. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'sub-objects' && field.items\" class=\"cn-form-dialog__sub-objects-wrapper\">\n\t\t\t\t\t\t\t<CnSubObjectsField\n\t\t\t\t\t\t\t\t:modelValue=\"Array.isArray(formData[field.key]) ? formData[field.key] : []\"\n\t\t\t\t\t\t\t\t:items=\"field.items\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"errors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Registry type-ahead (widget: 'property-source'): CnPropertySourceField;\n\t\t\t\t\t\t a pick can fill empty sibling fields from config.fill. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'property-source' && field.propertySource\" class=\"cn-form-dialog__property-source-wrapper\">\n\t\t\t\t\t\t\t<CnPropertySourceField\n\t\t\t\t\t\t\t\t:provider=\"field.propertySource.provider\"\n\t\t\t\t\t\t\t\t:mode=\"field.propertySource.mode\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\"\n\t\t\t\t\t\t\t\t@resolved=\"payload => onPropertySourceResolved(field, payload)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Icon (widget: 'icon'): renders CnIconBrowser, forwarding the field's icon config.\n\t\t\t\t\t\t `searchable` is gone — the browser always searches. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'icon'\" class=\"cn-form-dialog__icon-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<CnIconBrowser\n\t\t\t\t\t\t\t\t:value=\"formData[field.key] != null ? String(formData[field.key]) : null\"\n\t\t\t\t\t\t\t\t:sources=\"field.iconSources || ['mdi']\"\n\t\t\t\t\t\t\t\t:catalogues=\"field.catalogues || {}\"\n\t\t\t\t\t\t\t\t:allowCustomSvg=\"!!field.allowCustomSvg\"\n\t\t\t\t\t\t\t\t:clearable=\"!field.required\"\n\t\t\t\t\t\t\t\t@input=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Fallback: text input -->\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<small\n\t\t\t\t\t\t\tv-if=\"assignedFrom[field.key]\"\n\t\t\t\t\t\t\tclass=\"cn-form-dialog__assigned\"\n\t\t\t\t\t\t\taria-live=\"polite\"\n\t\t\t\t\t\t\tdata-testid=\"cn-form-dialog-assigned\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Filled in from {field}', { field: assignedFrom[field.key] }) }}\n\t\t\t\t\t\t</small>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- The extra questions a chosen case type / product line asks\n\t\t\t\t are fetched, so say they are coming. Silence here reads as\n\t\t\t\t \"this type has none\", and the user submits an incomplete\n\t\t\t\t form believing it was complete. -->\n\t\t\t\t<div v-if=\"dynamicLoading\"\n\t\t\t\t\tclass=\"cn-form-dialog__dynamic-loading\"\n\t\t\t\t\tdata-testid=\"cn-form-dialog-dynamic-loading\">\n\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t\t<span>{{ dynamicLoadingLabel }}</span>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- @slot after-fields Content below the last auto-generated field. -->\n\t\t\t\t<slot name=\"after-fields\" />\n\t\t\t</template>\n\t\t</div>\n\n\t\t<!-- \"Create\" on a select-or-create reference (`x-allow-create`) opens\n\t\t the referenced schema's own form on top of this one. Saving it\n\t\t selects the new object in the field that asked for it. -->\n\t\t<CnFormDialog\n\t\t\tv-if=\"nestedCreate !== null\"\n\t\t\tref=\"nestedCreateDialog\"\n\t\t\t:schema=\"nestedCreate.schema\"\n\t\t\t:register=\"nestedCreate.register\"\n\t\t\t:item=\"null\"\n\t\t\t:initialData=\"nestedCreate.initialData\"\n\t\t\t:excludeFields=\"nestedCreate.form.excludeFields\"\n\t\t\t:includeFields=\"nestedCreate.form.includeFields\"\n\t\t\t:fieldOverrides=\"nestedCreate.form.fieldOverrides\"\n\t\t\t:size=\"nestedCreate.form.size\"\n\t\t\t:columns=\"nestedCreate.form.columns\"\n\t\t\t:recoverDraft=\"false\"\n\t\t\tdata-testid=\"cn-form-dialog-nested-create\"\n\t\t\t@confirm=\"onNestedCreateConfirm\"\n\t\t\t@close=\"onNestedCreateClose\" />\n\n\t\t<CnReplaceValuesDialog\n\t\t\tv-if=\"pendingReplace !== null\"\n\t\t\t:changes=\"pendingReplace.changes\"\n\t\t\t@accept=\"resolveReplace(true)\"\n\t\t\t@decline=\"resolveReplace(false)\" />\n\n\t\t<template #actions>\n\t\t\t<!-- Board look: Save draft is the tertiary action at the far left, with\n\t\t\t the draft-state live region beside it (screens-dialog-parity). -->\n\t\t\t<span\n\t\t\t\tv-if=\"isBoardLook\"\n\t\t\t\tclass=\"cn-dialog__tertiary\"\n\t\t\t\tdata-testid=\"cn-dialog-tertiary\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"result === null && canSaveDraft\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:disabled=\"loading\"\n\t\t\t\t\tdata-testid=\"cn-form-dialog-save-draft\"\n\t\t\t\t\t@click=\"saveDraft\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Save draft') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<span\n\t\t\t\t\tclass=\"cn-form-dialog__draft-state\"\n\t\t\t\t\taria-live=\"polite\"\n\t\t\t\t\tdata-testid=\"cn-form-dialog-draft-state\">{{ draftIndicatorLabel }}</span>\n\t\t\t</span>\n\t\t\t<!-- One announcement per state change, so a screen reader hears\n\t\t\t \"Draft saved\" once rather than on every keystroke. -->\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tclass=\"cn-form-dialog__draft-state\"\n\t\t\t\taria-live=\"polite\"\n\t\t\t\tdata-testid=\"cn-form-dialog-draft-state\">{{ draftIndicatorLabel }}</span>\n\t\t\t<NcButton @click=\"$emit('close')\">\n\t\t\t\t{{ result !== null ? closeLabel : cancelLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"!isBoardLook && result === null && canSaveDraft\"\n\t\t\t\t:disabled=\"loading\"\n\t\t\t\tdata-testid=\"cn-form-dialog-save-draft\"\n\t\t\t\t@click=\"saveDraft\">\n\t\t\t\t{{ t('nextcloud-vue', 'Save draft') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"result === null\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"loading || confirmDisabled || !requiredFieldsFilled || !jsonFieldsValid\"\n\t\t\t\t@click=\"executeConfirm\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"20\" />\n\t\t\t\t\t<Plus v-else-if=\"isCreateMode\" :size=\"20\" />\n\t\t\t\t\t<ContentSaveOutline v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ resolvedConfirmLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { getLanguage, translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcCheckboxRadioSwitch, NcDateTimePickerNative, NcLoadingIcon, NcNoteCard, NcSelect, NcSelectUsers, NcTextField } from '@nextcloud/vue'\nimport ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnReplaceValuesDialog from '../../dialogs/CnReplaceValuesDialog.vue'\nimport CnChildRecordsField from '../CnChildRecordsField/CnChildRecordsField.vue'\nimport CnDialog from '../CnDialog/CnDialog.vue'\nimport CnDurationField from '../CnDurationField/CnDurationField.vue'\nimport CnFieldHelper from '../CnFieldHelper/CnFieldHelper.vue'\nimport CnFileField from '../CnFileField/CnFileField.vue'\nimport CnFormField from '../CnFormField/CnFormField.vue'\nimport CnIconBrowser from '../CnIconBrowser/CnIconBrowser.vue'\nimport CnJsonViewer from '../CnJsonViewer/CnJsonViewer.vue'\nimport CnPropertySourceField from '../CnPropertySourceField/CnPropertySourceField.vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport CnSubObjectsField from '../CnSubObjectsField/CnSubObjectsField.vue'\nimport { describeRowProblem, useChildRecords } from '../../composables/useChildRecords.js'\nimport { draftIndicatorText, draftKey, formDraftMixin, readDraft } from '../../composables/useFormDraft.js'\nimport { heldFromFailures, splitHeldFiles, uploadHeldFiles } from '../../composables/useHeldFileUpload.js'\nimport { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'\nimport { TENANT_CONTEXT_KEY } from '../../composables/useTenantContext.js'\nimport { useWriteFeedback } from '../../composables/useWriteFeedback.js'\nimport { cnSelectAria } from '../../directives/cnSelectAria.js'\nimport { dialogBoardMixin } from '../../mixins/dialogBoard.js'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { resolveCreateOverrideHandler } from '../../utils/actionsDispatcher.js'\nimport { loadCurrentUserProfile } from '../../utils/currentUserProfile.js'\nimport {\n\tdefinitionQueryParams,\n\tDYNAMIC_KEY_PREFIX,\n\textendsFormDeclarations,\n\tprefillDeclarations,\n\tprefillValues,\n\tpropertiesFromDefinitions,\n\tsplitDynamicFormData,\n} from '../../utils/dynamicProperties.js'\nimport { shouldShow } from '../../utils/fieldCondition.js'\nimport { computeAssignments } from '../../utils/formAssign.js'\nimport { resolveNextcloudGroup, searchNextcloudGroups } from '../../utils/groupAutocomplete.js'\nimport { durationSeconds } from '../../utils/isoDuration.js'\nimport { objectDisplayName } from '../../utils/objectName.js'\nimport { languageOptions, resolveDefaultToken, timezoneOptions } from '../../utils/pickerOptions.js'\nimport { getDotted, planPropertySourceFill } from '../../utils/propertySourceFill.js'\nimport { resolveFilterValue } from '../../utils/resolveFilterTokens.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { fieldsFromSchema, isTenantProperty } from '../../utils/schema.js'\nimport { resolveNextcloudUser, searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\n/**\n * Widgets that take the full width of a two-column form.\n *\n * These are the multi-line ones: a textarea, the JSON editor and the code\n * editor are unusable in half a dialog. Every other widget in the template's\n * vocabulary (text, number, select, date, switch, checkbox and friends) is a\n * single line and pairs happily.\n *\n * @type {string[]}\n */\nconst WIDE_WIDGETS = ['textarea', 'json', 'code']\n\n/** Largest file carried inline in the saved payload; bigger ones upload after save. */\nconst INLINE_FILE_CAP = 1024 * 1024\n\n/**\n * OpenRegister semantic-type discovery endpoint (ADR-048). Resolves a\n * canonical semantic-type URI to the provider schema that implements it.\n * Returns HTTP 200 with `{ resolved, registerSlug, schemaSlug, appId }` even\n * when nothing implements the URI (`resolved: false`). The exact route path\n * is still being finalised on the OpenRegister side — this path is the small,\n * single point of truth; a 404 (route absent) degrades to `resolved: false`\n * (see `resolveSemanticReference`), never a crash.\n */\nconst SEMANTIC_RESOLVE_ENDPOINT = '/apps/openregister/api/schemas/resolve-by-implements'\n\n/**\n * CnFormDialog — Create/edit dialog with auto-generated form from schema.\n *\n * When `item` is null, operates in create mode. When `item` is provided,\n * operates in edit mode. Auto-generates form fields from schema using\n * `fieldsFromSchema()`, but supports slot overrides at three levels:\n *\n * - `#form` — Replace the entire form content\n * - `#field-{key}` — Replace a single auto-generated field\n * - `#field-{key}-option` — Customize dropdown option rendering for a select/multiselect/tags field\n * - `#field-{key}-selected-option` — Customize selected option display for a select/multiselect/tags field\n * - `#before-fields` / `#after-fields` — Inject content around fields\n *\n * ## Schema-driven pickers\n *\n * Three JSON-Schema property shapes auto-render richer widgets:\n *\n * - `{ format: 'user' }` → a Nextcloud-user picker (async-searches the core\n * autocomplete endpoint; stores the chosen uid string).\n * - `{ format: 'uuid', $ref: '<schema>', 'x-allow-create': true }` → a single\n * object reference that renders `CnResourceSelect` — pick an existing object\n * OR create one inline from the typed term. Without `x-allow-create` a `$ref`\n * stays a plain select of existing objects.\n * - `{ enum: ['no', 'yes'], widget: 'switch' }` → a toggle mapping off → the\n * first enum value and on → the last enum value (keeps the stored value an\n * enum string, so an enum-driven lifecycle is unaffected).\n *\n * ## Async select support\n *\n * Select, multiselect, and tags fields support async options by setting `field.enum`\n * (or `field.items.enum` for multiselect) to an async function instead of a static array:\n *\n * ```js\n * { key: 'org', widget: 'select', enum: async (query) => fetchOrgs(query) }\n * ```\n *\n * The function receives the search query and must return an array of option objects\n * (each must have a `label` property for default display). Options are loaded on mount\n * (with empty query) and on each search input (debounced, default 300ms, configurable\n * via `field.debounce`). Async selects store the full option object in formData.\n *\n * ## OpenRegister object references (`$ref`)\n *\n * A schema property that is an object reference renders as a searchable\n * dropdown of the referenced objects instead of a free-text UUID box:\n *\n * - `{ type: 'string', format: 'uuid', $ref: '<schema-slug>' }` → single-select\n * - `{ type: 'array', items: { $ref: '<schema-slug>' } }` → multi-select\n *\n * `fieldsFromSchema` records the reference on the field as\n * `field.reference = { schema, multiple }`. Pass the `register` prop so the\n * dialog can fetch the referenced objects (`GET /api/objects/{register}/{schema}`)\n * — each is mapped to `{ label: <human name>, value: <uuid> }`. The chosen\n * value stored in formData is the UUID (single) or array of UUIDs (multiple),\n * NOT the full object. In edit mode the stored UUID is resolved to its label\n * so the current selection displays. When `register` is empty the field falls\n * back to a plain text input.\n *\n * ## Nextcloud user references\n *\n * A schema property marked as a Nextcloud user renders as a searchable\n * dropdown of real Nextcloud users instead of a free-text box:\n *\n * - `{ type: 'string', referenceType: 'nextcloud-user' }` → single-select\n * (`format: 'user'` / `'username'` also work as markers)\n * - `{ type: 'array', items: { referenceType: 'nextcloud-user' } }` → multi-select\n *\n * `fieldsFromSchema` tags the field as `field.userPicker = { multiple }`.\n * Users are loaded from the core autocomplete OCS endpoint (available to every\n * authenticated user), mapped to `{ label: <display name>, value: <uid> }`.\n * The value stored in formData is the UID string (single) or array of UIDs\n * (multiple) — never the display object. In edit mode the stored UID is\n * resolved to its display name so the current selection shows. Needs no\n * `register` prop. If the OCS call fails the picker fails soft (empty options;\n * the stored UID still shows).\n *\n * ## Tenant fields\n *\n * The platform sets the tenant a record belongs to, so the form never asks for\n * it. A property counts as the tenant when it is named `tenant`, `tenant_id` /\n * `tenantId` or `tenant_uuid`, or when it carries `x-openregister-tenant: true`,\n * `x-platform-managed: true`, `x-managed-by: 'platform'`, `format: 'tenant'` or\n * `referenceType: 'tenant'`. `x-openregister-tenant: false` opts a property out;\n * `fieldOverrides: { tenant_id: { hidden: false } }` shows it on one form.\n *\n * The hidden property is still sent. On edit it keeps the record's value. On\n * create it takes the active tenant from the tenant context the app provides\n * (`CnAppRoot`'s `initialOrganisationUuid`, or `provideTenantContext`). There\n * is no other source: an app's tenant is its own notion, not OpenRegister's\n * active organisation. When the app provides none, the key is left out of the\n * payload rather than sent blank, so the server's own default or validation\n * handles it.\n *\n * ## JSON / code fields\n *\n * Two widgets render a CnJsonViewer-powered editor:\n *\n * - `widget: 'json'` — Parses on input. formData holds the parsed value (object,\n * array, number, string, boolean, or `null` for empty). Invalid JSON displays an\n * inline error and blocks the confirm button until fixed. Pair with `type: 'object'`\n * (or any type) to opt a property out of the default object-filter in `fieldsFromSchema`.\n * - `widget: 'code'` — Stores the raw string. Optional `field.language` chooses\n * syntax highlighting (`'json'|'xml'|'html'|'text'|'auto'`, default `'auto'`).\n *\n * ## Icon field\n *\n * - `widget: 'icon'` — Renders a `CnIconBrowser`. Optional field config forwards to\n * the picker: `iconSources` (→ `sources`, default `['mdi']`), `catalogues`, and\n * `allowCustomSvg`. (`searchable` is obsolete — the browser always searches.)\n * formData holds the selected icon value (registry key, source value, URL, or\n * raw SVG). The bundled NL-government sets are offered alongside the catalogues.\n *\n * The dialog does NOT perform the save itself — it emits a `confirm` event\n * with the form data. The parent performs the actual API call and calls\n * `setResult()` via a ref.\n *\n * ```vue\n * <CnFormDialog\n * v-if=\"showFormDialog\"\n * ref=\"formDialog\"\n * :schema=\"schema\"\n * :item=\"editItem\"\n * @confirm=\"onFormConfirm\"\n * @close=\"showFormDialog = false\" />\n * ```\n *\n * // In methods:\n * async onFormConfirm(formData) {\n * try {\n * if (formData.id) {\n * await store.updateItem(formData.id, formData)\n * } else {\n * await store.createItem(formData)\n * }\n * this.$refs.formDialog.setResult({ success: true })\n * } catch (e) {\n * this.$refs.formDialog.setResult({ error: e.message })\n * }\n * }\n *\n * <caption>Async select with custom option rendering</caption>\n * ```vue\n * <CnFormDialog :fields=\"[{\n * key: 'organisation',\n * widget: 'select',\n * label: 'Organisation',\n * required: true,\n * enum: async (query) => {\n * const results = await store.search(query)\n * return results.map(o => ({ label: o.name, id: o.uuid, ...o }))\n * },\n * debounce: 500,\n * }]\" @confirm=\"onConfirm\">\n * <template #field-organisation-option=\"{ name, description }\">\n * <strong>{{ name }}</strong>\n * <p>{{ description }}</p>\n * </template>\n * <template #field-organisation-selected-option=\"{ name }\">\n * {{ name }}\n * </template>\n * </CnFormDialog>\n * ```\n *\n * @event confirm Emitted when the user confirms the form. Payload: formData object (includes `id` in edit mode).\n * @event close Emitted when the dialog should be closed (cancel, close button, or auto-close after success).\n */\nexport default {\n\tname: 'CnFormDialog',\n\n\tdirectives: { cnSelectAria },\n\n\tcomponents: {\n\t\tCnFileField,\n\t\tCnDurationField,\n\t\tCnPropertySourceField,\n\t\tCnReplaceValuesDialog,\n\t\tCnSubObjectsField,\n\t\tCnChildRecordsField,\n\t\tCnDialog,\n\t\tCnFormField,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcLoadingIcon,\n\t\tNcTextField,\n\t\tNcSelect,\n\t\tNcSelectUsers,\n\t\tNcDateTimePickerNative,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnJsonViewer,\n\t\tCnIconBrowser,\n\t\tCnResourceSelect,\n\t\tCnFieldHelper,\n\t\tPlus,\n\t\tContentSaveOutline,\n\t},\n\n\tmixins: [dialogBoardMixin, formDraftMixin()],\n\n\tinject: {\n\t\t_cnTenantContext: {\n\t\t\tfrom: TENANT_CONTEXT_KEY,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Field\n\t\t * labels/descriptions come from schema property titles, authored in\n\t\t * English as the canonical source; the visible label is resolved\n\t\t * through this function so it follows the user's language. Defaults to\n\t\t * identity when used standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\n\t\t/**\n\t\t * The app manifest, the v2 registry, the legacy customComponents map and\n\t\t * the registry-modal opener, all provided by CnAppRoot. A select-or-create\n\t\t * picker reads them to create an object the way the app's own index page\n\t\t * does (`config.createOverride` / `config.createModal`). Standalone (no\n\t\t * CnAppRoot) they are absent and the generic nested form is used.\n\t\t */\n\t\tcnManifest: { default: null },\n\t\tcnRegistry: { default: null },\n\t\tcnCustomComponents: { default: null },\n\t\tcnOpenModal: { default: null },\n\t},\n\n\tprops: {\n\t\t/** Schema for auto-generating fields. Either schema or fields must be provided. */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Existing item for edit mode. Pass null for create mode. */\n\t\titem: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Register slug to resolve OpenRegister object references against.\n\t\t *\n\t\t * A schema property that is an object reference (`$ref: '<schema-slug>'`,\n\t\t * or `items.$ref` for an array) renders as a searchable dropdown of the\n\t\t * referenced objects (label = human name, value = UUID) instead of a\n\t\t * free-text UUID box. The `$ref` value is the referenced *schema* slug;\n\t\t * this prop supplies the *register* the objects live in. When empty,\n\t\t * reference fields fall back to a plain text input (no fetch attempted).\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The word shown as \"(optional)\" after an optional field's label in the\n\t\t * board look. Required fields carry no mark and there is no asterisk.\n\t\t */\n\t\toptionalLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'optional'),\n\t\t},\n\n\t\t/**\n\t\t * Seed values for CREATE mode, keyed by field. Merged over the schema\n\t\t * defaults when opening a new-item form. Use it to pre-link a child to\n\t\t * its parent when adding from a detail page (e.g. `{ lead: '<uuid>' }`).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tinitialData: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Field keys rendered read-only (disabled) and immutable — typically the\n\t\t * parent reference seeded via `initialData` so the user can't repoint a\n\t\t * child away from the record it was created under.\n\t\t *\n\t\t * @type {string[]}\n\t\t */\n\t\tlockedFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Keep what the user typed and offer it back when this form reopens.\n\t\t *\n\t\t * Local only: nothing is written to the server until they save. On by\n\t\t * default because it changes nothing they did not type, and because the\n\t\t * case it solves is a closed tab.\n\t\t */\n\t\trecoverDraft: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Offer a \"Save draft\" button that stores the record with a draft\n\t\t * marker instead of validating it.\n\t\t *\n\t\t * Opt-in, and inert unless the schema carries `draftField`: a button\n\t\t * that writes a property the schema does not declare would have\n\t\t * OpenRegister drop it silently, and the record would come back\n\t\t * looking published.\n\t\t */\n\t\tallowDraft: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The boolean property that marks a record as a draft. */\n\t\tdraftField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'isDraft',\n\t\t},\n\n\t\t/**\n\t\t * The app the draft belongs to, and who is typing it.\n\t\t *\n\t\t * 🔴 THE USER IS PART OF THE DRAFT KEY. A shared browser profile at a\n\t\t * service desk is the ordinary case in a municipality, and a draft\n\t\t * keyed without the user hands the next person at the counter what the\n\t\t * last one typed. A host that passes nothing gets `anonymous`, which\n\t\t * is correct for a single-user context and wrong for a counter — so\n\t\t * the prop exists and is documented rather than guessed at.\n\t\t */\n\t\tdraftAppId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Who is typing, for the draft key. See `draftAppId`. */\n\t\tdraftUserId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Dialog title. Defaults to \"Create {schema.title}\" or \"Edit {schema.title}\". */\n\t\tdialogTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Manual field definitions. Overrides schema-generated fields when provided. */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Field keys to exclude from auto-generated form */\n\t\texcludeFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Field keys to include (whitelist mode) */\n\t\tincludeFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Per-field overrides passed to fieldsFromSchema */\n\t\tfieldOverrides: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Object context forwarded to integration single-entity\n\t\t * widgets rendered for fields that declare a `referenceType`\n\t\t * (AD-18): `{ register, schema, objectId }`. Optional.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\treferenceContext: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Which field is the \"name\".\n\t\t *\n\t\t * @deprecated Never read. The result note card renders whatever message the\n\t\t * parent hands to `setResult()`, so this field names nothing.\n\t\t */\n\t\tnameField: { // eslint-disable-line vue/no-unused-properties -- deprecated no-op, kept so apps still passing it do not leak it onto the DOM through $attrs.\n\t\t\ttype: String,\n\t\t\tdefault: 'title',\n\t\t},\n\n\t\t/** NcDialog size */\n\t\tsize: {\n\t\t\ttype: String,\n\t\t\tdefault: 'normal',\n\t\t},\n\n\t\t/**\n\t\t * How many columns the auto-generated fields flow into.\n\t\t *\n\t\t * `1` (the default) keeps every existing form exactly as it is. `2`\n\t\t * pairs the fields into two columns, which is worth it once a form\n\t\t * asks enough questions that the person has to scroll to see whether\n\t\t * there are more. Pair it with `size=\"large\"`, or the two columns are\n\t\t * merely two narrow ones.\n\t\t *\n\t\t * A field whose widget needs the room (a textarea, a JSON editor)\n\t\t * spans both columns regardless. Below 700px the layout collapses\n\t\t * back to one column, so this is safe on a narrow screen.\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1,\n\t\t\tvalidator: (value) => value === 1 || value === 2,\n\t\t},\n\n\t\t/** Success message. Defaults to \"Item saved successfully.\" */\n\t\tsuccessText: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label for the cancel button */\n\t\t/**\n\t\t * Text shown while the fields a chosen value brings with it are being\n\t\t * fetched. The default is deliberately generic; a host that knows the\n\t\t * domain can say what is actually loading (\"Loading the questions for\n\t\t * this case type.\").\n\t\t */\n\t\tdynamicLoadingLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Loading the fields this choice adds.'),\n\t\t},\n\n\t\t/** Label for the dismiss button while the form is still showing. */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the close button */\n\t\tcloseLabel: { type: String, default: () => t('nextcloud-vue', 'Close') },\n\t\t/**\n\t\t * Report the write with a toast: \"Saved {title}\" after a successful\n\t\t * save, the server's message after a failed one. `false` suppresses\n\t\t * the toasts and changes nothing else.\n\t\t */\n\t\tfeedback: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Confirm button label. Defaults to \"Create\" or \"Save\". */\n\t\tconfirmLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Keep the Confirm button disabled regardless of the form state, for a host that knows the form cannot be completed. */\n\t\tconfirmDisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['close', 'confirm', 'draft-saved'],\n\n\tsetup() {\n\t\t// Pluggable integration registry — used to resolve fields that\n\t\t// declare `referenceType: '<integration-id>'` (AD-18) to the\n\t\t// integration's single-entity widget. Cheap when no such\n\t\t// fields exist.\n\t\tconst { resolveWidget, getById } = useIntegrationRegistry()\n\t\treturn {\n\t\t\tresolveRegistryWidget: resolveWidget,\n\t\t\tgetRegistryIntegration: getById,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tformData: {},\n\t\t\terrors: {},\n\t\t\t/** Field key -> label of the answer a rule filled it in from (`assign`). */\n\t\t\tassignedFrom: {},\n\t\t\t/** The signed-in user's profile for `@me.*` defaults. */\n\t\t\tmeProfile: {},\n\t\t\t/** Files over the inline cap, by property key, uploaded once the object is saved. */\n\t\t\theldFiles: {},\n\t\t\t/** Entries a list file property keeps while its held files upload. */\n\t\t\theldKept: {},\n\t\t\t/** Names of files that did not upload (the dialog offers Retry). */\n\t\t\tuploadFailedNames: [],\n\t\t\t/** The save result the uploads belong to. */\n\t\t\tpendingResult: null,\n\t\t\t/** The file uploading now: `{ file, loaded, total }`, or null. */\n\t\t\tuploadProgress: null,\n\t\t\t/** Fill-over-typed-values question awaiting an answer: `{ changes, resolve }`, or null. */\n\t\t\tpendingReplace: null,\n\t\t\tloading: false,\n\t\t\tresult: null,\n\t\t\t/** Form-level error message (e.g. a server validation failure) shown above the fields without leaving the form phase. */\n\t\t\tformError: null,\n\t\t\tcloseTimeout: null,\n\t\t\t/** Per-field async state: { [fieldKey]: { options: [], loading: false, searchTimeout: null } } */\n\t\t\tasyncState: {},\n\t\t\t/** Per-field editor string for `json` widgets (preserves input between keystrokes even while invalid) */\n\t\t\tjsonDrafts: {},\n\t\t\t/** Per-field parse-error messages for `json` widgets (blocks confirm) */\n\t\t\tjsonErrors: {},\n\t\t\t/** Child rows as loaded, per child-records field, to diff against on save. */\n\t\t\tchildOriginals: {},\n\t\t\t/** Row problems reported by each child-records field. */\n\t\t\tchildProblems: {},\n\t\t\t/**\n\t\t\t * Resolved labels for `$ref` object-reference values, keyed by UUID:\n\t\t\t * `{ [uuid]: '<human label>' }`. Populated as reference options load\n\t\t\t * and when an edit-mode UUID is resolved by id, so the select shows\n\t\t\t * the human name for the currently-stored UUID(s). The stored value\n\t\t\t * itself always remains the UUID — this is display-only.\n\t\t\t */\n\t\t\treferenceLabels: {},\n\t\t\t/** Concept-scheme fields whose options request failed or came back empty. */\n\t\t\tcodedFailed: {},\n\t\t\t/** Scheme of each coded field, as OpenRegister reported it (for the notation route). */\n\t\t\tcodedSchemes: {},\n\t\t\t/**\n\t\t\t * Cross-app semantic-reference resolutions (ADR-048), keyed by the\n\t\t\t * semantic-type URI: `{ [uri]: { status, resolved, registerSlug,\n\t\t\t * schemaSlug, appId } }`. `status` is 'loading' | 'done'. Populated\n\t\t\t * once per URI by `resolveSemanticReferences()` (called from\n\t\t\t * `created()`), so the discovery endpoint is hit at most once per\n\t\t\t * distinct URI, not per render.\n\t\t\t */\n\t\t\tsemanticResolutions: {},\n\t\t\t/** Field keys the user has actually edited this session (used to avoid re-validating untouched persisted server values) */\n\t\t\ttouchedFields: {},\n\t\t\t/**\n\t\t\t * Schema properties contributed by the record's own data rather\n\t\t\t * than its schema — the extra questions a chosen case type (or\n\t\t\t * product line, or segment) asks. Keyed by the same prefixed id\n\t\t\t * the form data uses. Replaced wholesale whenever a driving value\n\t\t\t * changes; `{}` while nothing is chosen. See utils/dynamicProperties.\n\t\t\t */\n\t\t\tdynamicProperties: {},\n\t\t\t/** Prefixed keys among `dynamicProperties` that must be answered. */\n\t\t\tdynamicRequired: [],\n\t\t\t/**\n\t\t\t * Which declaration each dynamic field came from, keyed by its\n\t\t\t * prefixed key. A schema may declare more than one driving\n\t\t\t * property, and their answers go to different value schemas — an\n\t\t\t * answer that cannot name its own declaration would be written to\n\t\t\t * all of them.\n\t\t\t */\n\t\t\tdynamicOwners: {},\n\t\t\t/** Definition records behind the dynamic fields, for the confirm payload. */\n\t\t\tdynamicDefinitions: [],\n\t\t\t/** Whether a definitions fetch is in flight (the fields are announced as loading rather than absent). */\n\t\t\tdynamicLoading: false,\n\t\t\t/**\n\t\t\t * The driver signature the currently-loaded `dynamicProperties`\n\t\t\t * belong to. A fetch that resolves after its driver already moved\n\t\t\t * on is discarded against this, so a fast second pick cannot be\n\t\t\t * overwritten by the first pick's slower response.\n\t\t\t */\n\t\t\tdynamicToken: '',\n\t\t\t/**\n\t\t\t * The prefill signature the most recent prefill ran for. A slower\n\t\t\t * earlier fetch is discarded against this, the same way\n\t\t\t * `dynamicToken` guards the definitions fetch.\n\t\t\t */\n\t\t\tprefillToken: '',\n\t\t\t/**\n\t\t\t * The inline create in progress, or null: `{ field, schema,\n\t\t\t * register, slug, initialData, resolve }`. Set when \"Create\" is\n\t\t\t * chosen on a select-or-create reference; renders a nested form\n\t\t\t * for the referenced schema.\n\t\t\t */\n\t\t\tnestedCreate: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Where this form's draft lives, or '' when recovery is off.\n\t\t *\n\t\t * @return {string} The storage key.\n\t\t */\n\t\tformDraftKey() {\n\t\t\tif (this.recoverDraft !== true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\treturn draftKey({\n\t\t\t\tappId: this.draftAppId,\n\t\t\t\tschema: (this.schema?.slug || this.schema?.title || ''),\n\t\t\t\tobjectId: (this.item?.id || this.item?.uuid || ''),\n\t\t\t\tuserId: this.draftUserId,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether \"Save draft\" may be offered.\n\t\t *\n\t\t * Both halves are required. `allowDraft` is the host asking for it; the\n\t\t * schema declaring `draftField` is what makes it possible. Without the\n\t\t * property OpenRegister drops the marker on save with a 200 and an\n\t\t * object back, and the record reads as published.\n\t\t *\n\t\t * @return {boolean} True when the button is offered.\n\t\t */\n\t\tcanSaveDraft() {\n\t\t\tif (this.allowDraft !== true || !this.draftField) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn Object.hasOwn((this.schema?.properties || {}), this.draftField)\n\t\t},\n\n\t\t/**\n\t\t * What the draft indicator announces, or '' when it says nothing.\n\t\t *\n\t\t * @return {string} The text.\n\t\t */\n\t\tdraftIndicatorLabel() {\n\t\t\treturn draftIndicatorText(this.draftState, this.draftSavedAt)\n\t\t},\n\n\t\tisCreateMode() {\n\t\t\treturn !this.item\n\t\t},\n\n\t\tschemaTitle() {\n\t\t\t// Schema titles are authored in English as the canonical source, the\n\t\t\t// same as the property titles `fieldsFromSchema` translates — so the\n\t\t\t// heading of a Dutch form does not read \"Edit Time entry\".\n\t\t\tif (this.schema && this.schema.title) {\n\t\t\t\treturn this.cnTranslate(this.schema.title)\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Item')\n\t\t},\n\n\t\t/**\n\t\t * Whether the record being edited is a stored draft: drafts are on, the\n\t\t * schema declares the marker, and the record has it set. Such a record\n\t\t * shows \"(draft)\" in the title and is published, not merely saved.\n\t\t *\n\t\t * @return {boolean} True for a draft record.\n\t\t */\n\t\tisDraftRecord() {\n\t\t\treturn this.canSaveDraft && !!this.item && this.item[this.draftField] === true\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\tif (this.dialogTitle) {\n\t\t\t\treturn this.cnTranslate(this.dialogTitle)\n\t\t\t}\n\t\t\tif (this.isDraftRecord) {\n\t\t\t\treturn t('nextcloud-vue', 'Edit {title} (draft)', { title: this.schemaTitle })\n\t\t\t}\n\t\t\treturn this.isCreateMode\n\t\t\t\t? t('nextcloud-vue', 'Create {title}', { title: this.schemaTitle })\n\t\t\t\t: t('nextcloud-vue', 'Edit {title}', { title: this.schemaTitle })\n\t\t},\n\n\t\tresolvedConfirmLabel() {\n\t\t\tif (this.confirmLabel) {\n\t\t\t\treturn this.confirmLabel\n\t\t\t}\n\t\t\tif (this.isDraftRecord) {\n\t\t\t\treturn t('nextcloud-vue', 'Publish')\n\t\t\t}\n\t\t\treturn this.isCreateMode ? t('nextcloud-vue', 'Create') : t('nextcloud-vue', 'Save')\n\t\t},\n\n\t\tresolvedSuccessText() {\n\t\t\tif (this.successText) {\n\t\t\t\treturn this.successText\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{title} saved successfully.', { title: this.schemaTitle })\n\t\t},\n\n\t\t/** Whether all required fields have a non-empty value (hidden fields are skipped) */\n\t\trequiredFieldsFilled() {\n\t\t\treturn this.visibleFields\n\t\t\t\t.filter((f) => f.required)\n\t\t\t\t.every((f) => {\n\t\t\t\t\tconst val = this.formData[f.key]\n\t\t\t\t\tif (val === null || val === undefined || val === '') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (Array.isArray(val) && val.length === 0) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t},\n\n\t\t/** Whether every `json` widget currently parses successfully */\n\t\tjsonFieldsValid() {\n\t\t\treturn Object.keys(this.jsonErrors).every((k) => !this.jsonErrors[k])\n\t\t},\n\n\t\t/**\n\t\t * The schema's `x-openregister-extends-form` declarations — properties\n\t\t * whose chosen value brings further fields with it.\n\t\t *\n\t\t * Empty for the overwhelming majority of schemas, which is what keeps\n\t\t * the whole mechanism free: no declaration, no fetch, no watcher work.\n\t\t *\n\t\t * @return {Array<{key: string, config: object}>} The declarations.\n\t\t */\n\t\textendsDeclarations() {\n\t\t\treturn this.fields ? [] : extendsFormDeclarations(this.schema)\n\t\t},\n\n\t\t/**\n\t\t * A stable signature of every driving property's current value.\n\t\t *\n\t\t * Watched instead of the individual fields so one handler covers a\n\t\t * schema with several declarations, and so re-selecting the same value\n\t\t * does not re-fetch.\n\t\t *\n\t\t * @return {string} The signature ('' when nothing is chosen).\n\t\t */\n\t\tdynamicDriverToken() {\n\t\t\treturn this.extendsDeclarations\n\t\t\t\t.map(({ key }) => `${key}=${this.dynamicDriverValue(this.formData[key])}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * The schema's `x-openregister-prefill` declarations — properties\n\t\t * whose chosen record answers OTHER fields of this same form.\n\t\t *\n\t\t * Create mode only. In edit mode a blank field is a decision someone\n\t\t * already made about an existing record, and filling it because the\n\t\t * case type has an opinion would rewrite that decision on open.\n\t\t *\n\t\t * @return {Array<{key: string, config: object}>} The declarations.\n\t\t */\n\t\tprefillDecls() {\n\t\t\tif (this.fields || !this.isCreateMode) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn prefillDeclarations(this.schema)\n\t\t},\n\n\t\t/**\n\t\t * A stable signature of every prefilling property's current value.\n\t\t *\n\t\t * @return {string} The signature; changes drive a prefill.\n\t\t */\n\t\tprefillDriverToken() {\n\t\t\treturn this.prefillDecls\n\t\t\t\t.map(({ key }) => `${key}=${this.dynamicDriverValue(this.formData[key])}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * Every property whose `x-relation-filter` points at another FIELD of\n\t\t * this same form, paired with that field's key.\n\t\t *\n\t\t * `{ caseType: '@object.caseType' }` on a `status` picker says \"only\n\t\t * the statuses of the case type chosen above\". Only `@object.<key>`\n\t\t * tokens qualify: a literal value or `@objectId` cannot change while\n\t\t * the dialog is open, so neither can invalidate an option list.\n\t\t *\n\t\t * @return {Array<{key: string, drivers: string[]}>} Field key => the form keys it depends on.\n\t\t */\n\t\trelationFilterDecls() {\n\t\t\tconst properties = (this.schema && this.schema.properties) || {}\n\t\t\tconst decls = []\n\t\t\tfor (const [key, prop] of Object.entries(properties)) {\n\t\t\t\tconst filter = prop && prop['x-relation-filter']\n\t\t\t\tif (!filter || typeof filter !== 'object') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst drivers = Object.values(filter)\n\t\t\t\t\t.filter((v) => typeof v === 'string' && v.startsWith('@object.'))\n\t\t\t\t\t.map((v) => v.slice('@object.'.length))\n\t\t\t\tif (drivers.length > 0) {\n\t\t\t\t\tdecls.push({ key, drivers })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn decls\n\t\t},\n\n\t\t/**\n\t\t * A signature of the values concept-scheme fields take as their context.\n\t\t *\n\t\t * @return {string} A change means some coded field's options are stale.\n\t\t */\n\t\tcodedContextToken() {\n\t\t\treturn this.resolvedFields\n\t\t\t\t.filter((f) => f.codeList && f.codeList.contextProperty)\n\t\t\t\t.map((f) => `${f.key}:${String(this.formData[f.codeList.contextProperty] ?? '')}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * A stable signature of every value a relation filter depends on.\n\t\t *\n\t\t * @return {string} The signature; a change means some picker's options are stale.\n\t\t */\n\t\trelationFilterToken() {\n\t\t\treturn this.relationFilterDecls\n\t\t\t\t.flatMap(({ drivers }) => drivers)\n\t\t\t\t.map((key) => `${key}=${this.dynamicDriverValue(this.formData[key])}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * Fields derived from `dynamicProperties`, through the SAME engine the\n\t\t * schema's own fields go through — so a definition gets its widget,\n\t\t * enum labels, validation and description handling from one place\n\t\t * rather than a parallel implementation that drifts.\n\t\t *\n\t\t * @return {object[]} The dynamic fields, ordered after the schema's own.\n\t\t */\n\t\tdynamicFields() {\n\t\t\tif (Object.keys(this.dynamicProperties).length === 0) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn fieldsFromSchema(\n\t\t\t\t{ properties: this.dynamicProperties, required: this.dynamicRequired },\n\t\t\t\t{ translate: this.cnTranslate },\n\t\t\t)\n\t\t},\n\n\t\tresolvedFields() {\n\t\t\t// Manual fields take priority\n\t\t\tconst base = this.fields\n\t\t\t\t? this.fields\n\t\t\t\t: fieldsFromSchema(this.schema, {\n\t\t\t\t\t\texclude: this.excludeFields,\n\t\t\t\t\t\tinclude: this.includeFields,\n\t\t\t\t\t\toverrides: this.fieldOverrides,\n\t\t\t\t\t\thideTenant: true,\n\t\t\t\t\t\ttranslate: this.cnTranslate,\n\t\t\t\t\t\tlanguage: getLanguage(),\n\t\t\t\t\t}).concat(this.dynamicFields)\n\n\t\t\t// Render locked fields (parent references seeded via initialData) as\n\t\t\t// read-only so the disabled binding on every widget branch applies.\n\t\t\tif (!this.lockedFields.length) {\n\t\t\t\treturn base\n\t\t\t}\n\t\t\treturn base.map((field) => (\n\t\t\t\tthis.lockedFields.includes(field.key)\n\t\t\t\t\t? { ...field, readOnly: true }\n\t\t\t\t\t: field\n\t\t\t))\n\t\t},\n\n\t\t/**\n\t\t * Fields filtered through their per-field `condition` / `visibleWhen`\n\t\t * descriptor. Fields without a condition are always visible.\n\t\t *\n\t\t * The template iterates this computed instead of `resolvedFields`,\n\t\t * so hidden fields don't render at all. A watcher (below) clears\n\t\t * the form-data value for any field that transitions from visible\n\t\t * to hidden, so stale state is never submitted.\n\t\t *\n\t\t * @return {object[]} The visible subset of `resolvedFields`.\n\t\t */\n\t\tvisibleFields() {\n\t\t\treturn this.resolvedFields\n\t\t\t\t.filter((field) => shouldShow(field, this.formData))\n\t\t\t\t.map((field) => this.applySemanticResolution(field))\n\t\t\t\t.map((field) => this.degradeUnresolvableReference(field))\n\t\t\t\t.map((field) => this.applyCodePicker(field))\n\t\t},\n\n\t\t/**\n\t\t * The schema properties that hold the record's tenant, which the form\n\t\t * hides (see `isTenantProperty`) and fills itself: from the record\n\t\t * being edited, else the tenant context the app provides. A key a `fieldOverrides[key].hidden === false`\n\t\t * brings back is a normal field again and is not listed here.\n\t\t *\n\t\t * @return {string[]} The hidden tenant property keys.\n\t\t */\n\t\ttenantKeys() {\n\t\t\tif (this.fields || !this.schema || !this.schema.properties) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn Object.entries(this.schema.properties)\n\t\t\t\t.filter(([key, prop]) => {\n\t\t\t\t\tconst override = this.fieldOverrides && this.fieldOverrides[key]\n\t\t\t\t\treturn !(override && override.hidden === false) && isTenantProperty(key, prop)\n\t\t\t\t})\n\t\t\t\t.map(([key]) => key)\n\t\t},\n\t},\n\n\twatch: {\n\t\t/**\n\t\t * Keep the draft in step with what is typed.\n\t\t *\n\t\t * Deep, because a field writes into `formData[key]` rather than\n\t\t * replacing the object. Debounced in the mixin so a long form is not\n\t\t * one storage write per keystroke.\n\t\t */\n\t\tformData: {\n\t\t\tdeep: true,\n\t\t\thandler(values) {\n\t\t\t\t// 🔴 THE FIRST CHANGE IS THE COMPONENT SEEDING THE FORM, NOT\n\t\t\t\t// SOMEBODY TYPING. Writing a draft for it stores the empty\n\t\t\t\t// form over a real one, and the indicator announces \"Saving\n\t\t\t\t// draft\" about work nobody did — an instrument reporting\n\t\t\t\t// activity that did not happen, which is the whole class of\n\t\t\t\t// defect this library keeps paying down.\n\t\t\t\tif (this.draftSeeded !== true) {\n\t\t\t\t\tthis.draftSeeded = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Nothing is written while a draft is being offered: the user\n\t\t\t\t// has not chosen yet, and overwriting the stored draft with the\n\t\t\t\t// empty form they are looking at would destroy the thing they\n\t\t\t\t// are about to be asked about.\n\t\t\t\tif (!this.formDraftKey || this.draftOffer !== null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tthis.scheduleDraftWrite(this.formDraftKey, values)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Re-seed the form when the record changes — WITHOUT discarding\n\t\t * edits the user has already made to the record now arriving.\n\t\t *\n\t\t * A host can bind `item` to a value that is not resolved yet:\n\t\t * CnDetailPage binds it to `currentObject`, a read of the object\n\t\t * store that is `null` until the page's own fetch lands. The dialog\n\t\t * therefore opens over a record that has not arrived, renders every\n\t\t * field blank (a null `item` is create mode), and re-seeds when the\n\t\t * record shows up. Re-seeding wholesale at that point overwrites\n\t\t * whatever was typed in between, so Save submits the PRISTINE record\n\t\t * and the edit is lost with a 200 and no error anywhere.\n\t\t *\n\t\t * Seed from the record, then put the user's own values back on top:\n\t\t * untouched fields take the record's value (a whole-object PUT must\n\t\t * not blank them), touched fields keep what the user typed.\n\t\t *\n\t\t * @param {object|null} newItem The record now bound.\n\t\t * @param {object|null} oldItem The record previously bound.\n\t\t */\n\t\titem: {\n\t\t\timmediate: true,\n\t\t\thandler(newItem, oldItem) {\n\t\t\t\t// Scoped to the record the values were typed into. Swapping\n\t\t\t\t// the dialog to a DIFFERENT record starts from that record,\n\t\t\t\t// so an edit can never leak from one row onto another.\n\t\t\t\tconst pending = this.isSameRecord(oldItem, newItem)\n\t\t\t\t\t? this.pendingUserEdits()\n\t\t\t\t\t: null\n\t\t\t\tthis.initFormData(newItem)\n\t\t\t\tthis.restoreUserEdits(pending)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * When a field transitions from visible to hidden, clear its\n\t\t * form-data value so a stale (now-irrelevant) value isn't\n\t\t * carried into the submitted payload. We diff key lists rather\n\t\t * than mutating during the computed itself.\n\t\t *\n\t\t * @param {object[]} newFields The new visible field set.\n\t\t * @param {object[]} oldFields The previous visible field set.\n\t\t */\n\t\t/**\n\t\t * Load the fields a newly-chosen driving value brings with it.\n\t\t *\n\t\t * Immediate, because edit mode opens with the value already set and\n\t\t * the stored answers are unreadable without their definitions.\n\t\t *\n\t\t * @param {string} token The new driver signature.\n\t\t */\n\t\tdynamicDriverToken: {\n\t\t\timmediate: true,\n\t\t\thandler(token) {\n\t\t\t\tthis.loadDynamicProperties(token)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Prefill the fields the chosen record answers.\n\t\t *\n\t\t * NOT immediate, unlike the definitions watcher above. Immediate would\n\t\t * fire once on open with an empty driver and, worse, would run against\n\t\t * whatever `initialData` seeded — so a value a caller passed in\n\t\t * deliberately could be treated as \"empty enough\" on the very first\n\t\t * tick. Prefill is a response to a person choosing something.\n\t\t *\n\t\t * @param {string} token The new driver signature.\n\t\t */\n\t\tprefillDriverToken(token) {\n\t\t\tthis.applyPrefill(token)\n\t\t},\n\n\t\t/**\n\t\t * Refetch a scoped picker's options when the value it is scoped BY\n\t\t * changes.\n\t\t *\n\t\t * `fetchReferenceOptions` resolves `x-relation-filter` against the\n\t\t * live form data, and `initAsyncFields` calls it once, at open. On a\n\t\t * CREATE the form is empty at that moment, so `@object.caseType`\n\t\t * resolves to nothing, the filter entry is dropped (\"unfiltered beats\n\t\t * an empty picker\") and the fetch returns the first hundred rows of\n\t\t * the referenced schema. Nothing ever fetched again, so choosing a\n\t\t * case type left the status picker offering every case type's\n\t\t * statuses, four of them named \"Received\" with nothing to tell them\n\t\t * apart. Reported against dossiq's New case form; the same shape sits\n\t\t * on four more of its shipped properties.\n\t\t *\n\t\t * NOT immediate. The open-time load is `initAsyncFields`'s job, and\n\t\t * firing here as well would send two identical requests on every open.\n\t\t *\n\t\t * The new signature is not read: which fields to refetch comes from\n\t\t * `relationFilterDecls`, and the values themselves are resolved from\n\t\t * `formData` by `fetchReferenceOptions`.\n\t\t */\n\t\tcodedContextToken() {\n\t\t\t// Ask again for the options that follow another field. The chosen\n\t\t\t// value is kept and marked, never cleared behind the user's back.\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field.codeList && field.codeList.contextProperty && this.asyncState[field.key]) {\n\t\t\t\t\tthis.loadAsyncOptions(field, '')\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\trelationFilterToken() {\n\t\t\tconst stale = new Set(this.relationFilterDecls.map(({ key }) => key))\n\t\t\tif (stale.size === 0) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// The TRANSFORMED fields, the same list initAsyncFields iterates:\n\t\t\t// a resolved cross-app semantic reference is a `$ref` picker by\n\t\t\t// then and scopes itself the same way.\n\t\t\tfor (const field of this.resolvedFields.map((f) => this.applySemanticResolution(f))) {\n\t\t\t\tif (!stale.has(field.key)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (!this.isAsyncEnum(field) && !this.isAsyncItemsEnum(field)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// OPTIONS ONLY, the value is left alone. Clearing a selection\n\t\t\t\t// that the new scope no longer offers looks tempting, and it\n\t\t\t\t// would fight `applyPrefill`: `x-openregister-prefill` reacts\n\t\t\t\t// to the same change, runs from a watcher declared above this\n\t\t\t\t// one, and its whole job is to WRITE the field this one would\n\t\t\t\t// then blank. Where a prefill exists it replaces the stale\n\t\t\t\t// value already; where none does, a value the picker cannot\n\t\t\t\t// show is the pre-existing behaviour and not what was reported.\n\t\t\t\tthis.loadAsyncOptions(field, '')\n\t\t\t}\n\t\t},\n\n\t\tvisibleFields(newFields, oldFields) {\n\t\t\tif (!Array.isArray(oldFields)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst newKeys = new Set(newFields.map((f) => f.key))\n\t\t\tfor (const oldField of oldFields) {\n\t\t\t\tif (!newKeys.has(oldField.key) && Object.hasOwn(this.formData, oldField.key)) {\n\t\t\t\t\tdelete this.formData[oldField.key]\n\t\t\t\t\tif (Object.hasOwn(this.errors, oldField.key)) {\n\t\t\t\t\t\tdelete this.errors[oldField.key]\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.hasOwn(this.jsonErrors, oldField.key)) {\n\t\t\t\t\t\tdelete this.jsonErrors[oldField.key]\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.hasOwn(this.jsonDrafts, oldField.key)) {\n\t\t\t\t\t\tdelete this.jsonDrafts[oldField.key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// 🔴 OFFERED, NEVER APPLIED. The values are held and the user decides.\n\t\t// Silently refilling a form is how somebody submits last week's answers\n\t\t// without noticing they were there, and a form that fills itself is\n\t\t// indistinguishable from one the server prefilled.\n\t\tif (this.formDraftKey) {\n\t\t\tthis.draftOffer = readDraft(this.formDraftKey, {\n\t\t\t\tobjectUpdated: (this.item?.updated || this.item?.['@self']?.updated || ''),\n\t\t\t})\n\t\t}\n\n\t\t// Non-reactive cache of built enum option lists, keyed by field key.\n\t\t// Kept off `data` so Vue doesn't make the option objects reactive —\n\t\t// stable identity is what lets NcSelect recognise the selected option.\n\t\tthis._enumOptionCache = {}\n\t\t// ADR-048: resolve any cross-app semantic-reference URIs once, up\n\t\t// front. Async — fields render disabled (loading) until each URI\n\t\t// resolves, then re-render as a picker (resolved) or disabled+tooltip\n\t\t// (unresolved).\n\t\tthis.resolveSemanticReferences()\n\t},\n\n\tbeforeUnmount() {\n\t\tfor (const state of Object.values(this.asyncState)) {\n\t\t\tif (state.searchTimeout) {\n\t\t\t\tclearTimeout(state.searchTimeout)\n\t\t\t}\n\t\t}\n\t\tif (this.closeTimeout) {\n\t\t\tclearTimeout(this.closeTimeout)\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Resolve a field's reference integration widget, if any.\n\t\t * Returns the integration's single-entity widget component\n\t\t * (AD-19 fallback to its main `widget`) when the field\n\t\t * declares a `referenceType` that maps to a registered\n\t\t * integration; null otherwise.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object|null} Vue component, or null.\n\t\t */\n\t\tresolveReferenceWidget(field) {\n\t\t\tif (!field || typeof field.referenceType !== 'string' || field.referenceType === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (typeof this.getRegistryIntegration === 'function' && this.getRegistryIntegration(field.referenceType) === null) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.resolveRegistryWidget(field.referenceType, 'single-entity')\n\t\t},\n\n\t\t/**\n\t\t * Props passed to a reference integration widget: the current\n\t\t * value, the rendering surface, and the object context (from\n\t\t * the `referenceContext` prop).\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} Props object for the widget component.\n\t\t */\n\t\treferenceWidgetProps(field) {\n\t\t\treturn {\n\t\t\t\tsurface: 'single-entity',\n\t\t\t\tvalue: this.formData[field.key],\n\t\t\t\tfield,\n\t\t\t\t...(this.referenceContext || {}),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve every distinct cross-app semantic-reference URI (ADR-048)\n\t\t * declared by the schema's fields against OpenRegister's discovery\n\t\t * endpoint. Fires once per URI (deduped via `semanticResolutions`),\n\t\t * kicked off from `created()`. Each entry moves 'loading' → 'done';\n\t\t * while loading, the field renders disabled. Never throws.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tresolveSemanticReferences() {\n\t\t\tconst uris = new Set()\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field && typeof field.referenceSemanticType === 'string' && field.referenceSemanticType !== '') {\n\t\t\t\t\turis.add(field.referenceSemanticType)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const uri of uris) {\n\t\t\t\tif (this.semanticResolutions[uri]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.semanticResolutions[uri] = { status: 'loading', resolved: false }\n\t\t\t\tthis.resolveSemanticReference(uri).then((result) => {\n\t\t\t\t\tthis.semanticResolutions[uri] = { status: 'done', ...result }\n\t\t\t\t\t// Re-init async fields so a newly-resolved reference picker\n\t\t\t\t\t// starts fetching its options, and resolve any edit-mode\n\t\t\t\t\t// label for a value already stored on the field.\n\t\t\t\t\tif (result.resolved) {\n\t\t\t\t\t\tthis.initAsyncFields()\n\t\t\t\t\t\tthis.resolveInitialReferenceLabels()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Call the OpenRegister discovery endpoint for one semantic-type URI.\n\t\t * Degrades to `{ resolved: false }` on any error (including a 404 when\n\t\t * the route isn't present yet) — never crashes the form.\n\t\t *\n\t\t * @param {string} uri The canonical semantic-type URI.\n\t\t * @return {Promise<{resolved: boolean, registerSlug: string|null, schemaSlug: string|null, appId: string|null}>}\n\t\t */\n\t\tasync resolveSemanticReference(uri) {\n\t\t\tconst empty = { resolved: false, registerSlug: null, schemaSlug: null, appId: null }\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl(SEMANTIC_RESOLVE_ENDPOINT)\n\t\t\t\tconst res = await axios.get(url, { params: { uri } })\n\t\t\t\tconst data = (res && res.data) || {}\n\t\t\t\tif (data.resolved === true && data.registerSlug && data.schemaSlug) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tresolved: true,\n\t\t\t\t\t\tregisterSlug: String(data.registerSlug),\n\t\t\t\t\t\tschemaSlug: String(data.schemaSlug),\n\t\t\t\t\t\tappId: data.appId !== null && data.appId !== undefined ? String(data.appId) : null,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn empty\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: semantic reference resolve failed for \"${uri}\":`, err)\n\t\t\t\treturn empty\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a semantic-reference field's URI has resolved to an\n\t\t * installed provider schema. False while loading or when nothing\n\t\t * implements the URI.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {boolean}\n\t\t */\n\t\tisSemanticResolved(field) {\n\t\t\tif (!field || !field.referenceSemanticType) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst entry = this.semanticResolutions[field.referenceSemanticType]\n\t\t\treturn !!(entry && entry.status === 'done' && entry.resolved)\n\t\t},\n\n\t\t/**\n\t\t * Whether a semantic-reference field's URI is still being resolved.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {boolean}\n\t\t */\n\t\tisSemanticLoading(field) {\n\t\t\tif (!field || !field.referenceSemanticType) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst entry = this.semanticResolutions[field.referenceSemanticType]\n\t\t\treturn !entry || entry.status === 'loading'\n\t\t},\n\n\t\t/**\n\t\t * Tooltip / helper copy for an unresolved semantic-reference field:\n\t\t * \"Install {App} to pick a {Type}.\" `Type` is\n\t\t * derived from the URI's last path segment; the app label from\n\t\t * `referenceSemanticApp`, or a shorter sentence naming only the type\n\t\t * when the property names no provider.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tsemanticUnavailableText(field) {\n\t\t\tconst uri = (field && field.referenceSemanticType) || ''\n\t\t\tconst segment = uri.split(/[/#]/).filter(Boolean).pop() || uri\n\t\t\tconst typeLabel = segment || t('nextcloud-vue', 'this reference')\n\t\t\tconst appLabel = (field && field.referenceSemanticApp) || ''\n\t\t\t// TWO SENTENCES, NOT ONE WITH A FALLBACK NOUN. The single template\n\t\t\t// read \"The {appLabel} app that provides {typeLabel} is not\n\t\t\t// installed.\" and the no-app case substituted the literal\n\t\t\t// \"supporting app\", so a property that names no provider rendered\n\t\t\t// \"The supporting app app that provides Requester is not\n\t\t\t// installed.\" A filler noun that has to fit a slot built for a\n\t\t\t// proper name is how a doubled word ships. Where a provider IS\n\t\t\t// named the string can say what to do about it; where none is,\n\t\t\t// naming the missing type is all that is true.\n\t\t\tif (appLabel !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Install {appLabel} to pick a {typeLabel}.', { appLabel, typeLabel })\n\t\t\t}\n\n\t\t\treturn t('nextcloud-vue', 'No installed app provides {typeLabel}.', { typeLabel })\n\t\t},\n\n\t\t/**\n\t\t * Transform a resolved cross-app semantic-reference field (ADR-048)\n\t\t * into a normal `$ref` reference field so it flows through the\n\t\t * existing searchable-object-picker machinery, but pointed at the\n\t\t * PROVIDER'S register (cross-app — the provider lives in another\n\t\t * app's register). Unresolved/loading fields and non-semantic fields\n\t\t * are returned unchanged.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} The (possibly transformed) field.\n\t\t */\n\t\tapplySemanticResolution(field) {\n\t\t\tif (!field || !field.referenceSemanticType) {\n\t\t\t\treturn field\n\t\t\t}\n\t\t\tconst entry = this.semanticResolutions[field.referenceSemanticType]\n\t\t\tif (!entry || entry.status !== 'done' || !entry.resolved) {\n\t\t\t\treturn field\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...field,\n\t\t\t\twidget: 'select',\n\t\t\t\t// Carry the provider register on the reference so the fetch\n\t\t\t\t// targets registerSlug (cross-app), not the form's own register.\n\t\t\t\treference: { schema: entry.schemaSlug, multiple: false, register: entry.registerSlug },\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The id of a record, wherever it carries one. An OpenRegister\n\t\t * object holds its id in `@self`, not only at the top level.\n\t\t *\n\t\t * @param {object|null} record The record to read.\n\t\t * @return {string} The id, or '' when the record has none.\n\t\t */\n\t\trecordId(record) {\n\t\t\tif (!record || typeof record !== 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn String(record.id ?? record['@self']?.id ?? record.uuid ?? '')\n\t\t},\n\n\t\t/**\n\t\t * Whether two `item` values are the same record, for the purpose of\n\t\t * carrying pending edits across a re-seed.\n\t\t *\n\t\t * A record ARRIVING (null/id-less -> record) is the same record as\n\t\t * far as the user is concerned: they are filling in the form that is\n\t\t * already on screen. Two different ids are not.\n\t\t *\n\t\t * @param {object|null} oldItem The record previously bound.\n\t\t * @param {object|null} newItem The record now bound.\n\t\t * @return {boolean} True when edits may be carried over.\n\t\t */\n\t\tisSameRecord(oldItem, newItem) {\n\t\t\tconst oldId = this.recordId(oldItem)\n\t\t\tif (oldId === '') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn oldId === this.recordId(newItem)\n\t\t},\n\n\t\t/**\n\t\t * Snapshot the fields the user has edited by hand, so a record that\n\t\t * arrives later can be merged UNDER them rather than over them.\n\t\t *\n\t\t * @return {object|null} The pending edits, or null when there are none.\n\t\t */\n\t\tpendingUserEdits() {\n\t\t\tconst keys = Object.keys(this.touchedFields || {})\n\t\t\tif (keys.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst edits = {}\n\t\t\tfor (const key of keys) {\n\t\t\t\tif (Object.hasOwn(this.formData, key)) {\n\t\t\t\t\tedits[key] = this.formData[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Object.keys(edits).length > 0 ? edits : null\n\t\t},\n\n\t\t/**\n\t\t * Re-apply edits captured by `pendingUserEdits()` after a re-seed,\n\t\t * restoring their touched state with them.\n\t\t *\n\t\t * @param {object|null} edits The snapshot to re-apply.\n\t\t * @return {void}\n\t\t */\n\t\trestoreUserEdits(edits) {\n\t\t\tif (!edits) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor (const key of Object.keys(edits)) {\n\t\t\t\tthis.formData[key] = edits[key]\n\t\t\t\tthis.touchedFields[key] = true\n\t\t\t}\n\t\t},\n\n\t\tinitFormData(item) {\n\t\t\tif (item) {\n\t\t\t\t// Edit mode: clone item data, then normalise persisted\n\t\t\t\t// date/datetime values so an untouched field re-submits a\n\t\t\t\t// schema-valid value (the backend stores date-times\n\t\t\t\t// space-separated without a timezone, which would otherwise\n\t\t\t\t// fail the schema's `date-time` format on save).\n\t\t\t\tthis.formData = JSON.parse(JSON.stringify(item))\n\t\t\t\tthis.normalizePersistedDates()\n\t\t\t} else {\n\t\t\t\t// Create mode: initialize with field defaults\n\t\t\t\tconst data = {}\n\t\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\t\tconst tokenDefault = field.defaultToken ? resolveDefaultToken(field.defaultToken, field) : null\n\t\t\t\t\tif (field.default !== null && field.default !== undefined) {\n\t\t\t\t\t\tconst resolved = resolveFilterValue(field.default, { object: this.initialData || {}, me: this.meProfile })\n\t\t\t\t\t\t// A token that cannot resolve yet (the e-mail, until the profile loads) is not a value.\n\t\t\t\t\t\tdata[field.key] = typeof resolved === 'string' && resolved === field.default && resolved.startsWith('@') ? null : resolved\n\t\t\t\t\t} else if (tokenDefault !== null) {\n\t\t\t\t\t\t// `x-default: current-language` / `current-timezone`:\n\t\t\t\t\t\t// a NEW object starts with the user's own value.\n\t\t\t\t\t\tdata[field.key] = tokenDefault\n\t\t\t\t\t} else if (field.widget === 'checkbox') {\n\t\t\t\t\t\tdata[field.key] = false\n\t\t\t\t\t} else if (field.widget === 'tags' || field.widget === 'multiselect' || field.widget === 'user-multiselect' || field.widget === 'group-multiselect') {\n\t\t\t\t\t\tdata[field.key] = []\n\t\t\t\t\t} else if (field.widget === 'code') {\n\t\t\t\t\t\tdata[field.key] = ''\n\t\t\t\t\t} else if (field.widget === 'file' && field.file && field.file.multiple) {\n\t\t\t\t\t\tdata[field.key] = []\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdata[field.key] = null\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Seed create-mode values (parent pre-link from a detail page).\n\t\t\t\tfor (const key of Object.keys(this.initialData || {})) {\n\t\t\t\t\tif (this.initialData[key] !== undefined) {\n\t\t\t\t\t\tdata[key] = this.initialData[key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.formData = data\n\t\t\t}\n\t\t\t// Multi-tenancy auto-fill (multi-tenancy-context REQ-MT-4).\n\t\t\t// When the schema declares an `organisation` field and the\n\t\t\t// active form data does NOT already carry a value for it,\n\t\t\t// stamp the active organisation UUID from the shared\n\t\t\t// tenant context. Explicit values in `item` win — they are\n\t\t\t// already in `formData` at this point so the guard below\n\t\t\t// is correct on both create and edit paths.\n\t\t\tthis._autofillTenant()\n\t\t\tthis.errors = {}\n\t\t\tthis.formError = null\n\t\t\tthis.jsonDrafts = {}\n\t\t\tthis.jsonErrors = {}\n\t\t\tthis.touchedFields = {}\n\t\t\tthis.assignedFrom = {}\n\t\t\tthis.referenceLabels = {}\n\t\t\tthis.initAsyncFields()\n\t\t\tthis.resolveInitialReferenceLabels()\n\t\t\t// A new record opens with what the rules and defaults already know.\n\t\t\tif (!this.item) {\n\t\t\t\tthis.runAssignments(null)\n\t\t\t\tthis.loadProfileDefaults()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fill in `@me.displayName` / `@me.email` defaults the sync resolver could\n\t\t * not (the e-mail is on the profile). Asked for once, and only when a default needs it.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadProfileDefaults() {\n\t\t\tconst needs = this.resolvedFields.filter((f) => typeof f.default === 'string' && /^@me\\.(email|displayName)$/.test(f.default))\n\t\t\tif (needs.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.meProfile = await loadCurrentUserProfile()\n\t\t\tfor (const field of needs) {\n\t\t\t\tconst current = this.formData[field.key]\n\t\t\t\tconst value = resolveFilterValue(field.default, { me: this.meProfile })\n\t\t\t\tif ((current === null || current === undefined || current === '') && !this.touchedFields[field.key] && value !== field.default) {\n\t\t\t\t\tthis.formData[field.key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * For each reference field that already holds a UUID (edit mode),\n\t\t * resolve its label so the select shows the current selection's human\n\t\t * name. Runs after the options load; `resolveReferenceLabel` is a no-op\n\t\t * once the label is cached (so it doesn't re-fetch what the options\n\t\t * load already populated).\n\t\t */\n\t\tresolveInitialReferenceLabels() {\n\t\t\tfor (const field of this.resolvedFields.map((f) => this.applySemanticResolution(f))) {\n\t\t\t\tif (this.isReferenceField(field)) {\n\t\t\t\t\tconst uuid = this.formData[field.key]\n\t\t\t\t\tif (uuid) {\n\t\t\t\t\t\tthis.resolveReferenceLabel(field, uuid)\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isReferenceArrayField(field)) {\n\t\t\t\t\tconst uuids = this.formData[field.key]\n\t\t\t\t\tif (Array.isArray(uuids)) {\n\t\t\t\t\t\tfor (const uuid of uuids) {\n\t\t\t\t\t\t\tthis.resolveReferenceLabel(field, uuid)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isUserField(field)) {\n\t\t\t\t\tconst uid = this.formData[field.key]\n\t\t\t\t\tif (uid) {\n\t\t\t\t\t\tthis.resolveUserLabel(uid)\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isUserArrayField(field)) {\n\t\t\t\t\tconst uids = this.formData[field.key]\n\t\t\t\t\tif (Array.isArray(uids)) {\n\t\t\t\t\t\tfor (const uid of uids) {\n\t\t\t\t\t\t\tthis.resolveUserLabel(uid)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isGroupField(field) || this.isGroupArrayField(field)) {\n\t\t\t\t\tconst value = this.formData[field.key]\n\t\t\t\t\tfor (const gid of (Array.isArray(value) ? value : [value])) {\n\t\t\t\t\t\tif (gid) {\n\t\t\t\t\t\t\tthis.resolveGroupLabel(gid)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Request the options of a concept-scheme field from OpenRegister and\n\t\t * cache their labels. A failed or empty answer flips the field to the\n\t\t * text fallback; a held value the options no longer offer is resolved\n\t\t * through the concept route and marked.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @return {Promise<Array<{id: string, label: string}>>} The options in OpenRegister's order.\n\t\t */\n\t\tasync fetchCodedOptions(field) {\n\t\t\tconst code = field.codeList\n\t\t\tconst params = { schema: String((this.schema && (this.schema.id ?? this.schema.slug)) ?? ''), property: code.property, language: getLanguage() }\n\t\t\tif (code.contextProperty) {\n\t\t\t\tconst ctx = this.formData[code.contextProperty]\n\t\t\t\tif (ctx !== undefined && ctx !== null && ctx !== '') {\n\t\t\t\t\tparams.context = String(ctx)\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet options = []\n\t\t\ttry {\n\t\t\t\tconst response = await axios.get(generateUrl('/apps/openregister/api/vocabulary/options'), { params })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\tif (data.scheme) {\n\t\t\t\t\tthis.codedSchemes = { ...this.codedSchemes, [field.key]: data.scheme }\n\t\t\t\t}\n\t\t\t\toptions = (Array.isArray(data.results) ? data.results : [])\n\t\t\t\t\t.filter((o) => o && o.value !== undefined && o.value !== null)\n\t\t\t\t\t.map((o) => ({ id: String(o.value), label: o.label || o.notation || String(o.value) }))\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: vocabulary options failed for field \"${field.key}\":`, err)\n\t\t\t}\n\t\t\tthis.codedFailed = { ...this.codedFailed, [field.key]: options.length === 0 }\n\t\t\tconst labels = {}\n\t\t\tfor (const o of options) {\n\t\t\t\tlabels[o.id] = o.label\n\t\t\t}\n\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\tconst offered = new Set(options.map((o) => o.id))\n\t\t\tconst held = this.formData[field.key]\n\t\t\tfor (const value of (Array.isArray(held) ? held : [held])) {\n\t\t\t\tif (value !== undefined && value !== null && value !== '' && !offered.has(String(value))) {\n\t\t\t\t\tthis.resolveRetiredCode(field, String(value))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn options\n\t\t},\n\n\t\t/**\n\t\t * Show a held value the scheme no longer offers by its label, marked\n\t\t * \"no longer offered\", resolved through OpenRegister's concept route.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @param {string} value The stored value (uri, or notation per `store`).\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveRetiredCode(field, value) {\n\t\t\tconst marker = t('nextcloud-vue', 'no longer offered')\n\t\t\tlet label = value\n\t\t\ttry {\n\t\t\t\tconst url = field.codeList.store === 'notation'\n\t\t\t\t\t? generateUrl('/apps/openregister/api/vocabulary/concept/notation')\n\t\t\t\t\t: generateUrl('/apps/openregister/api/vocabulary/concept')\n\t\t\t\tconst params = field.codeList.store === 'notation'\n\t\t\t\t\t? { scheme: this.codedSchemes[field.key] || '', notation: value }\n\t\t\t\t\t: { uri: value }\n\t\t\t\tconst response = await axios.get(url, { params, headers: { 'Accept-Language': getLanguage() } })\n\t\t\t\tconst d = response && response.data ? response.data : {}\n\t\t\t\tconst concept = d.concept || d\n\t\t\t\tlabel = concept.label || concept.prefLabel || concept.notation || value\n\t\t\t} catch {\n\t\t\t\t// Unresolvable: the raw value is the best label left.\n\t\t\t}\n\t\t\tthis.referenceLabels = { ...this.referenceLabels, [value]: `${label} (${marker})` }\n\t\t},\n\n\t\t/**\n\t\t * The text shown in the fallback input of a coded field.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @return {string} The held value as text; several values are comma-separated.\n\t\t */\n\t\tcodedFallbackText(field) {\n\t\t\tconst v = this.formData[field.key]\n\t\t\tif (Array.isArray(v)) {\n\t\t\t\treturn v.join(', ')\n\t\t\t}\n\t\t\treturn v === undefined || v === null ? '' : String(v)\n\t\t},\n\n\t\t/**\n\t\t * Write the fallback input back: a string, or an array for several choices.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @param {string} text The typed text.\n\t\t */\n\t\tonCodedFallbackInput(field, text) {\n\t\t\tif (field.codeList.multiple) {\n\t\t\t\tthis.updateField(field.key, text.split(',').map((x) => x.trim()).filter(Boolean))\n\t\t\t} else {\n\t\t\t\tthis.updateField(field.key, text)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve a stored group id to its display name and cache it in\n\t\t * `referenceLabels`. No-op once cached; falls back to the gid.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {string} gid The stored group id.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveGroupLabel(gid) {\n\t\t\tif (!gid || this.referenceLabels[gid]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst option = await resolveNextcloudGroup(gid)\n\t\t\tif (option && option.id) {\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [option.id]: option.label || String(option.id) }\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve a single user UID to its display name (edit mode) and cache it\n\t\t * in `referenceLabels` so the select shows the name instead of the UID.\n\t\t * No-op once the label is cached. Fails soft — `resolveNextcloudUser`\n\t\t * falls back to the UID itself when the name can't be looked up.\n\t\t *\n\t\t * @param {string} uid The stored user UID.\n\t\t */\n\t\tasync resolveUserLabel(uid) {\n\t\t\tif (!uid || this.referenceLabels[uid]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst option = await resolveNextcloudUser(uid)\n\t\t\tif (option && option.id) {\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [option.id]: option.label || String(option.id) }\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Auto-fill the `organisation` field with the active tenant UUID\n\t\t * when the schema declares such a field and no value is already\n\t\t * set (explicit `item` data wins).\n\t\t *\n\t\t * Spec: multi-tenancy-context REQ-MT-4.\n\t\t */\n\t\t_autofillTenant() {\n\t\t\tconst ctx = this._cnTenantContext\n\t\t\tif (!ctx) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst uuid = ctx.activeOrganisationUuid && ctx.activeOrganisationUuid.value\n\t\t\tif (!uuid) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst hasOrgField = this.resolvedFields.some((f) => f.key === 'organisation')\n\t\t\tif (!hasOrgField) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst current = this.formData.organisation\n\t\t\tif (current !== null && current !== undefined && current !== '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.formData.organisation = uuid\n\t\t},\n\n\t\t/**\n\t\t * Whether a form value is empty for tenant-fill purposes.\n\t\t *\n\t\t * @param {unknown} value The value.\n\t\t * @return {boolean} True for null, undefined and ''.\n\t\t */\n\t\t_isEmptyTenantValue(value) {\n\t\t\treturn value === null || value === undefined || value === ''\n\t\t},\n\n\t\t/**\n\t\t * The tenant a hidden tenant property (`tenantKeys`) takes when the\n\t\t * form holds no value for it: the active tenant from the tenant\n\t\t * context the app provides, and nothing else.\n\t\t *\n\t\t * There is deliberately no fallback to OpenRegister's active\n\t\t * organisation. An app's tenant is its own notion (learniq resolves\n\t\t * it from the user's `tenant_id` preference, else the instance id),\n\t\t * and stamping another value would make new records invisible next\n\t\t * to the ones the app wrote itself.\n\t\t *\n\t\t * @return {string|null} The tenant UUID, or null when the app provided none.\n\t\t */\n\t\t_platformTenant() {\n\t\t\tconst ctx = this._cnTenantContext\n\t\t\tconst fromContext = ctx && ctx.activeOrganisationUuid && ctx.activeOrganisationUuid.value\n\t\t\treturn fromContext || null\n\t\t},\n\n\t\t/**\n\t\t * Evaluate a field's conditional-visibility predicate (#327).\n\t\t *\n\t\t * A field is visible when it has no `condition`/`visibleWhen`, or when\n\t\t * its predicate evaluates true against the current `formData`. The\n\t\t * predicate references another field via `field` and one of the\n\t\t * supported operators: `equals`, `notEquals`, `in`, `notIn`,\n\t\t * `truthy`, `falsy`. An unrecognised predicate keeps the field visible\n\t\t * and logs a warning (fail-open — better to show a field than hide it\n\t\t * by accident).\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {boolean} Whether the field should be shown.\n\t\t */\n\t\tfieldVisible(field) {\n\t\t\tconst condition = field.condition || field.visibleWhen\n\t\t\tif (!condition || typeof condition !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tconst target = this.formData[condition.field]\n\n\t\t\tif (Object.hasOwn(condition, 'equals')) {\n\t\t\t\treturn target === condition.equals\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'notEquals')) {\n\t\t\t\treturn target !== condition.notEquals\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'in')) {\n\t\t\t\treturn Array.isArray(condition.in) && condition.in.includes(target)\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'notIn')) {\n\t\t\t\treturn !(Array.isArray(condition.notIn) && condition.notIn.includes(target))\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'truthy')) {\n\t\t\t\treturn condition.truthy ? !!target : !target\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'falsy')) {\n\t\t\t\treturn condition.falsy ? !target : !!target\n\t\t\t}\n\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(\n\t\t\t\t`[CnFormDialog] Field \"${field.key}\" has a condition with no recognised predicate; keeping it visible.`,\n\t\t\t\tcondition,\n\t\t\t)\n\t\t\treturn true\n\t\t},\n\n\t\tupdateField(key, value) {\n\t\t\tthis.formData[key] = value\n\t\t\tthis.touchedFields[key] = true\n\t\t\t// Clear errors when a field is edited\n\t\t\tif (this.errors[key]) {\n\t\t\t\tdelete this.errors[key]\n\t\t\t}\n\t\t\tthis.formError = null\n\t\t\t// A field change may flip the visibility of conditional fields.\n\t\t\t// Drop the form-data of any field that just became hidden so a\n\t\t\t// stale value isn't submitted (#327).\n\t\t\tthis.pruneHiddenFields()\n\t\t\t// A hand edit ends this field's own rules; other fields may follow it.\n\t\t\tdelete this.assignedFrom[key]\n\t\t\tthis.runAssignments([key])\n\t\t},\n\n\t\t/**\n\t\t * Fill in fields from other answers (`assign` rules on the field, for\n\t\t * example through `fieldOverrides`). `changed` names the answers that just\n\t\t * changed; `null` is the pass when the form opens, over empty fields. A\n\t\t * field the person edited by hand is left alone.\n\t\t *\n\t\t * @param {string[]|null} changed Keys that changed.\n\t\t */\n\t\trunAssignments(changed) {\n\t\t\tconst { values, from } = computeAssignments({\n\t\t\t\tfields: this.resolvedFields,\n\t\t\t\tanswers: this.formData,\n\t\t\t\tchanged,\n\t\t\t\tedited: Object.keys(this.touchedFields).filter((k) => this.touchedFields[k]),\n\t\t\t\tctx: { me: this.meProfile, object: this.formData },\n\t\t\t})\n\t\t\tfor (const key of Object.keys(values)) {\n\t\t\t\tthis.formData[key] = values[key]\n\t\t\t\tdelete this.errors[key]\n\t\t\t\tconst source = this.resolvedFields.find((f) => f.key === from[key])\n\t\t\t\tthis.assignedFrom[key] = source ? source.label : from[key]\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Remove `formData` (and any error) for fields that are currently\n\t\t * hidden by their conditional-visibility predicate (#327).\n\t\t */\n\t\tpruneHiddenFields() {\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (!this.fieldVisible(field) && Object.hasOwn(this.formData, field.key)) {\n\t\t\t\t\tdelete this.formData[field.key]\n\t\t\t\t\tif (this.errors[field.key]) {\n\t\t\t\t\t\tdelete this.errors[field.key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the string shown in the CnJsonViewer for a `json`-widget field.\n\t\t * Prefers the unparsed draft (so invalid typing isn't clobbered), falling\n\t\t * back to a pretty-printed stringification of the parsed value in formData.\n\t\t *\n\t\t * @param {object} field Field definition.\n\t\t * @return {string} JSON string for the editor.\n\t\t */\n\t\tjsonStringFor(field) {\n\t\t\tif (Object.hasOwn(this.jsonDrafts, field.key)) {\n\t\t\t\treturn this.jsonDrafts[field.key]\n\t\t\t}\n\t\t\tconst value = this.formData[field.key]\n\t\t\tif (value === null || value === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(value, null, 2)\n\t\t\t} catch {\n\t\t\t\treturn String(value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle input in a `json`-widget CnJsonViewer. Parses on the fly:\n\t\t * on success, the parsed value lands in formData and any previous error\n\t\t * is cleared; on failure, formData keeps its last-known-good value and\n\t\t * `jsonErrors[key]` is set, which surfaces inline and disables confirm.\n\t\t *\n\t\t * @param {object} field Field definition.\n\t\t * @param {string} newString Current editor content.\n\t\t */\n\t\tonJsonFieldInput(field, newString) {\n\t\t\tthis.jsonDrafts[field.key] = newString\n\t\t\tconst trimmed = (newString || '').trim()\n\t\t\tif (!trimmed) {\n\t\t\t\tthis.updateField(field.key, null)\n\t\t\t\tdelete this.jsonErrors[field.key]\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(trimmed)\n\t\t\t\tthis.updateField(field.key, parsed)\n\t\t\t\tdelete this.jsonErrors[field.key]\n\t\t\t} catch (e) {\n\t\t\t\tthis.jsonErrors[field.key] = t('nextcloud-vue', 'Invalid JSON: {msg}', { msg: e.message })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Parse a date/datetime field's stored string into a Date for\n\t\t * NcDateTimePickerNative (which operates in local time). Returns\n\t\t * null for empty/unparseable values.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Date|null}\n\t\t */\n\t\tdateValueFor(field) {\n\t\t\tconst raw = this.formData[field.key]\n\t\t\tif (!raw) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Accept both ISO ('2026-10-15T14:30:00Z') and OpenRegister's\n\t\t\t// space-separated persisted form ('2026-10-15 14:30:00'); the\n\t\t\t// optional trailing 'Z'/offset is ignored for the local-time picker.\n\t\t\tconst parts = String(raw).match(/^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2}))?/)\n\t\t\tif (parts) {\n\t\t\t\treturn new Date(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]), Number(parts[4] || 0), Number(parts[5] || 0))\n\t\t\t}\n\t\t\tconst fallback = new Date(raw)\n\t\t\treturn isNaN(fallback.getTime()) ? null : fallback\n\t\t},\n\n\t\t/**\n\t\t * Serialise a Date into the canonical string a date/datetime field\n\t\t * stores and submits: 'YYYY-MM-DD' for `date`, and a full RFC 3339\n\t\t * 'YYYY-MM-DDTHH:mm:ss±hh:mm' (local-offset, lossless) for `datetime`.\n\t\t *\n\t\t * The datetime form includes seconds and a timezone offset so it\n\t\t * satisfies the JSON-Schema `date-time` format (ajv-formats requires\n\t\t * an offset) — the backend would otherwise reject a bare\n\t\t * 'YYYY-MM-DDTHH:mm' value on save.\n\t\t *\n\t\t * @param {string} widget The field widget ('date' | 'datetime').\n\t\t * @param {Date} date A valid Date instance.\n\t\t * @return {string} The serialised value.\n\t\t */\n\t\tformatDateValue(widget, date) {\n\t\t\tconst yyyy = String(date.getFullYear()).padStart(4, '0')\n\t\t\tconst MM = String(date.getMonth() + 1).padStart(2, '0')\n\t\t\tconst dd = String(date.getDate()).padStart(2, '0')\n\t\t\tif (widget !== 'datetime') {\n\t\t\t\treturn `${yyyy}-${MM}-${dd}`\n\t\t\t}\n\t\t\tconst hh = String(date.getHours()).padStart(2, '0')\n\t\t\tconst mm = String(date.getMinutes()).padStart(2, '0')\n\t\t\tconst ss = String(date.getSeconds()).padStart(2, '0')\n\t\t\t// Local timezone offset as ±hh:mm (getTimezoneOffset is minutes\n\t\t\t// behind UTC, so the sign is inverted).\n\t\t\tconst offMin = -date.getTimezoneOffset()\n\t\t\tconst sign = offMin >= 0 ? '+' : '-'\n\t\t\tconst offAbs = Math.abs(offMin)\n\t\t\tconst offH = String(Math.floor(offAbs / 60)).padStart(2, '0')\n\t\t\tconst offM = String(offAbs % 60).padStart(2, '0')\n\t\t\treturn `${yyyy}-${MM}-${dd}T${hh}:${mm}:${ss}${sign}${offH}:${offM}`\n\t\t},\n\n\t\t/**\n\t\t * Convert the Date emitted by NcDateTimePickerNative back to the\n\t\t * stored string format (see `formatDateValue`). Stores null when cleared.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {Date|null} date The chosen date\n\t\t */\n\t\tonDateFieldInput(field, date) {\n\t\t\tif (!(date instanceof Date) || isNaN(date.getTime())) {\n\t\t\t\tthis.updateField(field.key, null)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.updateField(field.key, this.formatDateValue(field.widget, date))\n\t\t},\n\n\t\t/**\n\t\t * Rewrite any persisted date/datetime field value in formData into\n\t\t * the canonical, schema-valid string produced by `formatDateValue`.\n\t\t * Runs once on edit-open so a field the user never touches still\n\t\t * re-submits a value the schema's `date`/`date-time` format accepts.\n\t\t * Leaves empty/unparseable values untouched.\n\t\t */\n\t\tnormalizePersistedDates() {\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field.widget !== 'date' && field.widget !== 'datetime') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst raw = this.formData[field.key]\n\t\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// Leave a value that ALREADY satisfies the schema format alone.\n\t\t\t\t//\n\t\t\t\t// This pass exists for OpenRegister's persisted shape\n\t\t\t\t// ('2026-10-15 14:30:00' — space-separated, no zone), which fails\n\t\t\t\t// the `date-time` format on save. A value that arrives as valid\n\t\t\t\t// RFC 3339 does not need it, and rewriting one CORRUPTS it:\n\t\t\t\t// dateValueFor() reads the wall-clock digits and DISCARDS the\n\t\t\t\t// offset (right for the local-time picker), then formatDateValue()\n\t\t\t\t// stamps the LOCAL offset back on. In Europe/Amsterdam a stored\n\t\t\t\t// '2026-10-15T14:30:00Z' came back as '2026-10-15T14:30:00+02:00'\n\t\t\t\t// — the same digits, two hours earlier, silently, on an edit that\n\t\t\t\t// never touched the field. It is schema-valid either way, so\n\t\t\t\t// nothing downstream objects (nextcloud-vue#835).\n\t\t\t\tif (this.isCanonicalDateValue(field.widget, raw)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst date = this.dateValueFor(field)\n\t\t\t\tif (date instanceof Date && !isNaN(date.getTime())) {\n\t\t\t\t\tthis.formData[field.key] = this.formatDateValue(field.widget, date)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a stored value is already in the shape this field submits.\n\t\t *\n\t\t * `date` wants a bare `YYYY-MM-DD`; `datetime` wants a full RFC 3339\n\t\t * timestamp carrying a zone (`Z` or `±hh:mm`), which is what\n\t\t * `formatDateValue` emits and what ajv-formats requires. Anything else\n\t\t * — notably OpenRegister's space-separated persisted form — still gets\n\t\t * normalised.\n\t\t *\n\t\t * @param {string} widget The field widget ('date' | 'datetime').\n\t\t * @param {unknown} raw The stored value.\n\t\t * @return {boolean} True when the value needs no rewrite.\n\t\t */\n\t\tisCanonicalDateValue(widget, raw) {\n\t\t\tif (typeof raw !== 'string') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (widget === 'date') {\n\t\t\t\treturn /^\\d{4}-\\d{2}-\\d{2}$/.test(raw)\n\t\t\t}\n\t\t\treturn /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/.test(raw)\n\t\t},\n\n\t\t/**\n\t\t * Display label for one enum value. The stored value (`id`) always stays\n\t\t * the raw schema value — only what the user reads is translated, first\n\t\t * through the field's `enumLabels` map when it has an entry, then through\n\t\t * the host app's catalogue. A schema enum is a set of English source\n\t\t * codes, so a Dutch session otherwise reads `submitted` in a form whose\n\t\t * every other string is Dutch.\n\t\t *\n\t\t * @param {object} field The field descriptor.\n\t\t * @param {unknown} val The raw enum value.\n\t\t * @return {string} The label to render.\n\t\t */\n\t\tenumOptionLabel(field, val) {\n\t\t\tconst labels = field.enumLabels || {}\n\t\t\treturn this.cnTranslate(labels[val] || String(val))\n\t\t},\n\n\t\tgetEnumOptions(field) {\n\t\t\tif (!field.enum) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\t// Cache the built option list per field so the same option object\n\t\t\t// references are reused across renders. NcSelect/vue-select match\n\t\t\t// the selected model against options by identity — returning fresh\n\t\t\t// objects each call left the selection unrecognised (the headless\n\t\t\t// \"chip renders but value never commits\" defect).\n\t\t\tconst cached = this._enumOptionCache[field.key]\n\t\t\tif (cached && cached.enum === field.enum && cached.enumLabels === (field.enumLabels || null)) {\n\t\t\t\treturn cached.options\n\t\t\t}\n\t\t\tconst options = field.enum.map((val) => ({\n\t\t\t\tid: val,\n\t\t\t\tlabel: this.enumOptionLabel(field, val),\n\t\t\t}))\n\t\t\tthis._enumOptionCache[field.key] = { enum: field.enum, enumLabels: field.enumLabels || null, options }\n\t\t\treturn options\n\t\t},\n\n\t\tgetSelectedEnumOption(field) {\n\t\t\tconst val = this.formData[field.key]\n\t\t\tif (val === null || val === undefined) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Return the SAME option object reference from the field's option\n\t\t\t// list so NcSelect/vue-select recognises it as selected (it matches\n\t\t\t// the model against options by identity). Returning a fresh\n\t\t\t// `{ id, label }` each render left the model unrecognised, which —\n\t\t\t// under headless Chromium — could drop the committed value and keep\n\t\t\t// the submit button disabled. Fall back to a constructed option for\n\t\t\t// values not present in the list.\n\t\t\tconst options = this.getEnumOptions(field)\n\t\t\tconst match = options.find((o) => o.id === val)\n\t\t\tif (match) {\n\t\t\t\treturn match\n\t\t\t}\n\t\t\treturn { id: val, label: this.enumOptionLabel(field, val) }\n\t\t},\n\n\t\tonSelectChange(key, option) {\n\t\t\tthis.updateField(key, option ? option.id : null)\n\t\t},\n\n\t\tgetArrayEnumOptions(field) {\n\t\t\tif (!field.items || !field.items.enum) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn field.items.enum.map((val) => ({\n\t\t\t\tid: val,\n\t\t\t\tlabel: this.enumOptionLabel(field, val),\n\t\t\t}))\n\t\t},\n\n\t\tgetSelectedArrayOptions(field) {\n\t\t\tconst val = this.formData[field.key]\n\t\t\tif (!Array.isArray(val)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn val.map((v) => ({ id: v, label: this.enumOptionLabel(field, v) }))\n\t\t},\n\n\t\tonMultiSelectChange(key, options) {\n\t\t\tthis.updateField(key, (options || []).map((o) => o.id))\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a single-value OpenRegister object reference\n\t\t * (`$ref`) we can resolve — i.e. it carries a `reference` descriptor,\n\t\t * is not multi-value, and a `register` is available to fetch against.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisReferenceField(field) {\n\t\t\treturn !!(field && field.reference && !field.reference.multiple && this.referenceRegister(field))\n\t\t},\n\n\t\t/**\n\t\t * The register a reference field fetches against: the reference's own\n\t\t * `register` (cross-app semantic references, ADR-048) when present,\n\t\t * otherwise the form's `register` prop.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {string} The register slug, or '' when none is available.\n\t\t */\n\t\treferenceRegister(field) {\n\t\t\treturn (field && field.reference && field.reference.register) || this.register || ''\n\t\t},\n\n\t\t/**\n\t\t * Degrade a reference field whose reference can't be resolved (no\n\t\t * `register` available) to a plain text input so it remains editable\n\t\t * instead of rendering an empty, optionless dropdown. Non-reference\n\t\t * fields and resolvable references are returned unchanged.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} The (possibly downgraded) field.\n\t\t */\n\t\tdegradeUnresolvableReference(field) {\n\t\t\tif (field && field.reference && !this.referenceRegister(field)) {\n\t\t\t\treturn { ...field, widget: 'text', reference: null }\n\t\t\t}\n\t\t\treturn field\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a multi-value OpenRegister object reference\n\t\t * (`items.$ref`) we can resolve.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisReferenceArrayField(field) {\n\t\t\treturn !!(field && field.reference && field.reference.multiple && this.referenceRegister(field))\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a single-value Nextcloud user reference — a\n\t\t * property the schema marked with `referenceType: 'nextcloud-user'`\n\t\t * (or `format: 'user'`/`'username'`). Renders as a searchable dropdown\n\t\t * of real Nextcloud users (label = display name, value = UID). Needs no\n\t\t * `register` (users come from the core autocomplete OCS endpoint).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\t/**\n\t\t * The text a select option shows: its label, else a display name, a\n\t\t * name or the id. A plain string option (a tag) is its own text.\n\t\t *\n\t\t * @spec openspec/changes/audit-round-lib-fixes/specs/schema-utilities/spec.md\n\t\t * @param {object|string} option The option (slot props).\n\t\t * @return {string} The text.\n\t\t */\n\t\t/**\n\t\t * The mark a required field carries in the label: \" *\" in the Nextcloud\n\t\t * look, nothing in the board look (optional fields say \"(optional)\").\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The suffix.\n\t\t */\n\t\trequiredSuffix(field) {\n\t\t\treturn !this.isBoardLook && field.required ? ' *' : ''\n\t\t},\n\n\t\t/**\n\t\t * The label text handed to a control that draws or announces its own.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The label.\n\t\t */\n\t\tfieldLabelText(field) {\n\t\t\treturn field.label + this.requiredSuffix(field)\n\t\t},\n\n\t\t/**\n\t\t * Whether the board look draws this field's label above the control. The\n\t\t * switch and checkbox keep their inline label, and the widgets that draw\n\t\t * their own label inside the control (file, duration, child records, sub\n\t\t * objects, property source, a reference widget) keep it too.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {boolean} True when the label sits above the control.\n\t\t */\n\t\tshowsOuterLabel(field) {\n\t\t\tif (!this.isBoardLook || this.$slots['field-' + field.key]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (['switch', 'checkbox', 'file', 'duration', 'child-records', 'sub-objects', 'property-source'].includes(field.widget)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn !this.resolveReferenceWidget(field)\n\t\t},\n\n\t\t/**\n\t\t * The id of a text control the board label points at (`for`); unset in the\n\t\t * Nextcloud look so the control keeps its generated id.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string|undefined} The id.\n\t\t */\n\t\tfieldControlId(field) {\n\t\t\treturn this.showsOuterLabel(field) ? 'cn-form-' + field.key : undefined\n\t\t},\n\n\t\t/**\n\t\t * The id of the field's error element.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The id.\n\t\t */\n\t\terrorIdFor(field) {\n\t\t\treturn 'cn-form-' + field.key + '-error'\n\t\t},\n\n\t\t/**\n\t\t * The id of the field's hint (the helper line).\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The id.\n\t\t */\n\t\thelpId(field) {\n\t\t\treturn 'cn-form-' + field.key + '-help'\n\t\t},\n\n\t\t/**\n\t\t * The error the helper line shows: in the board look the error sits above\n\t\t * the control instead, so the helper keeps only the hint.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string|undefined} The error, or '' in the board look.\n\t\t */\n\t\thelperError(field) {\n\t\t\treturn this.showsOuterLabel(field) ? '' : this.errors[field.key]\n\t\t},\n\n\t\t/**\n\t\t * The accessibility attributes of a text control. In both looks an invalid\n\t\t * control is `aria-invalid` and `aria-describedby` lists the error before\n\t\t * the hint; `aria-required` is set in the board look, where the asterisk is\n\t\t * gone.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {object} The attributes to bind.\n\t\t */\n\t\tfieldAria(field) {\n\t\t\tconst error = this.errors[field.key]\n\t\t\tconst boardError = this.showsOuterLabel(field)\n\t\t\tconst hasHint = !!(field.description || field.descriptionLong || field.help)\n\t\t\tconst ids = []\n\t\t\tif (error) {\n\t\t\t\tids.push(boardError ? this.errorIdFor(field) : this.helpId(field))\n\t\t\t}\n\t\t\tif (hasHint && (boardError || !error)) {\n\t\t\t\tids.push(this.helpId(field))\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t'aria-invalid': error ? 'true' : undefined,\n\t\t\t\t'aria-describedby': ids.length > 0 ? ids.join(' ') : undefined,\n\t\t\t\t'aria-required': this.isBoardLook && field.required ? 'true' : undefined,\n\t\t\t}\n\t\t},\n\n\t\toptionText(option) {\n\t\t\tif (option === null || option === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof option !== 'object') {\n\t\t\t\treturn String(option)\n\t\t\t}\n\t\t\tconst text = [option.label, option.displayName, option.name, option.id]\n\t\t\t\t.find((v) => v !== undefined && v !== null && v !== '')\n\t\t\treturn text === undefined ? '' : String(text)\n\t\t},\n\n\t\tisUserField(field) {\n\t\t\treturn !!(field && field.userPicker && !field.userPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a multi-value Nextcloud user reference (an array\n\t\t * of users) → a searchable multi-select of real Nextcloud users.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisUserArrayField(field) {\n\t\t\treturn !!(field && field.userPicker && field.userPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a single Nextcloud group (`format: 'nc-group'` or\n\t\t * `referenceType: 'nextcloud-group'`). Stores the group id.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisGroupField(field) {\n\t\t\treturn !!(field && field.groupPicker && !field.groupPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is an array of Nextcloud groups.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisGroupArrayField(field) {\n\t\t\treturn !!(field && field.groupPicker && field.groupPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a single-value field stores an id that maps to a label\n\t\t * fetched elsewhere: an object reference, a user or a group.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisIdPickerField(field) {\n\t\t\treturn this.isReferenceField(field) || this.isUserField(field) || this.isGroupField(field) || this.isCodedField(field)\n\t\t},\n\n\t\t/**\n\t\t * A single choice bound to an OpenRegister concept scheme.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisCodedField(field) {\n\t\t\treturn !!(field.codeList && !field.codeList.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Several choices bound to an OpenRegister concept scheme.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisCodedArrayField(field) {\n\t\t\treturn !!(field.codeList && field.codeList.multiple)\n\t\t},\n\n\t\t/**\n\t\t * The multi-value sibling of `isIdPickerField`.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisIdPickerArrayField(field) {\n\t\t\treturn this.isReferenceArrayField(field) || this.isUserArrayField(field) || this.isGroupArrayField(field) || this.isCodedArrayField(field)\n\t\t},\n\n\t\t/**\n\t\t * Turn a language or time zone field into a searchable select over\n\t\t * a fixed code list: labels in the user's language, the code stored.\n\t\t * Other fields pass through unchanged.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} The field, as a `select` when it is a code picker.\n\t\t */\n\t\tapplyCodePicker(field) {\n\t\t\tif (!field || (field.widget !== 'language' && field.widget !== 'timezone')) {\n\t\t\t\treturn field\n\t\t\t}\n\t\t\tconst list = field.widget === 'language' ? languageOptions() : timezoneOptions()\n\t\t\treturn { ...field, widget: 'select', codePicker: field.widget, enum: list.codes, enumLabels: list.labels }\n\t\t},\n\n\t\t/**\n\t\t * Check if a field has an async enum (function instead of static array).\n\t\t * Object references (`$ref`) and Nextcloud user references are treated\n\t\t * as async so they reuse the async-select load/search/loading machinery.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisAsyncEnum(field) {\n\t\t\treturn typeof field.enum === 'function' || this.isIdPickerField(field)\n\t\t},\n\n\t\t/**\n\t\t * Check if an array field has an async items enum. Multi-value object\n\t\t * references (`items.$ref`) and multi-value user references are treated\n\t\t * as async multiselects.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisAsyncItemsEnum(field) {\n\t\t\treturn !!(field.items && typeof field.items.enum === 'function') || this.isIdPickerArrayField(field)\n\t\t},\n\n\t\t/**\n\t\t * Resolve a human-readable label for an OpenRegister object.\n\t\t *\n\t\t * Delegates to the shared `objectDisplayName`, which asks `@self.name`\n\t\t * FIRST — the display name OpenRegister derived from whichever property\n\t\t * the schema actually uses — and type-checks every candidate.\n\t\t *\n\t\t * This used to be its own chain, and it took the first TRUTHY key\n\t\t * starting at `obj.title`, `obj.name`. That is wrong for any schema\n\t\t * whose `name` is structured rather than a string: Haal Centraal naming\n\t\t * gives a person `name: { givenNames, namePrefix, surname }`, so the\n\t\t * label became that OBJECT and the picker rendered `[object Object]`\n\t\t * where the requester's name belonged. `@self.name` held \"Stephan\n\t\t * Janssen\" the whole time, four candidates further down a list that\n\t\t * never reached it because `obj.name` was truthy.\n\t\t *\n\t\t * @param {object} obj An OpenRegister object.\n\t\t * @return {string} The display label.\n\t\t */\n\t\tdisplayLabel(obj) {\n\t\t\tif (!obj || typeof obj !== 'object') {\n\t\t\t\treturn String(obj)\n\t\t\t}\n\t\t\treturn objectDisplayName(obj)\n\t\t},\n\n\t\t/**\n\t\t * The object field CnResourceSelect uses as the option label and writes\n\t\t * on inline create. Reads `field.reference.labelField` when the schema\n\t\t * declares one, else defaults to `name`.\n\t\t *\n\t\t * @param {object} field A resolved reference field descriptor.\n\t\t * @return {string} The label field name.\n\t\t */\n\t\treferenceLabelField(field) {\n\t\t\treturn (field && field.reference && field.reference.labelField) || 'name'\n\t\t},\n\n\t\t/**\n\t\t * Cache the label of an object just created inline via CnResourceSelect,\n\t\t * so any read-side that resolves the stored UUID shows its name. The\n\t\t * value itself is set by CnResourceSelect's `update:modelValue`.\n\t\t *\n\t\t * @param {object} field The reference field.\n\t\t * @param {object} obj The freshly-created object (carries id + label field).\n\t\t * @return {void}\n\t\t */\n\t\tonReferenceCreated(field, obj) {\n\t\t\tif (obj && obj.id) {\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [obj.id]: this.displayLabel(obj) }\n\t\t\t}\n\t\t\t// A freshly-created template carries its own fields — fill from it directly.\n\t\t\tthis.applyTemplateFill(field, obj)\n\t\t},\n\n\t\t/**\n\t\t * Handle selection of a create-capable (`CnResourceSelect`) reference:\n\t\t * store the chosen UUID, then run any `fillFrom` template copy.\n\t\t *\n\t\t * @param {object} field The reference field descriptor.\n\t\t * @param {string|null} value The chosen object's UUID (or null on clear).\n\t\t * @return {void}\n\t\t */\n\t\tonReferenceSelected(field, value) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tthis.updateField(field.key, value)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.updateField(field.key, value || null)\n\t\t\tif (value) {\n\t\t\t\tthis.applyTemplateFill(field, value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The value CnResourceSelect binds for a select-or-create reference:\n\t\t * an array of ids for a multi-value reference, else the id string.\n\t\t *\n\t\t * @param {object} field The reference field descriptor.\n\t\t * @return {string|string[]} The bound value.\n\t\t */\n\t\treferenceModelValue(field) {\n\t\t\tconst value = this.formData[field.key]\n\t\t\tif (field.reference && field.reference.multiple) {\n\t\t\t\treturn Array.isArray(value) ? value.map((v) => String(v)) : []\n\t\t\t}\n\t\t\treturn value !== null && value !== undefined ? String(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * \"Create\" on a select-or-create reference: open the referenced\n\t\t * schema's own form, prefilled with the typed term, on top of this one.\n\t\t *\n\t\t * Resolves with the created object once that form is saved, or with\n\t\t * null when it is closed without saving. CnResourceSelect then selects\n\t\t * the new object. When the referenced schema cannot be loaded, the\n\t\t * term alone is saved as before, so the field never dead-ends.\n\t\t *\n\t\t * The form is the one the schema's index page shows for its own Add\n\t\t * button: that page's `createOverride` saves it and its form settings\n\t\t * shape it, with the typed term in the name.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {object} field The reference field descriptor.\n\t\t * @param {string} term The typed search term.\n\t\t * @return {Promise<object|null>} The created object, or null.\n\t\t */\n\t\tasync openNestedCreate(field, term) {\n\t\t\tconst store = this.getObjectStore()\n\t\t\tconst register = this.referenceRegister(field)\n\t\t\tif (!store || !register || !field.reference) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst schemaRef = String(field.reference.schema)\n\t\t\tconst slug = store.createObjectTypeSlug(register, schemaRef)\n\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\tstore.registerObjectType(slug, schemaRef, register)\n\t\t\t}\n\t\t\tconst labelField = this.referenceLabelField(field)\n\t\t\tconst loadSchema = async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await store.fetchSchema(slug)\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The app may own how this schema is created (pipelinq's client and\n\t\t\t// contact need a contact-first endpoint; a plain save 400s). A\n\t\t\t// create override keeps this form and saves through the app's\n\t\t\t// handler; a create modal (only when no override exists) opens the\n\t\t\t// app's own dialog instead of the generic form.\n\t\t\t//\n\t\t\t// A stored schema usually names its reference by id (`$ref: 28`)\n\t\t\t// while the manifest page names it by slug (`schema: 'client'`), so\n\t\t\t// when the reference itself matches no page, the referenced schema\n\t\t\t// is loaded and its slug, id and uuid are tried too.\n\t\t\tlet schema\n\t\t\tlet appCreate = this.appCreateFor(register, [schemaRef])\n\t\t\tif (!appCreate.page) {\n\t\t\t\tschema = await loadSchema()\n\t\t\t\tif (schema) {\n\t\t\t\t\tappCreate = this.appCreateFor(register, [schemaRef, schema.slug, schema.id, schema.uuid])\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!appCreate.override && appCreate.modal) {\n\t\t\t\treturn this.openAppCreateModal(appCreate.modal, store, slug, labelField, term)\n\t\t\t}\n\t\t\tconst override = appCreate.override\n\t\t\tif (schema === undefined) {\n\t\t\t\tschema = await loadSchema()\n\t\t\t}\n\t\t\tif (!schema || !schema.properties) {\n\t\t\t\tconst payload = { [labelField]: term }\n\t\t\t\treturn override\n\t\t\t\t\t? override(payload, { register, schema: schemaRef, objectType: slug, effectiveSchema: null })\n\t\t\t\t\t: store.saveObject(slug, payload)\n\t\t\t}\n\t\t\tconst initialData = {}\n\t\t\tconst prefillKey = this.nestedPrefillKey(schema, labelField)\n\t\t\tif (term && prefillKey) {\n\t\t\t\tinitialData[prefillKey] = term\n\t\t\t}\n\t\t\tconst form = appCreate.form || this.nestedFormConfig(null)\n\t\t\treturn new Promise((resolve) => {\n\t\t\t\tthis.nestedCreate = { field, schema, register, slug, schemaRef, initialData, override, form, resolve }\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The property of the referenced schema the typed term belongs in: the\n\t\t * reference's label field when the schema has it, else its name, title\n\t\t * or label. Visibility is decided by the form (a page `fieldOverrides`\n\t\t * entry can make a read-only name editable), not here.\n\t\t *\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {object} schema The referenced schema.\n\t\t * @param {string} labelField The reference's label field.\n\t\t * @return {string|null} The property key, or null when none fits.\n\t\t */\n\t\tnestedPrefillKey(schema, labelField) {\n\t\t\tconst properties = (schema && schema.properties) || {}\n\t\t\treturn [labelField, 'name', 'title', 'label'].find((key) => properties[key]) || null\n\t\t},\n\n\t\t/**\n\t\t * The create-form settings of the index page that lists a schema, in\n\t\t * the shape the nested CnFormDialog binds: the same `excludeFields`,\n\t\t * `includeFields`, `fieldOverrides`, `formSize` and `formColumns`\n\t\t * CnIndexPage hands its own Add form, so creating from a picker shows\n\t\t * the form the page shows. Without a page, the form's defaults.\n\t\t *\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {object|null} config The page's `config`, or null.\n\t\t * @return {{excludeFields: string[], includeFields: (string[]|null), fieldOverrides: object, size: string, columns: number}} The form settings.\n\t\t */\n\t\tnestedFormConfig(config) {\n\t\t\tconst c = config || {}\n\t\t\tconst isMap = (v) => v !== null && typeof v === 'object' && !Array.isArray(v)\n\t\t\treturn {\n\t\t\t\texcludeFields: Array.isArray(c.excludeFields) ? c.excludeFields : [],\n\t\t\t\tincludeFields: Array.isArray(c.includeFields) ? c.includeFields : null,\n\t\t\t\tfieldOverrides: isMap(c.fieldOverrides) ? c.fieldOverrides : {},\n\t\t\t\tsize: typeof c.formSize === 'string' && c.formSize !== '' ? c.formSize : 'normal',\n\t\t\t\tcolumns: c.formColumns === 2 ? 2 : 1,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * How the app creates objects of a schema, read off the manifest page\n\t\t * that lists them: the index page whose `config.schema` (and, when it\n\t\t * names one, `config.register`) matches. Its `config.createOverride` is\n\t\t * resolved to the registered handler; its `config.createModal` is the\n\t\t * registry key of a `kind: 'modal'` dialog; its form settings\n\t\t * (`fieldOverrides` and friends) shape the nested form.\n\t\t *\n\t\t * The schema is matched on any of the given references, so a caller\n\t\t * holding the referenced schema can pass its slug, id and uuid: the\n\t\t * manifest names a schema by slug, a stored `$ref` often by id.\n\t\t *\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {string} register The reference's register.\n\t\t * @param {string|Array<string|number>} schemaRefs The schema's slug, id or uuid, one or several.\n\t\t * @return {{page: (object|null), override: (((formData: object, ctx: object) => Promise<object>)|null), modal: (string|null), form: (object|null)}} What the app declared.\n\t\t */\n\t\tappCreateFor(register, schemaRefs) {\n\t\t\tconst none = { page: null, override: null, modal: null, form: null }\n\t\t\tconst pages = this.cnManifest && Array.isArray(this.cnManifest.pages) ? this.cnManifest.pages : []\n\t\t\tconst wanted = (Array.isArray(schemaRefs) ? schemaRefs : [schemaRefs])\n\t\t\t\t.filter((ref) => ref !== undefined && ref !== null && ref !== '')\n\t\t\t\t.map((ref) => String(ref).toLowerCase())\n\t\t\tconst page = pages.find((p) => {\n\t\t\t\tconst config = p && p.config\n\t\t\t\tif (!config || config.schema === undefined || config.schema === null) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif (!wanted.includes(String(config.schema).toLowerCase())) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn !config.register || String(config.register) === String(register)\n\t\t\t})\n\t\t\tif (!page) {\n\t\t\t\treturn none\n\t\t\t}\n\t\t\tconst config = page.config\n\t\t\tconst override = typeof config.createOverride === 'function'\n\t\t\t\t? config.createOverride\n\t\t\t\t: resolveCreateOverrideHandler(config.createOverride, this.cnRegistry, this.cnCustomComponents)\n\t\t\tconst modalEntry = typeof config.createModal === 'string' && config.createModal !== ''\n\t\t\t\t? (this.cnRegistry || {})[config.createModal]\n\t\t\t\t: null\n\t\t\tconst modal = modalEntry && modalEntry.kind === 'modal' && typeof this.cnOpenModal === 'function'\n\t\t\t\t? config.createModal\n\t\t\t\t: null\n\t\t\treturn { page, override: override || null, modal, form: this.nestedFormConfig(config) }\n\t\t},\n\n\t\t/**\n\t\t * Open the app's own create dialog (a registry modal) for a picker and\n\t\t * wait for it. The dialog reports the new object (or its id) with a\n\t\t * `created` event; `close` without one resolves null so the field stays\n\t\t * as it was. A reported id is fetched so the picker can label it.\n\t\t *\n\t\t * @spec openspec/changes/review-round-two/specs/schema-utilities/spec.md\n\t\t * @param {string} key The registry key of the modal.\n\t\t * @param {object} store The object store.\n\t\t * @param {string} slug The object type slug.\n\t\t * @param {string} labelField The property the typed term belongs in.\n\t\t * @param {string} term The typed search term.\n\t\t * @return {Promise<object|null>} The created object, or null.\n\t\t */\n\t\topenAppCreateModal(key, store, slug, labelField, term) {\n\t\t\treturn new Promise((resolve) => {\n\t\t\t\tlet settled = false\n\t\t\t\tlet reported = false\n\t\t\t\tconst settle = (value) => {\n\t\t\t\t\tif (!settled) {\n\t\t\t\t\t\tsettled = true\n\t\t\t\t\t\tresolve(value)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.cnOpenModal(key, {\n\t\t\t\t\tinitialData: term ? { [labelField]: term } : {},\n\t\t\t\t\tonCreated: async (created) => {\n\t\t\t\t\t\tif (created && typeof created === 'object') {\n\t\t\t\t\t\t\tsettle(created)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (typeof created !== 'string' && typeof created !== 'number') {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\treported = true\n\t\t\t\t\t\tlet obj\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tobj = await store.fetchObject(slug, String(created))\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tobj = null\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsettle(obj || { id: String(created), [labelField]: term })\n\t\t\t\t\t},\n\t\t\t\t\tonClose: () => {\n\t\t\t\t\t\t// A dialog often emits `close` right after `created`,\n\t\t\t\t\t\t// while the id is still being fetched: that fetch settles.\n\t\t\t\t\t\tif (!reported) {\n\t\t\t\t\t\t\tsettle(null)\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The nested create form was confirmed: save the object, hand it to\n\t\t * the waiting picker and close the nested form. A refused save keeps\n\t\t * the nested form open with the server's message.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} formData The nested form's values.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onNestedCreateConfirm(formData) {\n\t\t\tconst pending = this.nestedCreate\n\t\t\tif (!pending) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tlet created = null\n\t\t\tlet thrown = null\n\t\t\tif (pending.override) {\n\t\t\t\t// The app's create override owns persistence (same contract as\n\t\t\t\t// CnIndexPage's `createOverride`).\n\t\t\t\ttry {\n\t\t\t\t\tcreated = await pending.override(formData, {\n\t\t\t\t\t\tregister: pending.register,\n\t\t\t\t\t\tschema: pending.schemaRef,\n\t\t\t\t\t\tobjectType: pending.slug,\n\t\t\t\t\t\teffectiveSchema: pending.schema,\n\t\t\t\t\t})\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthrown = (err && err.response && err.response.data && err.response.data.error) || (err && err.message) || null\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcreated = store ? await store.saveObject(pending.slug, formData) : null\n\t\t\t}\n\t\t\tif (!created) {\n\t\t\t\tconst error = store && store.errors ? store.errors[pending.slug] : null\n\t\t\t\tconst message = thrown\n\t\t\t\t\t|| (!pending.override && error && error.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The item could not be created.')\n\t\t\t\tconst dialog = this.$refs.nestedCreateDialog\n\t\t\t\tif (dialog && typeof dialog.setValidationErrors === 'function') {\n\t\t\t\t\tdialog.setValidationErrors({}, message)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.nestedCreate = null\n\t\t\tpending.resolve(created)\n\t\t},\n\n\t\t/**\n\t\t * The nested create form was closed without saving: leave the field\n\t\t * as it was.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonNestedCreateClose() {\n\t\t\tconst pending = this.nestedCreate\n\t\t\tthis.nestedCreate = null\n\t\t\tif (pending) {\n\t\t\t\tpending.resolve(null)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a value at a plain or dotted key; a dotted key reaches a field\n\t\t * of an object property.\n\t\t *\n\t\t * @param {string} key Field key, or `parent.child`.\n\t\t * @param {*} value The value to write.\n\t\t */\n\t\twriteDotted(key, value) {\n\t\t\tif (Object.hasOwn(this.formData, key) || !key.includes('.')) {\n\t\t\t\tthis.updateField(key, value)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst [head, ...rest] = key.split('.')\n\t\t\tconst unsafe = ['__proto__', 'constructor', 'prototype']\n\t\t\tif (unsafe.includes(head) || rest.some((k) => unsafe.includes(k))) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst clone = JSON.parse(JSON.stringify(this.formData[head] ?? {}))\n\t\t\tlet cur = clone\n\t\t\trest.slice(0, -1).forEach((k) => {\n\t\t\t\tif (cur[k] === null || typeof cur[k] !== 'object') {\n\t\t\t\t\tcur[k] = {}\n\t\t\t\t}\n\t\t\t\tcur = cur[k]\n\t\t\t})\n\t\t\tcur[rest[rest.length - 1]] = value\n\t\t\tthis.updateField(head, clone)\n\t\t},\n\n\t\t/**\n\t\t * After a registry pick resolved, fill empty sibling fields from the\n\t\t * declaration's `config.fill` map and ask once before replacing typed\n\t\t * values. Mode `live` fills nothing.\n\t\t *\n\t\t * @param {object} field The property-source field.\n\t\t * @param {{value: *}} payload The resolved answer.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPropertySourceResolved(field, payload) {\n\t\t\tconst source = field.propertySource\n\t\t\tif (!source || source.mode !== 'default' || !source.config || !source.config.fill) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst known = new Set(this.resolvedFields.map((f) => f.key))\n\t\t\tconst { apply, conflicts } = planPropertySourceFill(\n\t\t\t\tsource.config.fill,\n\t\t\t\tpayload.value,\n\t\t\t\tthis.formData,\n\t\t\t\t(key) => known.has(key) || known.has(String(key).split('.')[0]),\n\t\t\t)\n\t\t\tapply.forEach(({ key, value }) => this.writeDotted(key, value))\n\t\t\tif (conflicts.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst labelOf = (key) => {\n\t\t\t\tconst f = this.resolvedFields.find((x) => x.key === key)\n\t\t\t\treturn f ? f.label : key\n\t\t\t}\n\t\t\tconst replace = await new Promise((resolve) => {\n\t\t\t\tthis.pendingReplace = {\n\t\t\t\t\tchanges: conflicts.map((c) => ({ ...c, label: labelOf(c.key), oldValue: getDotted(this.formData, c.key) })),\n\t\t\t\t\tresolve,\n\t\t\t\t}\n\t\t\t})\n\t\t\tif (replace) {\n\t\t\t\tconflicts.forEach(({ key, newValue }) => this.writeDotted(key, newValue))\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Answer the pending replace question.\n\t\t *\n\t\t * @param {boolean} replace True to overwrite the typed values.\n\t\t */\n\t\tresolveReplace(replace) {\n\t\t\tconst pending = this.pendingReplace\n\t\t\tthis.pendingReplace = null\n\t\t\tif (pending) {\n\t\t\t\tpending.resolve(replace)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Copy template values off a selected/created reference object into the\n\t\t * form, per the field's `fillFrom` map (`{ formKey: sourceKey }`). When\n\t\t * `source` is an id, the object is fetched first. Overwrites existing\n\t\t * values so re-selecting a template refreshes them; no-op without a map.\n\t\t *\n\t\t * @param {object} field The reference field carrying `fillFrom`.\n\t\t * @param {object|string} source The selected object, or its UUID.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync applyTemplateFill(field, source) {\n\t\t\tif (!field || !field.fillFrom || !source) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet obj = (typeof source === 'object') ? source : null\n\t\t\tif (!obj) {\n\t\t\t\tconst register = this.referenceRegister(field)\n\t\t\t\tconst store = this.getObjectStore()\n\t\t\t\tif (!register || !store || !field.reference) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst slug = store.createObjectTypeSlug(register, field.reference.schema)\n\t\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\t\tstore.registerObjectType(slug, field.reference.schema, register)\n\t\t\t\t\t}\n\t\t\t\t\tobj = await store.fetchObject(slug, String(source))\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error(`CnFormDialog: template fill fetch failed for \"${field.key}\":`, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!obj) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor (const formKey of Object.keys(field.fillFrom)) {\n\t\t\t\tconst sourceKey = field.fillFrom[formKey]\n\t\t\t\tconst value = obj[sourceKey]\n\t\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\t\tthis.updateField(formKey, value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an enum `widget:\"switch\"` field is currently \"on\" — i.e. its\n\t\t * value equals the last enum value (off → first enum value).\n\t\t *\n\t\t * @param {object} field The switch field descriptor (carries `enum`).\n\t\t * @return {boolean}\n\t\t */\n\t\tisSwitchOn(field) {\n\t\t\tconst values = Array.isArray(field.enum) ? field.enum : []\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn !!this.formData[field.key]\n\t\t\t}\n\t\t\treturn this.formData[field.key] === values[values.length - 1]\n\t\t},\n\n\t\t/**\n\t\t * Map a switch toggle to the field's enum value: on → last enum value,\n\t\t * off → first enum value.\n\t\t *\n\t\t * @param {object} field The switch field descriptor (carries `enum`).\n\t\t * @param {boolean} on The new switch state.\n\t\t * @return {unknown} The enum value to store.\n\t\t */\n\t\tswitchValueFor(field, on) {\n\t\t\tconst values = Array.isArray(field.enum) ? field.enum : []\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn on\n\t\t\t}\n\t\t\treturn on ? values[values.length - 1] : values[0]\n\t\t},\n\n\t\t/**\n\t\t * Fetch the options for a `$ref` reference field: the objects of the\n\t\t * referenced schema in the form's register, mapped to\n\t\t * `{ id: <uuid>, label: <human name> }`. Server-filters by the search\n\t\t * term when present. Resolved labels are cached in `referenceLabels`\n\t\t * so the select can display the current selection's name. Fails soft\n\t\t * (returns `[]`) when no register is set or the fetch errors.\n\t\t *\n\t\t * @param {object} field The field definition (must carry `field.reference`).\n\t\t * @param {string} query The NcSelect search term.\n\t\t * @return {Promise<Array<{id: string, label: string}>>}\n\t\t */\n\t\t/**\n\t\t * Lazily resolve the generic OpenRegister object store. Acquired on\n\t\t * demand (not in `setup()`) so mounting CnFormDialog without an active\n\t\t * Pinia — e.g. forms with no reference fields, or unit tests — never\n\t\t * fails. Returns null when no Pinia is available.\n\t\t *\n\t\t * @return {object|null} The object store, or null.\n\t\t */\n\t\tgetObjectStore() {\n\t\t\tif (this._objectStore) {\n\t\t\t\treturn this._objectStore\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tthis._objectStore = useObjectStore()\n\t\t\t\treturn this._objectStore\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The comparable form of a driving property's value.\n\t\t *\n\t\t * A `$ref` picker may hold either the bare UUID or the whole option\n\t\t * object, depending on whether the user has just chosen it or it came\n\t\t * back from the server. Both must produce the same signature, or\n\t\t * merely opening an existing record would look like a change and\n\t\t * refetch its definitions.\n\t\t *\n\t\t * @param {unknown} value The raw form value.\n\t\t * @return {string} The value's identity, '' when unset.\n\t\t */\n\t\tdynamicDriverValue(value) {\n\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof value === 'object') {\n\t\t\t\treturn String(value.id || value.uuid || '')\n\t\t\t}\n\t\t\treturn String(value)\n\t\t},\n\n\t\t/**\n\t\t * Fetch and install the extra fields the current driving values ask\n\t\t * for. A driver that is unset clears them; a fetch that fails leaves\n\t\t * them cleared rather than half-populated, because a form missing a\n\t\t * required question it never showed is worse than one that shows none.\n\t\t *\n\t\t * @param {string} token The driver signature this load is for.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadDynamicProperties(token) {\n\t\t\tthis.dynamicToken = token\n\t\t\tconst active = this.extendsDeclarations\n\t\t\t\t.filter(({ key }) => this.dynamicDriverValue(this.formData[key]) !== '')\n\t\t\tif (active.length === 0) {\n\t\t\t\tthis.dynamicProperties = {}\n\t\t\t\tthis.dynamicRequired = []\n\t\t\t\tthis.dynamicOwners = {}\n\t\t\t\tthis.dynamicLoading = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.dynamicLoading = true\n\t\t\tconst properties = {}\n\t\t\tconst required = []\n\t\t\tconst owners = {}\n\t\t\tconst definitions = []\n\t\t\ttry {\n\t\t\t\tfor (const { key, config } of active) {\n\t\t\t\t\tconst schemaSlug = config.definitions && config.definitions.schema\n\t\t\t\t\t// A definitions schema normally lives beside the object's own,\n\t\t\t\t\t// so the form's register is the default; `register` names\n\t\t\t\t\t// another app's when it does not (ADR-066).\n\t\t\t\t\tconst register = (config.definitions && config.definitions.register) || this.register\n\t\t\t\t\tif (!schemaSlug || !register) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst slug = store.createObjectTypeSlug(register, schemaSlug)\n\t\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\t\tstore.registerObjectType(slug, schemaSlug, register)\n\t\t\t\t\t}\n\t\t\t\t\tconst params = definitionQueryParams(\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tthis.dynamicDriverValue(this.formData[key]),\n\t\t\t\t\t\tthis.formData,\n\t\t\t\t\t)\n\t\t\t\t\tconst records = await store.fetchCollectionForOptions(slug, params)\n\t\t\t\t\t// Kept for the confirm payload. Array-mode declarations\n\t\t\t\t\t// store the definition's NAME beside its value on the\n\t\t\t\t\t// parent, and the answers alone carry only ids.\n\t\t\t\t\tif (Array.isArray(records)) {\n\t\t\t\t\t\tdefinitions.push(...records)\n\t\t\t\t\t}\n\t\t\t\t\t// `orderFrom` keeps each declaration's block together and\n\t\t\t\t\t// after the schema's own fields, whose `order` values are\n\t\t\t\t\t// authored well below this.\n\t\t\t\t\tconst mappedProps = propertiesFromDefinitions(\n\t\t\t\t\t\tArray.isArray(records) ? records : [],\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t{ orderFrom: 1000 + Object.keys(properties).length },\n\t\t\t\t\t)\n\t\t\t\t\tObject.assign(properties, mappedProps.properties)\n\t\t\t\t\trequired.push(...mappedProps.required)\n\t\t\t\t\tfor (const propKey of Object.keys(mappedProps.properties)) {\n\t\t\t\t\t\towners[propKey] = key\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnFormDialog: could not load the fields for this selection:', err)\n\t\t\t}\n\t\t\t// A slower earlier fetch must not overwrite a later selection's\n\t\t\t// fields; the signature it started under is the only way to tell.\n\t\t\tif (this.dynamicToken !== token) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dynamicDefinitions = definitions\n\t\t\tthis.dynamicProperties = properties\n\t\t\tthis.dynamicRequired = required\n\t\t\tthis.dynamicOwners = owners\n\t\t\tthis.dynamicLoading = false\n\t\t\tthis.seedDynamicDefaults(properties)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field takes the full width of a two-column form.\n\t\t *\n\t\t * A textarea or a JSON editor in a half-width column is worse than no\n\t\t * columns at all, and a checkbox pair reads as one control rather than\n\t\t * two. The list is deliberately about the WIDGET, not the field name,\n\t\t * so it needs no per-app configuration to behave.\n\t\t *\n\t\t * @param {object} field The resolved field.\n\t\t * @return {boolean} True when the field spans both columns.\n\t\t */\n\t\tfieldSpansBothColumns(field) {\n\t\t\tif (this.columns !== 2 || !field) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn WIDE_WIDGETS.includes(field.widget)\n\t\t},\n\n\t\t/**\n\t\t * Copy what the chosen record already answers into the empty fields\n\t\t * that ask the same question.\n\t\t *\n\t\t * Only empty targets are written. Someone who has typed a title before\n\t\t * picking a case type keeps their title, and switching case type after\n\t\t * that keeps it too: the field is no longer empty, so it is no longer\n\t\t * a candidate. That is the whole rule, and it is what makes the\n\t\t * feature safe to run on every change rather than only the first.\n\t\t *\n\t\t * A field the person deliberately cleared reads as empty and will be\n\t\t * filled again by the next change of case type. That is the accepted\n\t\t * cost of not tracking per-field provenance; the alternative is a\n\t\t * dirty-flag per field, which is a great deal of state for a case that\n\t\t * ends with the person clearing the field again.\n\t\t *\n\t\t * @param {string} token The prefill signature this run is for.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync applyPrefill(token) {\n\t\t\tthis.prefillToken = token\n\t\t\tconst active = this.prefillDecls\n\t\t\t\t.filter(({ key }) => this.dynamicDriverValue(this.formData[key]) !== '')\n\t\t\tif (active.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst resolved = {}\n\t\t\ttry {\n\t\t\t\tfor (const { key, config } of active) {\n\t\t\t\t\t// The declaration may name its own schema; otherwise the\n\t\t\t\t\t// picker's own `$ref` target is the record being chosen.\n\t\t\t\t\tconst field = this.resolvedFields.find((f) => f.key === key)\n\t\t\t\t\tconst schemaSlug = config.schema\n\t\t\t\t\t\t|| (field && field.reference && field.reference.schema)\n\t\t\t\t\tconst register = config.register\n\t\t\t\t\t\t|| (field ? this.referenceRegister(field) : this.register)\n\t\t\t\t\tif (!schemaSlug || !register) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tconst slug = store.createObjectTypeSlug(register, schemaSlug)\n\t\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\t\tstore.registerObjectType(slug, schemaSlug, register)\n\t\t\t\t\t}\n\t\t\t\t\tconst record = await store.fetchObject(\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\tthis.dynamicDriverValue(this.formData[key]),\n\t\t\t\t\t)\n\t\t\t\t\tObject.assign(resolved, prefillValues(record, config))\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// A prefill that fails leaves the person typing the values\n\t\t\t\t// themselves, which is exactly where they were before.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnFormDialog: could not prefill from this selection:', err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (this.prefillToken !== token) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor (const [target, value] of Object.entries(resolved)) {\n\t\t\t\tconst current = this.formData[target]\n\t\t\t\tconst empty = current === null || current === undefined || current === ''\n\t\t\t\t\t|| (Array.isArray(current) && current.length === 0)\n\t\t\t\tif (empty) {\n\t\t\t\t\tthis.formData[target] = value\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Seed a newly-arrived dynamic field with its default, without\n\t\t * touching one the user (or the record) already answered.\n\t\t *\n\t\t * @param {object} properties The installed dynamic properties.\n\t\t */\n\t\tseedDynamicDefaults(properties) {\n\t\t\tfor (const [key, prop] of Object.entries(properties)) {\n\t\t\t\tif (prop.default === undefined) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (this.formData[key] !== undefined && this.formData[key] !== null && this.formData[key] !== '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.formData[key] = prop.default\n\t\t\t}\n\t\t},\n\n\t\tasync fetchReferenceOptions(field, query) {\n\t\t\tconst register = this.referenceRegister(field)\n\t\t\tif (!register || !field.reference || !field.reference.schema) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst params = { _limit: 100 }\n\t\t\t\tif (query) {\n\t\t\t\t\tparams._search = query\n\t\t\t\t}\n\t\t\t\t// Declarative option scoping (`x-relation-filter`): narrow the\n\t\t\t\t// picker to objects that fit the form's CURRENT values — e.g. a\n\t\t\t\t// line item's `product` scoped to the chosen leadProduct. Mirrors\n\t\t\t\t// CnObjectDataWidget: values are token-resolved (@object.<field> /\n\t\t\t\t// @objectId) against the live form data; an entry whose token stays\n\t\t\t\t// unresolved is dropped (unfiltered beats an empty picker).\n\t\t\t\tconst prop = (this.schema && this.schema.properties && this.schema.properties[field.key]) || null\n\t\t\t\tconst rawFilter = prop && prop['x-relation-filter']\n\t\t\t\tif (rawFilter && typeof rawFilter === 'object') {\n\t\t\t\t\tconst ctx = { objectId: this.formData.id, object: { ...this.formData } }\n\t\t\t\t\tconst filter = resolveFilterTokens(rawFilter, ctx)\n\t\t\t\t\tfor (const [fk, fv] of Object.entries(filter)) {\n\t\t\t\t\t\tif (typeof fv === 'string' && fv.charAt(0) === '@') {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (fv && typeof fv === 'object') {\n\t\t\t\t\t\t\tfor (const [op, ov] of Object.entries(fv)) {\n\t\t\t\t\t\t\t\tparams[`${fk}[${op}]`] = ov\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (fv !== '' && fv !== null && fv !== undefined) {\n\t\t\t\t\t\t\tparams[fk] = fv\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst slug = store.createObjectTypeSlug(register, field.reference.schema)\n\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\tstore.registerObjectType(slug, field.reference.schema, register)\n\t\t\t\t}\n\t\t\t\tconst results = await store.fetchCollectionForOptions(slug, params)\n\t\t\t\tconst list = Array.isArray(results) ? results : []\n\t\t\t\tconst labels = {}\n\t\t\t\tconst options = list\n\t\t\t\t\t.filter((obj) => obj && obj.id)\n\t\t\t\t\t.map((obj) => {\n\t\t\t\t\t\tconst label = this.displayLabel(obj)\n\t\t\t\t\t\tlabels[obj.id] = label\n\t\t\t\t\t\treturn { id: obj.id, label }\n\t\t\t\t\t})\n\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t}\n\t\t\t\treturn options\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: reference fetch failed for field \"${field.key}\":`, err)\n\t\t\t\treturn []\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve a single reference UUID to its `{ id, label }` option,\n\t\t * fetching the object by id when its label isn't cached yet (edit mode).\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @param {string} uuid The stored UUID.\n\t\t */\n\t\tasync resolveReferenceLabel(field, uuid) {\n\t\t\tconst register = this.referenceRegister(field)\n\t\t\tif (!uuid || this.referenceLabels[uuid] || !register || !field.reference) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst slug = store.createObjectTypeSlug(register, field.reference.schema)\n\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\tstore.registerObjectType(slug, field.reference.schema, register)\n\t\t\t\t}\n\t\t\t\tconst obj = await store.fetchObject(slug, uuid)\n\t\t\t\tif (obj && obj.id) {\n\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [obj.id]: this.displayLabel(obj) }\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: reference label resolve failed for \"${uuid}\":`, err)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Initialize async state for all async fields and trigger initial load.\n\t\t */\n\t\tinitAsyncFields() {\n\t\t\t// Clean up existing timeouts\n\t\t\tfor (const state of Object.values(this.asyncState)) {\n\t\t\t\tif (state.searchTimeout) {\n\t\t\t\t\tclearTimeout(state.searchTimeout)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Iterate the *transformed* fields so a resolved cross-app\n\t\t\t// semantic reference (now a `$ref` picker) gets async state too.\n\t\t\tconst fields = this.resolvedFields.map((field) => this.applySemanticResolution(field))\n\t\t\tconst newState = {}\n\t\t\tfor (const field of fields) {\n\t\t\t\tif (this.isAsyncEnum(field) || this.isAsyncItemsEnum(field)) {\n\t\t\t\t\tnewState[field.key] = { options: [], loading: false, searchTimeout: null }\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.asyncState = newState\n\n\t\t\t// Trigger initial load for each async field. A select-or-create\n\t\t\t// reference loads its own options (CnResourceSelect), so it is\n\t\t\t// skipped here rather than fetched twice.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tfor (const field of fields) {\n\t\t\t\t\tif (field.allowCreate && field.reference) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (this.isAsyncEnum(field) || this.isAsyncItemsEnum(field)) {\n\t\t\t\t\t\tthis.loadAsyncOptions(field, '')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Load async options for a field by calling its enum function.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {string} query Search query\n\t\t */\n\t\tasync loadAsyncOptions(field, query) {\n\t\t\tconst state = this.asyncState[field.key]\n\t\t\tif (!state) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tstate.loading = true\n\n\t\t\ttry {\n\t\t\t\tlet results\n\t\t\t\tif (this.isCodedField(field) || this.isCodedArrayField(field)) {\n\t\t\t\t\t// Options OpenRegister serves for the bound scheme: its order,\n\t\t\t\t\t// its validity window, its labels in the user's language.\n\t\t\t\t\tresults = await this.fetchCodedOptions(field)\n\t\t\t\t} else if (this.isReferenceField(field) || this.isReferenceArrayField(field)) {\n\t\t\t\t\t// OpenRegister object reference — fetch the referenced objects.\n\t\t\t\t\tresults = await this.fetchReferenceOptions(field, query)\n\t\t\t\t} else if (this.isUserField(field) || this.isUserArrayField(field)) {\n\t\t\t\t\t// Nextcloud user reference — search real users via the core\n\t\t\t\t\t// autocomplete OCS endpoint. Cache labels so the current\n\t\t\t\t\t// selection still displays its display name.\n\t\t\t\t\tresults = await searchNextcloudUsers(query)\n\t\t\t\t\tconst labels = {}\n\t\t\t\t\tfor (const opt of results) {\n\t\t\t\t\t\tif (opt && opt.id) {\n\t\t\t\t\t\t\tlabels[opt.id] = opt.label || String(opt.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isGroupField(field) || this.isGroupArrayField(field)) {\n\t\t\t\t\t// Nextcloud group: search groups, store the gid, show the name.\n\t\t\t\t\tresults = await searchNextcloudGroups(query)\n\t\t\t\t\tconst labels = {}\n\t\t\t\t\tfor (const opt of results) {\n\t\t\t\t\t\tif (opt && opt.id) {\n\t\t\t\t\t\t\tlabels[opt.id] = opt.label || String(opt.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst enumFn = typeof field.enum === 'function' ? field.enum : field.items.enum\n\t\t\t\t\tresults = await enumFn(query)\n\t\t\t\t}\n\t\t\t\tstate.options = Array.isArray(results) ? results : []\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: async enum error for field \"${field.key}\":`, err)\n\t\t\t\tstate.options = []\n\t\t\t} finally {\n\t\t\t\tstate.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle search input on an async select with debounce.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {string} query Search query\n\t\t */\n\t\tonAsyncSearch(field, query) {\n\t\t\tconst state = this.asyncState[field.key]\n\t\t\tif (!state) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (state.searchTimeout) {\n\t\t\t\tclearTimeout(state.searchTimeout)\n\t\t\t}\n\n\t\t\tconst debounceMs = field.debounce || 300\n\n\t\t\tstate.searchTimeout = setTimeout(() => {\n\t\t\t\tthis.loadAsyncOptions(field, query || '')\n\t\t\t}, debounceMs)\n\t\t},\n\n\t\t/**\n\t\t * Get the effective options for a select field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Array}\n\t\t */\n\t\tgetEffectiveOptions(field) {\n\t\t\tif (this.isAsyncEnum(field)) {\n\t\t\t\t// TODO: restore to `this.asyncState[field.key]?.options || []` once on Vue 3 (buble doesn't support optional chaining)\n\t\t\t\treturn (this.asyncState[field.key] && this.asyncState[field.key].options) || []\n\t\t\t}\n\t\t\treturn this.getEnumOptions(field)\n\t\t},\n\n\t\t/**\n\t\t * Get the effective selected value for a select field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {object|null}\n\t\t */\n\t\tgetEffectiveSelectedOption(field) {\n\t\t\tif (this.isIdPickerField(field)) {\n\t\t\t\t// Reference / user fields store the UUID/UID — resolve it to a\n\t\t\t\t// display option `{ id, label }` (label from the resolved-labels\n\t\t\t\t// cache, falling back to the id until it loads).\n\t\t\t\tconst uuid = this.formData[field.key]\n\t\t\t\tif (uuid === null || uuid === undefined || uuid === '') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst label = this.referenceLabels[uuid] || String(uuid)\n\t\t\t\t// NcSelectUsers reads `displayName`, not `label` — a user field's\n\t\t\t\t// selected option needs both.\n\t\t\t\treturn this.isUserField(field) ? { id: uuid, label, displayName: label } : { id: uuid, label }\n\t\t\t}\n\t\t\tif (this.isAsyncEnum(field)) {\n\t\t\t\t// For async fields, formData stores the full option object\n\t\t\t\treturn this.formData[field.key] || null\n\t\t\t}\n\t\t\treturn this.getSelectedEnumOption(field)\n\t\t},\n\n\t\t/**\n\t\t * Handle select change for both async and static fields.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {object|null} option The selected option\n\t\t */\n\t\tonEffectiveSelectChange(field, option) {\n\t\t\tif (this.isIdPickerField(field)) {\n\t\t\t\t// Reference / user fields store the chosen id (UUID / UID),\n\t\t\t\t// not the full option. Cache its label so the selection displays.\n\t\t\t\t// A user option can carry only `displayName` (NcSelectUsers' own\n\t\t\t\t// shape), and without that fallback the chip showed the uid.\n\t\t\t\tif (option && option.id) {\n\t\t\t\t\tconst name = option.label || option.displayName || this.referenceLabels[option.id]\n\t\t\t\t\tif (name) {\n\t\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [option.id]: name }\n\t\t\t\t\t} else if (this.isUserField(field)) {\n\t\t\t\t\t\tthis.resolveUserLabel(option.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.updateField(field.key, option ? option.id : null)\n\t\t\t\t// Reference options are label-only ({id,label}) — pass the id so\n\t\t\t\t// template pre-fill fetches the full object.\n\t\t\t\tif (option && option.id && this.isReferenceField(field)) {\n\t\t\t\t\tthis.applyTemplateFill(field, String(option.id))\n\t\t\t\t}\n\t\t\t} else if (this.isAsyncEnum(field)) {\n\t\t\t\t// Store full option object for async selects\n\t\t\t\tthis.updateField(field.key, option || null)\n\t\t\t} else {\n\t\t\t\tthis.onSelectChange(field.key, option)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Get effective options for a multiselect field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Array}\n\t\t */\n\t\tgetEffectiveArrayOptions(field) {\n\t\t\tif (this.isAsyncItemsEnum(field)) {\n\t\t\t\t// TODO: restore to `this.asyncState[field.key]?.options || []` once on Vue 3 (buble doesn't support optional chaining)\n\t\t\t\treturn (this.asyncState[field.key] && this.asyncState[field.key].options) || []\n\t\t\t}\n\t\t\treturn this.getArrayEnumOptions(field)\n\t\t},\n\n\t\t/**\n\t\t * Get effective selected values for a multiselect field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Array}\n\t\t */\n\t\tgetEffectiveSelectedArrayOptions(field) {\n\t\t\tif (this.isIdPickerArrayField(field)) {\n\t\t\t\t// Reference / user arrays store an array of ids (UUIDs / UIDs) —\n\t\t\t\t// resolve each to a display option `{ id, label }`.\n\t\t\t\tconst uuids = this.formData[field.key]\n\t\t\t\tif (!Array.isArray(uuids)) {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\t\t\t\treturn uuids.map((uuid) => ({ id: uuid, label: this.referenceLabels[uuid] || String(uuid) }))\n\t\t\t}\n\t\t\tif (this.isAsyncItemsEnum(field)) {\n\t\t\t\t// For async fields, formData stores array of full option objects\n\t\t\t\treturn this.formData[field.key] || []\n\t\t\t}\n\t\t\treturn this.getSelectedArrayOptions(field)\n\t\t},\n\n\t\t/**\n\t\t * Handle multiselect change for both async and static fields.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {Array} options The selected options\n\t\t */\n\t\tonEffectiveMultiSelectChange(field, options) {\n\t\t\tif (this.isIdPickerArrayField(field)) {\n\t\t\t\t// Reference / user arrays store an array of ids (UUIDs / UIDs).\n\t\t\t\t// Cache labels so the chips still display the human names.\n\t\t\t\tconst list = options || []\n\t\t\t\tconst labels = {}\n\t\t\t\tfor (const o of list) {\n\t\t\t\t\tif (o && o.id) {\n\t\t\t\t\t\tlabels[o.id] = o.label || o.displayName || this.referenceLabels[o.id] || String(o.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t}\n\t\t\t\tthis.updateField(field.key, list.map((o) => o.id))\n\t\t\t} else if (this.isAsyncItemsEnum(field)) {\n\t\t\t\t// Store full option objects for async multiselect\n\t\t\t\tthis.updateField(field.key, options || [])\n\t\t\t} else {\n\t\t\t\tthis.onMultiSelectChange(field.key, options)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a field's async options are currently loading.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisFieldLoading(field) {\n\t\t\t// TODO: restore to `this.asyncState[field.key]?.loading || false` once on Vue 3 (buble doesn't support optional chaining)\n\t\t\treturn (this.asyncState[field.key] && this.asyncState[field.key].loading) || false\n\t\t},\n\n\t\t/**\n\t\t * Whether a field has any async behavior (enum or items.enum is a function).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisFieldAsync(field) {\n\t\t\treturn this.isAsyncEnum(field) || this.isAsyncItemsEnum(field)\n\t\t},\n\n\t\t/**\n\t\t * Run client-side validation on all form fields.\n\t\t * Checks required, minLength, maxLength, pattern, minimum, maximum.\n\t\t *\n\t\t * @return {boolean} True if all fields pass validation\n\t\t * @public\n\t\t */\n\t\tvalidate() {\n\t\t\tconst newErrors = {}\n\t\t\tfor (const field of this.visibleFields) {\n\t\t\t\tconst value = this.formData[field.key]\n\n\t\t\t\t// Child rows: a failing row blocks the submit and names row and field.\n\t\t\t\tif (field.widget === 'child-records') {\n\t\t\t\t\tconst problems = this.childProblems[field.key]\n\t\t\t\t\tif (Array.isArray(problems) && problems.length > 0) {\n\t\t\t\t\t\tnewErrors[field.key] = describeRowProblem(problems)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Required check\n\t\t\t\tif (field.required) {\n\t\t\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\t\t\tnewErrors[field.key] = `${field.label} is required.`\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (Array.isArray(value) && value.length === 0) {\n\t\t\t\t\t\tnewErrors[field.key] = `${field.label} is required.`\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Skip further validation if empty and not required\n\t\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Sub-objects: every row must carry the row schema's required values.\n\t\t\t\tif (field.widget === 'sub-objects' && Array.isArray(value) && field.items && Array.isArray(field.items.required)) {\n\t\t\t\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\t\t\t\tconst missing = field.items.required.find((k) => {\n\t\t\t\t\t\t\tconst cell = value[i] ? value[i][k] : undefined\n\t\t\t\t\t\t\treturn cell === undefined || cell === null || cell === ''\n\t\t\t\t\t\t})\n\t\t\t\t\t\tif (missing !== undefined) {\n\t\t\t\t\t\t\tnewErrors[field.key] = t('nextcloud-vue', 'Row {n}: {field} is required.', { n: i + 1, field: missing })\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Duration: schema minimum / maximum given as ISO strings compare in seconds.\n\t\t\t\tif (field.widget === 'duration' && typeof value === 'string') {\n\t\t\t\t\tconst v0 = field.validation || {}\n\t\t\t\t\tconst secs = durationSeconds(value)\n\t\t\t\t\tif (secs !== null && typeof v0.minimum === 'string' && durationSeconds(v0.minimum) !== null && secs < durationSeconds(v0.minimum)) {\n\t\t\t\t\t\tnewErrors[field.key] = t('nextcloud-vue', 'Minimum duration is {min}.', { min: v0.minimum })\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (secs !== null && typeof v0.maximum === 'string' && durationSeconds(v0.maximum) !== null && secs > durationSeconds(v0.maximum)) {\n\t\t\t\t\t\tnewErrors[field.key] = t('nextcloud-vue', 'Maximum duration is {max}.', { max: v0.maximum })\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst v = field.validation || {}\n\n\t\t\t\t// Whether the user actually edited this field this session.\n\t\t\t\t// Persisted server values that the user never touched are\n\t\t\t\t// trusted as-is — re-running format/pattern checks against\n\t\t\t\t// them would block editing an unrelated field (e.g. a stored\n\t\t\t\t// uuid relation or a normalised date-time).\n\t\t\t\tconst touched = !!this.touchedFields[field.key]\n\n\t\t\t\t// String length / pattern checks. minLength/maxLength only\n\t\t\t\t// apply when they are real numbers — an explicit `null`\n\t\t\t\t// (or undefined) means \"no limit\", not a zero cap.\n\t\t\t\tif (typeof value === 'string') {\n\t\t\t\t\tif (typeof v.minLength === 'number' && value.length < v.minLength) {\n\t\t\t\t\t\tnewErrors[field.key] = `Minimum ${v.minLength} characters.`\n\t\t\t\t\t} else if (typeof v.maxLength === 'number' && value.length > v.maxLength) {\n\t\t\t\t\t\tnewErrors[field.key] = `Maximum ${v.maxLength} characters.`\n\t\t\t\t\t} else if (v.pattern !== undefined && v.pattern !== null && touched) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (!new RegExp(v.pattern).test(value)) {\n\t\t\t\t\t\t\t\tnewErrors[field.key] = 'Invalid format.'\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Ignore invalid regex patterns\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Number range checks (numeric bounds only)\n\t\t\t\tif (typeof value === 'number') {\n\t\t\t\t\tif (typeof v.minimum === 'number' && value < v.minimum) {\n\t\t\t\t\t\tnewErrors[field.key] = `Minimum value is ${v.minimum}.`\n\t\t\t\t\t} else if (typeof v.maximum === 'number' && value > v.maximum) {\n\t\t\t\t\t\tnewErrors[field.key] = `Maximum value is ${v.maximum}.`\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.errors = newErrors\n\t\t\treturn Object.keys(newErrors).length === 0\n\t\t},\n\n\t\texecuteConfirm() {\n\t\t\tif (!this.validate()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.jsonFieldsValid) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.formError = null\n\t\t\tthis.loading = true\n\t\t\t// The object's own fields and the answers to its data-driven\n\t\t\t// questions go out separately: a value row references the parent,\n\t\t\t// so it cannot be written in the same call, and posting a dynamic\n\t\t\t// key to the parent schema would have OpenRegister drop it\n\t\t\t// silently — a 200, an object back, and the answer gone. A host\n\t\t\t// that ignores the second argument therefore still posts a clean\n\t\t\t// payload rather than losing declared fields to undeclared ones.\n\t\t\t// Publishing a draft runs the full validation above and clears the marker.\n\t\t\tconst submit = this.buildSubmitPayload()\n\t\t\tif (this.isDraftRecord) {\n\t\t\t\tsubmit[this.draftField] = false\n\t\t\t}\n\t\t\tconst { base, answers: raw } = splitDynamicFormData(submit)\n\t\t\tconst answers = raw.map((answer) => ({\n\t\t\t\t...answer,\n\t\t\t\tdeclarationKey: this.dynamicOwners[DYNAMIC_KEY_PREFIX + answer.definitionId] || '',\n\t\t\t}))\n\t\t\t/**\n\t\t\t * @event confirm Emitted when the user confirms the form.\n\t\t\t * Payload: form data object. Includes `id` when editing. A second\n\t\t\t * argument carries the data-driven answers (`{ answers, declarations }`)\n\t\t\t * when the schema declares `x-openregister-extends-form`; it is\n\t\t\t * `null` otherwise, which is every schema that declares nothing.\n\t\t\t */\n\t\t\tthis.$emit('confirm', base, answers.length > 0\n\t\t\t\t? { answers, declarations: this.extendsDeclarations, definitions: this.dynamicDefinitions }\n\t\t\t\t: null)\n\t\t},\n\n\t\t/**\n\t\t * Take the offered draft into the form.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\trestoreDraft() {\n\t\t\tif (this.draftOffer === null) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.formData = { ...this.formData, ...this.draftOffer.values }\n\t\t\tthis.draftOffer = null\n\t\t},\n\n\t\t/**\n\t\t * Decline the offered draft and forget it.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdiscardDraft() {\n\t\t\tthis.forgetDraft(this.formDraftKey)\n\t\t},\n\n\t\t/**\n\t\t * Save the record as a draft, without validating it.\n\t\t *\n\t\t * 🔴 THE ONE PATH AROUND VALIDATION, AND IT IS EXPLICIT. A half-filled\n\t\t * form is the whole point of a draft, so required fields are not\n\t\t * enforced. It is reachable only by pressing a button that says so, and\n\t\t * only on a schema that declares somewhere to record it.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tsaveDraft() {\n\t\t\tif (!this.canSaveDraft) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.formError = null\n\t\t\tthis.loading = true\n\t\t\tconst payload = { ...this.buildSubmitPayload(), [this.draftField]: true }\n\n\t\t\t/**\n\t\t\t * @event draft-saved Emitted when the user asks to store the record as a draft. Payload: the form data with the draft field set. The host saves it and calls `setResult()` as it would for a confirm.\n\t\t\t */\n\t\t\tthis.$emit('draft-saved', payload)\n\t\t},\n\n\t\t/**\n\t\t * Build the payload emitted on confirm. A shallow clone of formData\n\t\t * with one normalisation: an empty-string value on a field that\n\t\t * carries a `format` or `pattern` constraint is coerced to `null`.\n\t\t * An empty string is not a valid `uuid` / `email` / `date-time` etc.,\n\t\t * so re-submitting a persisted-but-blank constrained field as `''`\n\t\t * would trip the backend's format validator; `null` (no value) does\n\t\t * not. Plain unconstrained string fields are left as-is.\n\t\t *\n\t\t * @return {object} The payload to emit.\n\t\t */\n\t\tbuildSubmitPayload() {\n\t\t\tconst payload = { ...this.formData }\n\t\t\t// Hidden tenant keys: an empty one takes the platform's tenant; one\n\t\t\t// nobody can fill goes out absent rather than as '', so the\n\t\t\t// server's own default or validation handles it.\n\t\t\tconst tenant = this._platformTenant()\n\t\t\tfor (const key of this.tenantKeys) {\n\t\t\t\tif (!this._isEmptyTenantValue(payload[key])) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (tenant) {\n\t\t\t\t\tpayload[key] = tenant\n\t\t\t\t} else {\n\t\t\t\t\tdelete payload[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Child records are saved as their own objects after the parent,\n\t\t\t// never nested in the parent's payload.\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field.widget === 'child-records') {\n\t\t\t\t\tdelete payload[field.key]\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (payload[field.key] !== '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst v = field.validation || {}\n\t\t\t\tconst hasFormatConstraint = !!field.format\n\t\t\t\t\t|| (v.pattern !== undefined && v.pattern !== null)\n\t\t\t\tif (hasFormatConstraint) {\n\t\t\t\t\tpayload[field.key] = null\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Files over the inline cap upload after the object is saved.\n\t\t\tconst split = splitHeldFiles(payload, this.resolvedFields.filter((f) => f.widget === 'file').map((f) => f.key))\n\t\t\tthis.heldFiles = split.held\n\t\t\tthis.heldKept = split.kept\n\t\t\treturn split.payload\n\t\t},\n\n\t\t/**\n\t\t * Size options for a file field: the property's limit, and the inline cap when the limit is above it.\n\t\t *\n\t\t * @param {object} field The resolved field.\n\t\t * @return {object} Props for CnFileField.\n\t\t */\n\t\tfileLimits(field) {\n\t\t\tconst max = field.file && field.file.maxSize\n\t\t\tif (!max) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn max > INLINE_FILE_CAP ? { maxSize: max, inlineMax: INLINE_FILE_CAP } : { maxSize: max }\n\t\t},\n\n\t\t/**\n\t\t * Upload the held files of the saved object and report any that failed.\n\t\t *\n\t\t * @param {{id?: string, object?: object}} resultData The parent's result; its id is the saved object's id.\n\t\t * @return {Promise<object>} The result, with an `error` naming files that were not uploaded.\n\t\t */\n\t\tasync uploadHeld(resultData) {\n\t\t\tconst objectId = resultData.id || resultData.object?.id || resultData.object?.uuid || this.item?.id || this.item?.uuid\n\t\t\tconst schemaId = (this.schema && (this.schema.slug || this.schema.id)) || ''\n\t\t\tif (!objectId || !schemaId || Object.keys(this.heldFiles).length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\tthis.uploadProgress = { loaded: 0, total: 0, file: '' }\n\t\t\tconst outcome = await uploadHeldFiles({\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: String(schemaId),\n\t\t\t\tobjectId,\n\t\t\t\theld: this.heldFiles,\n\t\t\t\tkept: this.heldKept,\n\t\t\t\tonProgress: (p) => {\n\t\t\t\t\tthis.uploadProgress = p\n\t\t\t\t},\n\t\t\t})\n\t\t\tthis.uploadProgress = null\n\t\t\tthis.heldKept = { ...this.heldKept, ...Object.fromEntries(Object.entries(outcome.attached).filter(([, v]) => Array.isArray(v))) }\n\t\t\tthis.heldFiles = heldFromFailures(outcome.failed)\n\t\t\tthis.uploadFailedNames = outcome.failed.map((f) => f.file.name)\n\t\t\tthis.pendingResult = resultData\n\t\t\tif (outcome.failed.length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\treturn { ...resultData, error: [resultData.error, t('nextcloud-vue', 'Saved, but some files were not uploaded: {files}', { files: this.uploadFailedNames.join(', ') })].filter(Boolean).join(' ') }\n\t\t},\n\n\t\t/** Try the files that did not upload again. */\n\t\tasync retryUploads() {\n\t\t\tthis.loading = true\n\t\t\tconst merged = await this.uploadHeld(this.pendingResult || { success: true })\n\t\t\tthis.loading = false\n\t\t\tthis.applyResult(merged)\n\t\t},\n\n\t\t/**\n\t\t * Set the result of the save operation. Call this from the parent\n\t\t * after the API call completes.\n\t\t *\n\t\t * @param {{ success?: boolean, error?: string }} resultData - Result data to pass to the dialog\n\t\t * @public\n\t\t */\n\t\tsetResult(resultData) {\n\t\t\tthis.loading = false\n\t\t\tif (resultData && resultData.success && Object.keys(this.heldFiles).length > 0) {\n\t\t\t\tconst rows = this.resolvedFields.some((f) => f.widget === 'child-records') ? this.saveChildRecords(resultData) : Promise.resolve(resultData)\n\t\t\t\treturn rows.then((merged) => this.uploadHeld(merged)).then((merged) => this.applyResult(merged))\n\t\t\t}\n\t\t\tif (resultData && resultData.success && this.resolvedFields.some((f) => f.widget === 'child-records')) {\n\t\t\t\treturn this.saveChildRecords(resultData).then((merged) => this.applyResult(merged))\n\t\t\t}\n\t\t\treturn this.applyResult(resultData)\n\t\t},\n\n\t\t/**\n\t\t * Toast the outcome of the write, unless `feedback` is off: success\n\t\t * names the object's title (or the schema's), a failure carries the\n\t\t * server's message.\n\t\t *\n\t\t * @param {{ success?: boolean, error?: string }} resultData The result being shown.\n\t\t */\n\t\treportWrite(resultData) {\n\t\t\tif (!this.feedback || !resultData) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst feedback = useWriteFeedback()\n\t\t\tif (resultData.success) {\n\t\t\t\tconst title = objectDisplayName(this.formData || {}) || String((this.schema && this.schema.title) || '').toLowerCase()\n\t\t\t\tfeedback.success(title !== '' ? t('nextcloud-vue', 'Saved {title}', { title }) : t('nextcloud-vue', 'Saved'))\n\t\t\t}\n\t\t\tif (resultData.error) {\n\t\t\t\tfeedback.error(resultData.error)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Show a result and, on success, auto-close.\n\t\t *\n\t\t * @param {{ success?: boolean, error?: string }} resultData The result to show.\n\t\t */\n\t\tapplyResult(resultData) {\n\t\t\tthis.result = resultData\n\t\t\tthis.reportWrite(resultData)\n\t\t\t// A file that did not upload keeps the dialog open for Retry.\n\t\t\tif (resultData.success && Object.keys(this.heldFiles).length === 0) {\n\t\t\t\t// The values are on the server now, so the local copy has\n\t\t\t\t// nothing left to protect. Cleared only on SUCCESS: a failed\n\t\t\t\t// save is exactly when somebody needs their typing back.\n\t\t\t\tthis.forgetDraft(this.formDraftKey)\n\t\t\t\tthis.closeTimeout = setTimeout(() => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event close Emitted when the dialog should close: the user dismissed it, or a successful save auto-closed it.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('close')\n\t\t\t\t}, 2000)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Save the child-records tables after the parent: one bulk save and one\n\t\t * bulk delete per field. A refused row is named in the result and the\n\t\t * parent stays saved.\n\t\t *\n\t\t * @param {{success?: boolean, error?: string, id?: string, object?: object}} resultData The parent's result; its id (or `object.id`) is the parent id.\n\t\t * @return {Promise<object>} The result, with an `error` listing rows that were not saved.\n\t\t */\n\t\tasync saveChildRecords(resultData) {\n\t\t\tconst fields = this.resolvedFields.filter((f) => f.widget === 'child-records' && f.childRecords)\n\t\t\tif (fields.length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\tconst parentId = resultData.id || resultData.object?.id || resultData.object?.uuid || this.item?.id || this.item?.uuid\n\t\t\tif (!parentId) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\tconst { save } = useChildRecords()\n\t\t\tconst lines = []\n\t\t\tfor (const field of fields) {\n\t\t\t\tconst outcome = await save({\n\t\t\t\t\tregister: this.register,\n\t\t\t\t\tschema: field.childRecords.schema,\n\t\t\t\t\tparentField: field.childRecords.parentField,\n\t\t\t\t\tparentId,\n\t\t\t\t\toriginal: this.childOriginals[field.key] || [],\n\t\t\t\t\trows: Array.isArray(this.formData[field.key]) ? this.formData[field.key] : [],\n\t\t\t\t})\n\t\t\t\tfor (const failure of outcome.failed) {\n\t\t\t\t\tlines.push(`${field.label}: ${failure.reason}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lines.length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\treturn { ...resultData, error: [resultData.error, t('nextcloud-vue', 'Saved, but some rows were not saved: {rows}', { rows: lines.join('; ') })].filter(Boolean).join(' ') }\n\t\t},\n\n\t\t/**\n\t\t * Set validation errors from the server WITHOUT leaving the form phase,\n\t\t * so the user can correct the data. Call this from the parent (instead\n\t\t * of `setResult`) when the API returns a validation error.\n\t\t *\n\t\t * @param {object} [fieldErrors] Object keyed by field key with per-field error messages\n\t\t * @param {string} [message] Form-level message shown in an error note above the fields\n\t\t * @public\n\t\t */\n\t\tsetValidationErrors(fieldErrors = {}, message = null) {\n\t\t\tthis.loading = false\n\t\t\tthis.result = null\n\t\t\tthis.errors = { ...this.errors, ...fieldErrors }\n\t\t\tthis.formError = message\n\t\t\tif (this.feedback && message) {\n\t\t\t\tuseWriteFeedback().error(message)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* A select option breaks between words only, never inside one. */\n.cn-form-dialog__option {\n\tdisplay: block;\n\tmin-width: 0;\n\twhite-space: normal;\n\toverflow-wrap: normal;\n\tword-break: normal;\n\thyphens: manual;\n}\n\n.cn-form-dialog__draft-actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n/* The state is announced in WORDS, and the colour only repeats them. A\n reader who cannot see it still hears \"Draft saved\" (WCAG 2.2 SC 1.4.1). */\n.cn-form-dialog__draft-state {\n\talign-self: center;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tmargin-inline-end: auto;\n}\n\n.cn-form-dialog__switch-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-form-dialog__form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-form-dialog__field {\n\tmargin-bottom: 8px;\n}\n\n/* Two-column layout. The grid lives on a wrapper that only exists when\n `columns` is 2, so a single-column form keeps its plain block flow and\n cannot be reflowed by a rule meant for the other mode.\n\n `min-width: 0` on the items is what stops a long select or a wide input\n from pushing its column past its share: a grid item's default `auto`\n minimum is its content, so without it one long option label widens the\n whole column and the two stop being equal. */\n.cn-form-dialog__form--two-column {\n\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr;\n\tcolumn-gap: 16px;\n\talign-items: start;\n}\n\n.cn-form-dialog__form--two-column > * {\n\tmin-width: 0;\n}\n\n/* Anything that is not a field (the form-level error note, the slots above\n and below the fields) spans the full width, so a note card never sits in\n a half-width column beside an input. */\n.cn-form-dialog__form--two-column > :not(.cn-form-dialog__field),\n.cn-form-dialog__form--two-column > .cn-form-dialog__field--wide {\n\tgrid-column: 1 / -1;\n}\n\n/* A narrow viewport gets the single column back. Two columns of roughly\n 150px each are worse than one readable one. */\n@media (max-width: 700px) {\n\t.cn-form-dialog__form--two-column {\n\t\tdisplay: block;\n\t}\n}\n\n.cn-form-dialog__textarea-wrapper,\n.cn-form-dialog__select-wrapper,\n.cn-form-dialog__json-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-form-dialog__label {\n\tfont-weight: 600;\n\tfont-size: 0.9em;\n\tcolor: var(--color-main-text);\n}\n\n/* Cross-app semantic reference (ADR-048) whose provider isn't installed —\n rendered disabled; the wrapper carries the mouse-over tooltip. */\n.cn-form-dialog__semantic-unresolved {\n\tcursor: not-allowed;\n}\n\n.cn-form-dialog__textarea {\n\twidth: 100%;\n\tmin-height: 80px;\n\tpadding: 8px;\n\tborder: 2px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-large);\n\tbackground-color: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tresize: vertical;\n}\n\n.cn-form-dialog__textarea:focus {\n\tborder-color: var(--color-primary-element);\n\toutline: none;\n}\n\n.cn-form-dialog__textarea:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n.cn-form-dialog__helper {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-form-dialog__helper--error {\n\tcolor: var(--color-error);\n}\n\n.cn-form-dialog__dynamic-loading {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 0;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_createBlock","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","_createTextVNode","_normalizeClass","_renderSlot","_Fragment","_renderList","_resolveDynamicComponent","_mergeProps","_withCtx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFormDialog.vue3.js","sources":["../../../../src/components/CnFormDialog/CnFormDialog.vue?vue&type=template&id=c3eeb0a2&lang.js"],"sourcesContent":["<template>\n\t<CnDialog\n\t\t:look=\"look\"\n\t\t:width=\"width\"\n\t\tdefaultWidth=\"form\"\n\t\t:eyebrow=\"eyebrow\"\n\t\t:subtitle=\"subtitle\"\n\t\t:name=\"resolvedTitle\"\n\t\t:size=\"size\"\n\t\t:noClose=\"loading\"\n\t\t@closing=\"$emit('close')\">\n\t\t<!-- Result phase -->\n\t\t<div v-if=\"result !== null\"\n\t\t\tclass=\"cn-form-dialog__result\"\n\t\t\tdata-testid=\"cn-modal\"\n\t\t\tdata-testid-modal=\"cn-form-dialog\"\n\t\t\tdata-testid-phase=\"result\">\n\t\t\t<NcNoteCard v-if=\"result.success\" type=\"success\">\n\t\t\t\t{{ resolvedSuccessText }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard v-if=\"result.error\" type=\"error\">\n\t\t\t\t{{ result.error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"uploadFailedNames.length > 0 && Object.keys(heldFiles).length > 0\"\n\t\t\t\t:disabled=\"loading\"\n\t\t\t\tdata-testid=\"cn-form-dialog-retry-uploads\"\n\t\t\t\t@click=\"retryUploads\">\n\t\t\t\t{{ t('nextcloud-vue', 'Retry') }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<!-- Progress of a file uploading after the save. -->\n\t\t<p v-if=\"uploadProgress\"\n\t\t\tclass=\"cn-form-dialog__upload-progress\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-form-dialog-upload-progress\">\n\t\t\t{{ t('nextcloud-vue', 'Uploading {file}', { file: uploadProgress.file }) }}\n\t\t\t<progress v-if=\"uploadProgress.total > 0\" :max=\"uploadProgress.total\" :value=\"uploadProgress.loaded\" />\n\t\t</p>\n\n\t\t<!-- Form phase -->\n\t\t<div v-else\n\t\t\tclass=\"cn-form-dialog__form\"\n\t\t\t:class=\"{ 'cn-form-dialog__form--two-column': columns === 2 }\"\n\t\t\tdata-testid=\"cn-modal\"\n\t\t\tdata-testid-modal=\"cn-form-dialog\"\n\t\t\tdata-testid-phase=\"form\">\n\t\t\t<!-- The draft this form was left in. OFFERED, never applied: a form\n\t\t\t that fills itself is indistinguishable from one the server\n\t\t\t prefilled, and somebody submits last week's answers. -->\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"draftOffer !== null\"\n\t\t\t\ttype=\"info\"\n\t\t\t\tdata-testid=\"cn-form-dialog-draft-offer\">\n\t\t\t\t{{ t('nextcloud-vue', 'You have unsaved changes from an earlier visit.') }}\n\t\t\t\t<div class=\"cn-form-dialog__draft-actions\">\n\t\t\t\t\t<NcButton data-testid=\"cn-form-dialog-draft-restore\" @click=\"restoreDraft\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Restore') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton data-testid=\"cn-form-dialog-draft-discard\" @click=\"discardDraft\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Discard') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</NcNoteCard>\n\n\t\t\t<!-- Form-level error (e.g. server validation) — keeps the form visible so the user can fix the data -->\n\t\t\t<NcNoteCard v-if=\"formError\" type=\"error\" data-testid=\"cn-form-dialog-error\">\n\t\t\t\t{{ formError }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<!-- @slot form Replace the entire auto-generated form. -->\n\t\t\t<!-- @binding {object[]} fields The resolved, visible field definitions. -->\n\t\t\t<!-- @binding {object} form-data The live form values, keyed by field. -->\n\t\t\t<!-- @binding {object} errors The current validation errors, keyed by field. -->\n\t\t\t<!-- @binding {Function} update-field Write one field: `updateField(key, value)`. -->\n\t\t\t<slot\n\t\t\t\tv-if=\"$slots.form\"\n\t\t\t\tname=\"form\"\n\t\t\t\t:fields=\"resolvedFields\"\n\t\t\t\t:formData=\"formData\"\n\t\t\t\t:errors=\"errors\"\n\t\t\t\t:updateField=\"updateField\" />\n\n\t\t\t<!-- Auto-generated form -->\n\t\t\t<template v-else>\n\t\t\t\t<!-- @slot before-fields Content above the first auto-generated field. Use it for introductory text or an input the schema does not describe. -->\n\t\t\t\t<slot name=\"before-fields\" />\n\n\t\t\t\t<!-- `data-cn-field` is the only per-field identity this form\n\t\t\t\t exposes to a test. Without it an assertion about a field can\n\t\t\t\t only match on the label TEXT — which is the thing under test\n\t\t\t\t the moment labels are translated, so the assertion passes by\n\t\t\t\t construction in whichever language it was written in. -->\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"field in visibleFields\"\n\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t:data-cn-field=\"field.key\"\n\t\t\t\t\tclass=\"cn-form-dialog__field\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'cn-form-dialog__field--wide': fieldSpansBothColumns(field),\n\t\t\t\t\t\t'cn-form-field--half': isBoardLook && field.width === 'half',\n\t\t\t\t\t\t'cn-form-field--invalid': isBoardLook && !!errors[field.key],\n\t\t\t\t\t}\">\n\t\t\t\t\t<!-- Board look: the label sits above the control, the error between\n\t\t\t\t\t them; the control and the hint keep their place below. -->\n\t\t\t\t\t<CnFormField\n\t\t\t\t\t\tv-if=\"showsOuterLabel(field)\"\n\t\t\t\t\t\t:controlId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t:text=\"field.label\"\n\t\t\t\t\t\t:optional=\"!field.required\"\n\t\t\t\t\t\t:optionalLabel=\"optionalLabel\"\n\t\t\t\t\t\t:error=\"errors[field.key] || ''\"\n\t\t\t\t\t\t:errorId=\"errorIdFor(field)\" />\n\t\t\t\t\t<!-- @slot field-{key} Replace one auto-generated field with your own control. -->\n\t\t\t\t\t<!-- @binding {object} field The field definition. -->\n\t\t\t\t\t<!-- @binding {*} value The field's current value. -->\n\t\t\t\t\t<!-- @binding {string} error The field's validation error, if any. -->\n\t\t\t\t\t<!-- @binding {Function} update-field Write the value: `updateField(key, value)`. -->\n\t\t\t\t\t<slot\n\t\t\t\t\t\tv-if=\"$slots['field-' + field.key]\"\n\t\t\t\t\t\t:name=\"'field-' + field.key\"\n\t\t\t\t\t\t:field=\"field\"\n\t\t\t\t\t\t:value=\"formData[field.key]\"\n\t\t\t\t\t\t:error=\"errors[field.key]\"\n\t\t\t\t\t\t:updateField=\"updateField\" />\n\n\t\t\t\t\t<!-- referenceType (AD-18): render the integration's\n\t\t\t\t\t single-entity widget instead of a plain input. -->\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"resolveReferenceWidget(field)\"\n\t\t\t\t\t\tv-else-if=\"resolveReferenceWidget(field)\"\n\t\t\t\t\t\tv-bind=\"referenceWidgetProps(field)\"\n\t\t\t\t\t\t@input=\"value => updateField(field.key, value)\" />\n\n\t\t\t\t\t<!-- referenceSemanticType (ADR-048): a field that declares a\n\t\t\t\t\t canonical semantic-type URI whose provider schema is NOT\n\t\t\t\t\t installed renders DISABLED with a mouse-over tooltip. When\n\t\t\t\t\t a provider IS installed the field is transformed upstream\n\t\t\t\t\t (applySemanticResolution → a `$ref` reference) so it flows\n\t\t\t\t\t through the normal select branch below; only the\n\t\t\t\t\t unresolved/loading case lands here. -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"field.referenceSemanticType && !isSemanticResolved(field)\"\n\t\t\t\t\t\tclass=\"cn-form-dialog__semantic-unresolved\"\n\t\t\t\t\t\t:title=\"semanticUnavailableText(field)\">\n\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t:helperText=\"isSemanticLoading(field) ? '' : semanticUnavailableText(field)\"\n\t\t\t\t\t\t\t:disabled=\"true\"\n\t\t\t\t\t\t\t:loading=\"isSemanticLoading(field)\"\n\t\t\t\t\t\t\t:placeholder=\"field.description\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Concept-scheme choice whose options could not be loaded: the\n\t\t\t\t\t plain text input it had before, with a line saying so. -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"field.codeList && codedFailed[field.key]\"\n\t\t\t\t\t\tclass=\"cn-form-dialog__coded-fallback\">\n\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t:modelValue=\"codedFallbackText(field)\"\n\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t:helperText=\"t('nextcloud-vue', 'The list of choices could not be loaded. Enter the value by hand.')\"\n\t\t\t\t\t\t\t@update:modelValue=\"onCodedFallbackInput(field, $event)\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Auto-generated field -->\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t<!-- Text / Email / URL. The helper line lives outside the\n\t\t\t\t\t\t input (rather than on `helper-text`) so an over-long\n\t\t\t\t\t\t description can hang its info popover off it. -->\n\t\t\t\t\t\t<template v-if=\"field.widget === 'text' || field.widget === 'email' || field.widget === 'url'\">\n\t\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t:type=\"field.widget === 'email' ? 'email' : field.widget === 'url' ? 'url' : 'text'\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</template>\n\n\t\t\t\t\t\t<!-- Number -->\n\t\t\t\t\t\t<template v-else-if=\"field.widget === 'number'\">\n\t\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value !== '' ? Number(value) : null)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</template>\n\n\t\t\t\t\t\t<!-- Textarea -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'textarea'\" class=\"cn-form-dialog__textarea-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t:id=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\tclass=\"cn-form-dialog__textarea\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:value=\"formData[field.key] || ''\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\trows=\"4\"\n\t\t\t\t\t\t\t\t@input=\"updateField(field.key, $event.target.value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Object reference with inline create (`x-allow-create`):\n\t\t\t\t\t\t a select-OR-create picker over the referenced schema.\n\t\t\t\t\t\t Checked before the plain select branch so create-enabled\n\t\t\t\t\t\t references get CnResourceSelect; plain references keep the\n\t\t\t\t\t\t read-only NcSelect below. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-else-if=\"(isReferenceField(field) || isReferenceArrayField(field)) && field.allowCreate\"\n\t\t\t\t\t\t\tclass=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<CnResourceSelect\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:register=\"referenceRegister(field)\"\n\t\t\t\t\t\t\t\t:schema=\"String(field.reference.schema)\"\n\t\t\t\t\t\t\t\t:labelField=\"referenceLabelField(field)\"\n\t\t\t\t\t\t\t\t:multiple=\"!!field.reference.multiple\"\n\t\t\t\t\t\t\t\t:modelValue=\"referenceModelValue(field)\"\n\t\t\t\t\t\t\t\t:clearable=\"!field.required || !!field.reference.multiple\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:preload=\"true\"\n\t\t\t\t\t\t\t\t:createHandler=\"term => openNestedCreate(field, term)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => onReferenceSelected(field, value)\"\n\t\t\t\t\t\t\t\t@create=\"obj => onReferenceCreated(field, obj)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Enum toggle (`widget: \"switch\"` on a 2-value enum):\n\t\t\t\t\t\t renders a switch mapping off→enum[0], on→last enum value. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'switch'\" class=\"cn-form-dialog__switch-wrapper\">\n\t\t\t\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t\t\t\t:modelValue=\"isSwitchOn(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, switchValueFor(field, value))\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Select (enum / $ref object reference / single Nextcloud user, supports async function).\n\t\t\t\t\t\t A single Nextcloud-user field (`widget: \"user\"`) renders here too:\n\t\t\t\t\t\t `:user-select=\"isUserField(field)\"` flips this NcSelect into NC's native\n\t\t\t\t\t\t user picker, and the async load/search/select machinery (getEffective*,\n\t\t\t\t\t\t onAsyncSearch) resolves real users — mirroring how `user-multiselect`\n\t\t\t\t\t\t shares the multiselect branch below. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'select' || field.widget === 'user' || field.widget === 'group'\" class=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<!-- A `widget: \"user\"` field renders NcSelectUsers, everything\n\t\t\t\t\t\t\t else NcSelect. It used to be one NcSelect with\n\t\t\t\t\t\t\t `:user-select=\"isUserField(field)\"`, and @nextcloud/vue 9\n\t\t\t\t\t\t\t REMOVED that prop, so a user field had silently become a\n\t\t\t\t\t\t\t plain select. NcSelectUsers wraps NcSelect and merges the\n\t\t\t\t\t\t\t rest of these bindings through, so the async load, search\n\t\t\t\t\t\t\t and select machinery is unchanged either way. -->\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"isUserField(field) ? 'NcSelectUsers' : 'NcSelect'\"\n\t\t\t\t\t\t\t\tv-cn-select-aria=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:options=\"getEffectiveOptions(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"getEffectiveSelectedOption(field)\"\n\t\t\t\t\t\t\t\t:clearable=\"!field.required\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:loading=\"isFieldLoading(field)\"\n\t\t\t\t\t\t\t\t:filterable=\"!isAsyncEnum(field)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"onEffectiveSelectChange(field, $event)\"\n\t\t\t\t\t\t\t\t@search=\"isAsyncEnum(field) ? onAsyncSearch(field, $event) : undefined\">\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-option']\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<!-- @slot field-{key}-option Render one dropdown option for a select, multiselect or tags field. -->\n\t\t\t\t\t\t\t\t\t<!-- @binding {object} option The option being rendered (`{ id, label }`). -->\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<!-- NcSelect's own option splits a label in two spans\n\t\t\t\t\t\t\t\t (NcEllipsisedOption, keeping its last characters\n\t\t\t\t\t\t\t\t visible), and in a narrow dropdown the halves broke\n\t\t\t\t\t\t\t\t onto two lines mid-word: \"Agricu lture\". A plain\n\t\t\t\t\t\t\t\t label wraps at spaces only. NcSelectUsers keeps its\n\t\t\t\t\t\t\t\t avatar rendering. -->\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-else-if=\"!isUserField(field)\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-selected-option']\"\n\t\t\t\t\t\t\t\t\t#selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<!-- @slot field-{key}-selected-option Render the chosen option of a select, multiselect or tags field. -->\n\t\t\t\t\t\t\t\t\t<!-- @binding {object} option The selected option (`{ id, label }`). -->\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-selected-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Multiselect (array enum items / $ref array / Nextcloud users, supports async function) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'multiselect' || field.widget === 'user-multiselect' || field.widget === 'group-multiselect'\" class=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\t\tv-cn-select-aria=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:options=\"getEffectiveArrayOptions(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"getEffectiveSelectedArrayOptions(field)\"\n\t\t\t\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:loading=\"isFieldLoading(field)\"\n\t\t\t\t\t\t\t\t:filterable=\"!isAsyncItemsEnum(field)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"onEffectiveMultiSelectChange(field, $event)\"\n\t\t\t\t\t\t\t\t@search=\"isAsyncItemsEnum(field) ? onAsyncSearch(field, $event) : undefined\">\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-option']\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-selected-option']\"\n\t\t\t\t\t\t\t\t\t#selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-selected-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</NcSelect>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Tags (array, freeform, supports async suggestions) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'tags'\" class=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<!-- TODO: restore `:options` to `asyncState[field.key]?.options` once on Vue 3 (buble doesn't support optional chaining) -->\n\t\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\t\tv-cn-select-aria=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] || []\"\n\t\t\t\t\t\t\t\t:options=\"isFieldAsync(field) ? ((asyncState[field.key] && asyncState[field.key].options) || []) : []\"\n\t\t\t\t\t\t\t\t:multiple=\"true\"\n\t\t\t\t\t\t\t\t:keepOpen=\"true\"\n\t\t\t\t\t\t\t\t:taggable=\"true\"\n\t\t\t\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:loading=\"isFieldLoading(field)\"\n\t\t\t\t\t\t\t\t:filterable=\"!isFieldAsync(field)\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"updateField(field.key, $event)\"\n\t\t\t\t\t\t\t\t@search=\"isFieldAsync(field) ? onAsyncSearch(field, $event) : undefined\">\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-option']\"\n\t\t\t\t\t\t\t\t\t#option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template\n\t\t\t\t\t\t\t\t\tv-if=\"$slots['field-' + field.key + '-selected-option']\"\n\t\t\t\t\t\t\t\t\t#selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<slot :name=\"'field-' + field.key + '-selected-option'\" v-bind=\"optionProps\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t<template v-else #selected-option=\"optionProps\">\n\t\t\t\t\t\t\t\t\t<span class=\"cn-form-dialog__option\">{{ optionText(optionProps) }}</span>\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</NcSelect>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Checkbox / Switch (boolean) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'checkbox'\" class=\"cn-form-dialog__switch-wrapper\">\n\t\t\t\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t\t\t\t:modelValue=\"!!formData[field.key]\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Date / Datetime (NcTextField's type validator rejects\n\t\t\t\t\t\t 'date'/'datetime-local', so use NcDateTimePickerNative) -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-else-if=\"field.widget === 'date' || field.widget === 'datetime'\"\n\t\t\t\t\t\t\tclass=\"cn-form-dialog__select-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<NcDateTimePickerNative\n\t\t\t\t\t\t\t\t:id=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:type=\"field.widget === 'datetime' ? 'datetime-local' : 'date'\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:hideLabel=\"true\"\n\t\t\t\t\t\t\t\t:modelValue=\"dateValueFor(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"date => onDateFieldInput(field, date)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- JSON (type: 'object'|'array'|... with widget: 'json'): parses on input, stores parsed value in formData -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'json'\" class=\"cn-form-dialog__json-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<CnJsonViewer\n\t\t\t\t\t\t\t\t:value=\"jsonStringFor(field)\"\n\t\t\t\t\t\t\t\tlanguage=\"json\"\n\t\t\t\t\t\t\t\t:readOnly=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:errorText=\"jsonErrors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:value=\"value => onJsonFieldInput(field, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Code (freeform editor, stored as raw string; optional `field.language` chooses highlighting) -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'code'\" class=\"cn-form-dialog__json-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<CnJsonViewer\n\t\t\t\t\t\t\t\t:value=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:language=\"field.language || 'auto'\"\n\t\t\t\t\t\t\t\t:readOnly=\"field.readOnly\"\n\t\t\t\t\t\t\t\t@update:value=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- File (widget: 'file'): one file or several, small ones inline, big ones uploaded after save, optional camera. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'file'\" class=\"cn-form-dialog__file-wrapper\">\n\t\t\t\t\t\t\t<CnFileField\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key]\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:accept=\"field.file ? field.file.accept : ''\"\n\t\t\t\t\t\t\t\t:multiple=\"!!(field.file && field.file.multiple)\"\n\t\t\t\t\t\t\t\t:capture=\"field.file ? field.file.capture : ''\"\n\t\t\t\t\t\t\t\tv-bind=\"fileLimits(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:helperText=\"errors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Duration (widget: 'duration'): number + unit over an ISO 8601 string. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'duration'\" class=\"cn-form-dialog__duration-wrapper\">\n\t\t\t\t\t\t\t<CnDurationField\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : null\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Child records (widget: 'child-records'): another schema's records as an editable table, saved after the parent. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'child-records' && field.childRecords\" class=\"cn-form-dialog__child-records-wrapper\">\n\t\t\t\t\t\t\t<CnChildRecordsField\n\t\t\t\t\t\t\t\t:modelValue=\"Array.isArray(formData[field.key]) ? formData[field.key] : []\"\n\t\t\t\t\t\t\t\t:config=\"field.childRecords\"\n\t\t\t\t\t\t\t\t:register=\"register\"\n\t\t\t\t\t\t\t\t:parentId=\"item ? (item.id || item.uuid || '') : ''\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"errors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\"\n\t\t\t\t\t\t\t\t@loaded=\"rows => { childOriginals[field.key] = rows }\"\n\t\t\t\t\t\t\t\t@validity=\"problems => { childProblems[field.key] = problems }\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Sub-objects (widget: 'sub-objects'): an array of objects as an editable table. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'sub-objects' && field.items\" class=\"cn-form-dialog__sub-objects-wrapper\">\n\t\t\t\t\t\t\t<CnSubObjectsField\n\t\t\t\t\t\t\t\t:modelValue=\"Array.isArray(formData[field.key]) ? formData[field.key] : []\"\n\t\t\t\t\t\t\t\t:items=\"field.items\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"errors[field.key] || ''\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Registry type-ahead (widget: 'property-source'): CnPropertySourceField;\n\t\t\t\t\t\t a pick can fill empty sibling fields from config.fill. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'property-source' && field.propertySource\" class=\"cn-form-dialog__property-source-wrapper\">\n\t\t\t\t\t\t\t<CnPropertySourceField\n\t\t\t\t\t\t\t\t:provider=\"field.propertySource.provider\"\n\t\t\t\t\t\t\t\t:mode=\"field.propertySource.mode\"\n\t\t\t\t\t\t\t\t:inputId=\"'cn-form-' + field.key\"\n\t\t\t\t\t\t\t\t:inputLabel=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\"\n\t\t\t\t\t\t\t\t@resolved=\"payload => onPropertySourceResolved(field, payload)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Icon (widget: 'icon'): renders CnIconBrowser, forwarding the field's icon config.\n\t\t\t\t\t\t `searchable` is gone — the browser always searches. -->\n\t\t\t\t\t\t<div v-else-if=\"field.widget === 'icon'\" class=\"cn-form-dialog__icon-wrapper\">\n\t\t\t\t\t\t\t<label v-if=\"!showsOuterLabel(field)\" :for=\"'cn-form-' + field.key\" class=\"cn-form-dialog__label\">\n\t\t\t\t\t\t\t\t{{ field.label }}{{ requiredSuffix(field) }}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<CnIconBrowser\n\t\t\t\t\t\t\t\t:value=\"formData[field.key] != null ? String(formData[field.key]) : null\"\n\t\t\t\t\t\t\t\t:sources=\"field.iconSources || ['mdi']\"\n\t\t\t\t\t\t\t\t:catalogues=\"field.catalogues || {}\"\n\t\t\t\t\t\t\t\t:allowCustomSvg=\"!!field.allowCustomSvg\"\n\t\t\t\t\t\t\t\t:clearable=\"!field.required\"\n\t\t\t\t\t\t\t\t@input=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Fallback: text input -->\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<NcTextField\n\t\t\t\t\t\t\t\t:id=\"fieldControlId(field)\"\n\t\t\t\t\t\t\t\t:label=\"fieldLabelText(field)\"\n\t\t\t\t\t\t\t\t:labelOutside=\"isBoardLook\"\n\t\t\t\t\t\t\t\tv-bind=\"fieldAria(field)\"\n\t\t\t\t\t\t\t\t:modelValue=\"formData[field.key] != null ? String(formData[field.key]) : ''\"\n\t\t\t\t\t\t\t\t:error=\"!!errors[field.key]\"\n\t\t\t\t\t\t\t\t:disabled=\"field.readOnly\"\n\t\t\t\t\t\t\t\t:placeholder=\"field.description\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"value => updateField(field.key, value)\" />\n\t\t\t\t\t\t\t<CnFieldHelper\n\t\t\t\t\t\t\t\t:id=\"helpId(field)\"\n\t\t\t\t\t\t\t\t:text=\"field.description\"\n\t\t\t\t\t\t\t\t:more=\"field.descriptionLong\"\n\t\t\t\t\t\t\t\t:help=\"field.help\"\n\t\t\t\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t\t\t\t:error=\"helperError(field)\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<small\n\t\t\t\t\t\t\tv-if=\"assignedFrom[field.key]\"\n\t\t\t\t\t\t\tclass=\"cn-form-dialog__assigned\"\n\t\t\t\t\t\t\taria-live=\"polite\"\n\t\t\t\t\t\t\tdata-testid=\"cn-form-dialog-assigned\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Filled in from {field}', { field: assignedFrom[field.key] }) }}\n\t\t\t\t\t\t</small>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- The extra questions a chosen case type / product line asks\n\t\t\t\t are fetched, so say they are coming. Silence here reads as\n\t\t\t\t \"this type has none\", and the user submits an incomplete\n\t\t\t\t form believing it was complete. -->\n\t\t\t\t<div v-if=\"dynamicLoading\"\n\t\t\t\t\tclass=\"cn-form-dialog__dynamic-loading\"\n\t\t\t\t\tdata-testid=\"cn-form-dialog-dynamic-loading\">\n\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t\t<span>{{ dynamicLoadingLabel }}</span>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- @slot after-fields Content below the last auto-generated field. -->\n\t\t\t\t<slot name=\"after-fields\" />\n\t\t\t</template>\n\t\t</div>\n\n\t\t<!-- \"Create\" on a select-or-create reference (`x-allow-create`) opens\n\t\t the referenced schema's own form on top of this one. Saving it\n\t\t selects the new object in the field that asked for it. -->\n\t\t<CnFormDialog\n\t\t\tv-if=\"nestedCreate !== null\"\n\t\t\tref=\"nestedCreateDialog\"\n\t\t\t:schema=\"nestedCreate.schema\"\n\t\t\t:register=\"nestedCreate.register\"\n\t\t\t:item=\"null\"\n\t\t\t:initialData=\"nestedCreate.initialData\"\n\t\t\t:excludeFields=\"nestedCreate.form.excludeFields\"\n\t\t\t:includeFields=\"nestedCreate.form.includeFields\"\n\t\t\t:fieldOverrides=\"nestedCreate.form.fieldOverrides\"\n\t\t\t:size=\"nestedCreate.form.size\"\n\t\t\t:columns=\"nestedCreate.form.columns\"\n\t\t\t:recoverDraft=\"false\"\n\t\t\tdata-testid=\"cn-form-dialog-nested-create\"\n\t\t\t@confirm=\"onNestedCreateConfirm\"\n\t\t\t@close=\"onNestedCreateClose\" />\n\n\t\t<CnReplaceValuesDialog\n\t\t\tv-if=\"pendingReplace !== null\"\n\t\t\t:changes=\"pendingReplace.changes\"\n\t\t\t@accept=\"resolveReplace(true)\"\n\t\t\t@decline=\"resolveReplace(false)\" />\n\n\t\t<template #actions>\n\t\t\t<!-- Board look: Save draft is the tertiary action at the far left, with\n\t\t\t the draft-state live region beside it (screens-dialog-parity). -->\n\t\t\t<span\n\t\t\t\tv-if=\"isBoardLook\"\n\t\t\t\tclass=\"cn-dialog__tertiary\"\n\t\t\t\tdata-testid=\"cn-dialog-tertiary\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"result === null && canSaveDraft\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:disabled=\"loading\"\n\t\t\t\t\tdata-testid=\"cn-form-dialog-save-draft\"\n\t\t\t\t\t@click=\"saveDraft\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Save draft') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<span\n\t\t\t\t\tclass=\"cn-form-dialog__draft-state\"\n\t\t\t\t\taria-live=\"polite\"\n\t\t\t\t\tdata-testid=\"cn-form-dialog-draft-state\">{{ draftIndicatorLabel }}</span>\n\t\t\t</span>\n\t\t\t<!-- One announcement per state change, so a screen reader hears\n\t\t\t \"Draft saved\" once rather than on every keystroke. -->\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tclass=\"cn-form-dialog__draft-state\"\n\t\t\t\taria-live=\"polite\"\n\t\t\t\tdata-testid=\"cn-form-dialog-draft-state\">{{ draftIndicatorLabel }}</span>\n\t\t\t<NcButton @click=\"$emit('close')\">\n\t\t\t\t{{ result !== null ? closeLabel : cancelLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"!isBoardLook && result === null && canSaveDraft\"\n\t\t\t\t:disabled=\"loading\"\n\t\t\t\tdata-testid=\"cn-form-dialog-save-draft\"\n\t\t\t\t@click=\"saveDraft\">\n\t\t\t\t{{ t('nextcloud-vue', 'Save draft') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"result === null\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"loading || confirmDisabled || !requiredFieldsFilled || !jsonFieldsValid\"\n\t\t\t\t@click=\"executeConfirm\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"20\" />\n\t\t\t\t\t<Plus v-else-if=\"isCreateMode\" :size=\"20\" />\n\t\t\t\t\t<ContentSaveOutline v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ resolvedConfirmLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { getLanguage, translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcCheckboxRadioSwitch, NcDateTimePickerNative, NcLoadingIcon, NcNoteCard, NcSelect, NcSelectUsers, NcTextField } from '@nextcloud/vue'\nimport ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnReplaceValuesDialog from '../../dialogs/CnReplaceValuesDialog.vue'\nimport CnChildRecordsField from '../CnChildRecordsField/CnChildRecordsField.vue'\nimport CnDialog from '../CnDialog/CnDialog.vue'\nimport CnDurationField from '../CnDurationField/CnDurationField.vue'\nimport CnFieldHelper from '../CnFieldHelper/CnFieldHelper.vue'\nimport CnFileField from '../CnFileField/CnFileField.vue'\nimport CnFormField from '../CnFormField/CnFormField.vue'\nimport CnIconBrowser from '../CnIconBrowser/CnIconBrowser.vue'\nimport CnJsonViewer from '../CnJsonViewer/CnJsonViewer.vue'\nimport CnPropertySourceField from '../CnPropertySourceField/CnPropertySourceField.vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport CnSubObjectsField from '../CnSubObjectsField/CnSubObjectsField.vue'\nimport { describeRowProblem, useChildRecords } from '../../composables/useChildRecords.js'\nimport { draftIndicatorText, draftKey, formDraftMixin, readDraft } from '../../composables/useFormDraft.js'\nimport { heldFromFailures, splitHeldFiles, uploadHeldFiles } from '../../composables/useHeldFileUpload.js'\nimport { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'\nimport { TENANT_CONTEXT_KEY } from '../../composables/useTenantContext.js'\nimport { useWriteFeedback } from '../../composables/useWriteFeedback.js'\nimport { cnSelectAria } from '../../directives/cnSelectAria.js'\nimport { dialogBoardMixin } from '../../mixins/dialogBoard.js'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { resolveCreateOverrideHandler } from '../../utils/actionsDispatcher.js'\nimport { loadCurrentUserProfile } from '../../utils/currentUserProfile.js'\nimport {\n\tdefinitionQueryParams,\n\tDYNAMIC_KEY_PREFIX,\n\textendsFormDeclarations,\n\tprefillDeclarations,\n\tprefillValues,\n\tpropertiesFromDefinitions,\n\tsplitDynamicFormData,\n} from '../../utils/dynamicProperties.js'\nimport { shouldShow } from '../../utils/fieldCondition.js'\nimport { computeAssignments } from '../../utils/formAssign.js'\nimport { resolveNextcloudGroup, searchNextcloudGroups } from '../../utils/groupAutocomplete.js'\nimport { durationSeconds } from '../../utils/isoDuration.js'\nimport { objectDisplayName } from '../../utils/objectName.js'\nimport { languageOptions, resolveDefaultToken, timezoneOptions } from '../../utils/pickerOptions.js'\nimport { getDotted, planPropertySourceFill } from '../../utils/propertySourceFill.js'\nimport { resolveFilterValue } from '../../utils/resolveFilterTokens.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { fieldsFromSchema, isTenantProperty } from '../../utils/schema.js'\nimport { resolveNextcloudUser, searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\n/**\n * Widgets that take the full width of a two-column form.\n *\n * These are the multi-line ones: a textarea, the JSON editor and the code\n * editor are unusable in half a dialog. Every other widget in the template's\n * vocabulary (text, number, select, date, switch, checkbox and friends) is a\n * single line and pairs happily.\n *\n * @type {string[]}\n */\nconst WIDE_WIDGETS = ['textarea', 'json', 'code']\n\n/** Largest file carried inline in the saved payload; bigger ones upload after save. */\nconst INLINE_FILE_CAP = 1024 * 1024\n\n/**\n * OpenRegister semantic-type discovery endpoint (ADR-048). Resolves a\n * canonical semantic-type URI to the provider schema that implements it.\n * Returns HTTP 200 with `{ resolved, registerSlug, schemaSlug, appId }` even\n * when nothing implements the URI (`resolved: false`). The exact route path\n * is still being finalised on the OpenRegister side — this path is the small,\n * single point of truth; a 404 (route absent) degrades to `resolved: false`\n * (see `resolveSemanticReference`), never a crash.\n */\nconst SEMANTIC_RESOLVE_ENDPOINT = '/apps/openregister/api/schemas/resolve-by-implements'\n\n/**\n * CnFormDialog — Create/edit dialog with auto-generated form from schema.\n *\n * When `item` is null, operates in create mode. When `item` is provided,\n * operates in edit mode. Auto-generates form fields from schema using\n * `fieldsFromSchema()`, but supports slot overrides at three levels:\n *\n * - `#form` — Replace the entire form content\n * - `#field-{key}` — Replace a single auto-generated field\n * - `#field-{key}-option` — Customize dropdown option rendering for a select/multiselect/tags field\n * - `#field-{key}-selected-option` — Customize selected option display for a select/multiselect/tags field\n * - `#before-fields` / `#after-fields` — Inject content around fields\n *\n * ## Schema-driven pickers\n *\n * Three JSON-Schema property shapes auto-render richer widgets:\n *\n * - `{ format: 'user' }` → a Nextcloud-user picker (async-searches the core\n * autocomplete endpoint; stores the chosen uid string).\n * - `{ format: 'uuid', $ref: '<schema>', 'x-allow-create': true }` → a single\n * object reference that renders `CnResourceSelect` — pick an existing object\n * OR create one inline from the typed term. Without `x-allow-create` a `$ref`\n * stays a plain select of existing objects.\n * - `{ enum: ['no', 'yes'], widget: 'switch' }` → a toggle mapping off → the\n * first enum value and on → the last enum value (keeps the stored value an\n * enum string, so an enum-driven lifecycle is unaffected).\n *\n * ## Async select support\n *\n * Select, multiselect, and tags fields support async options by setting `field.enum`\n * (or `field.items.enum` for multiselect) to an async function instead of a static array:\n *\n * ```js\n * { key: 'org', widget: 'select', enum: async (query) => fetchOrgs(query) }\n * ```\n *\n * The function receives the search query and must return an array of option objects\n * (each must have a `label` property for default display). Options are loaded on mount\n * (with empty query) and on each search input (debounced, default 300ms, configurable\n * via `field.debounce`). Async selects store the full option object in formData.\n *\n * ## OpenRegister object references (`$ref`)\n *\n * A schema property that is an object reference renders as a searchable\n * dropdown of the referenced objects instead of a free-text UUID box:\n *\n * - `{ type: 'string', format: 'uuid', $ref: '<schema-slug>' }` → single-select\n * - `{ type: 'array', items: { $ref: '<schema-slug>' } }` → multi-select\n *\n * `fieldsFromSchema` records the reference on the field as\n * `field.reference = { schema, multiple }`. Pass the `register` prop so the\n * dialog can fetch the referenced objects (`GET /api/objects/{register}/{schema}`)\n * — each is mapped to `{ label: <human name>, value: <uuid> }`. The chosen\n * value stored in formData is the UUID (single) or array of UUIDs (multiple),\n * NOT the full object. In edit mode the stored UUID is resolved to its label\n * so the current selection displays. When `register` is empty the field falls\n * back to a plain text input.\n *\n * ## Nextcloud user references\n *\n * A schema property marked as a Nextcloud user renders as a searchable\n * dropdown of real Nextcloud users instead of a free-text box:\n *\n * - `{ type: 'string', referenceType: 'nextcloud-user' }` → single-select\n * (`format: 'user'` / `'username'` also work as markers)\n * - `{ type: 'array', items: { referenceType: 'nextcloud-user' } }` → multi-select\n *\n * `fieldsFromSchema` tags the field as `field.userPicker = { multiple }`.\n * Users are loaded from the core autocomplete OCS endpoint (available to every\n * authenticated user), mapped to `{ label: <display name>, value: <uid> }`.\n * The value stored in formData is the UID string (single) or array of UIDs\n * (multiple) — never the display object. In edit mode the stored UID is\n * resolved to its display name so the current selection shows. Needs no\n * `register` prop. If the OCS call fails the picker fails soft (empty options;\n * the stored UID still shows).\n *\n * ## Tenant fields\n *\n * The platform sets the tenant a record belongs to, so the form never asks for\n * it. A property counts as the tenant when it is named `tenant`, `tenant_id` /\n * `tenantId` or `tenant_uuid`, or when it carries `x-openregister-tenant: true`,\n * `x-platform-managed: true`, `x-managed-by: 'platform'`, `format: 'tenant'` or\n * `referenceType: 'tenant'`. `x-openregister-tenant: false` opts a property out;\n * `fieldOverrides: { tenant_id: { hidden: false } }` shows it on one form.\n *\n * The hidden property is still sent. On edit it keeps the record's value. On\n * create it takes the active tenant from the tenant context the app provides\n * (`CnAppRoot`'s `initialOrganisationUuid`, or `provideTenantContext`). There\n * is no other source: an app's tenant is its own notion, not OpenRegister's\n * active organisation. When the app provides none, the key is left out of the\n * payload rather than sent blank, so the server's own default or validation\n * handles it.\n *\n * ## JSON / code fields\n *\n * Two widgets render a CnJsonViewer-powered editor:\n *\n * - `widget: 'json'` — Parses on input. formData holds the parsed value (object,\n * array, number, string, boolean, or `null` for empty). Invalid JSON displays an\n * inline error and blocks the confirm button until fixed. Pair with `type: 'object'`\n * (or any type) to opt a property out of the default object-filter in `fieldsFromSchema`.\n * - `widget: 'code'` — Stores the raw string. Optional `field.language` chooses\n * syntax highlighting (`'json'|'xml'|'html'|'text'|'auto'`, default `'auto'`).\n *\n * ## Icon field\n *\n * - `widget: 'icon'` — Renders a `CnIconBrowser`. Optional field config forwards to\n * the picker: `iconSources` (→ `sources`, default `['mdi']`), `catalogues`, and\n * `allowCustomSvg`. (`searchable` is obsolete — the browser always searches.)\n * formData holds the selected icon value (registry key, source value, URL, or\n * raw SVG). The bundled NL-government sets are offered alongside the catalogues.\n *\n * The dialog does NOT perform the save itself — it emits a `confirm` event\n * with the form data. The parent performs the actual API call and calls\n * `setResult()` via a ref.\n *\n * ```vue\n * <CnFormDialog\n * v-if=\"showFormDialog\"\n * ref=\"formDialog\"\n * :schema=\"schema\"\n * :item=\"editItem\"\n * @confirm=\"onFormConfirm\"\n * @close=\"showFormDialog = false\" />\n * ```\n *\n * // In methods:\n * async onFormConfirm(formData) {\n * try {\n * if (formData.id) {\n * await store.updateItem(formData.id, formData)\n * } else {\n * await store.createItem(formData)\n * }\n * this.$refs.formDialog.setResult({ success: true })\n * } catch (e) {\n * this.$refs.formDialog.setResult({ error: e.message })\n * }\n * }\n *\n * <caption>Async select with custom option rendering</caption>\n * ```vue\n * <CnFormDialog :fields=\"[{\n * key: 'organisation',\n * widget: 'select',\n * label: 'Organisation',\n * required: true,\n * enum: async (query) => {\n * const results = await store.search(query)\n * return results.map(o => ({ label: o.name, id: o.uuid, ...o }))\n * },\n * debounce: 500,\n * }]\" @confirm=\"onConfirm\">\n * <template #field-organisation-option=\"{ name, description }\">\n * <strong>{{ name }}</strong>\n * <p>{{ description }}</p>\n * </template>\n * <template #field-organisation-selected-option=\"{ name }\">\n * {{ name }}\n * </template>\n * </CnFormDialog>\n * ```\n *\n * @event confirm Emitted when the user confirms the form. Payload: formData object (includes `id` in edit mode).\n * @event close Emitted when the dialog should be closed (cancel, close button, or auto-close after success).\n */\nexport default {\n\tname: 'CnFormDialog',\n\n\tdirectives: { cnSelectAria },\n\n\tcomponents: {\n\t\tCnFileField,\n\t\tCnDurationField,\n\t\tCnPropertySourceField,\n\t\tCnReplaceValuesDialog,\n\t\tCnSubObjectsField,\n\t\tCnChildRecordsField,\n\t\tCnDialog,\n\t\tCnFormField,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcLoadingIcon,\n\t\tNcTextField,\n\t\tNcSelect,\n\t\tNcSelectUsers,\n\t\tNcDateTimePickerNative,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnJsonViewer,\n\t\tCnIconBrowser,\n\t\tCnResourceSelect,\n\t\tCnFieldHelper,\n\t\tPlus,\n\t\tContentSaveOutline,\n\t},\n\n\tmixins: [dialogBoardMixin, formDraftMixin()],\n\n\tinject: {\n\t\t_cnTenantContext: {\n\t\t\tfrom: TENANT_CONTEXT_KEY,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Field\n\t\t * labels/descriptions come from schema property titles, authored in\n\t\t * English as the canonical source; the visible label is resolved\n\t\t * through this function so it follows the user's language. Defaults to\n\t\t * identity when used standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\n\t\t/**\n\t\t * The app manifest, the v2 registry, the legacy customComponents map and\n\t\t * the registry-modal opener, all provided by CnAppRoot. A select-or-create\n\t\t * picker reads them to create an object the way the app's own index page\n\t\t * does (`config.createOverride` / `config.createModal`). Standalone (no\n\t\t * CnAppRoot) they are absent and the generic nested form is used.\n\t\t */\n\t\tcnManifest: { default: null },\n\t\tcnRegistry: { default: null },\n\t\tcnCustomComponents: { default: null },\n\t\tcnOpenModal: { default: null },\n\t},\n\n\tprops: {\n\t\t/** Schema for auto-generating fields. Either schema or fields must be provided. */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Existing item for edit mode. Pass null for create mode. */\n\t\titem: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Register slug to resolve OpenRegister object references against.\n\t\t *\n\t\t * A schema property that is an object reference (`$ref: '<schema-slug>'`,\n\t\t * or `items.$ref` for an array) renders as a searchable dropdown of the\n\t\t * referenced objects (label = human name, value = UUID) instead of a\n\t\t * free-text UUID box. The `$ref` value is the referenced *schema* slug;\n\t\t * this prop supplies the *register* the objects live in. When empty,\n\t\t * reference fields fall back to a plain text input (no fetch attempted).\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The word shown as \"(optional)\" after an optional field's label in the\n\t\t * board look. Required fields carry no mark and there is no asterisk.\n\t\t */\n\t\toptionalLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'optional'),\n\t\t},\n\n\t\t/**\n\t\t * Seed values for CREATE mode, keyed by field. Merged over the schema\n\t\t * defaults when opening a new-item form. Use it to pre-link a child to\n\t\t * its parent when adding from a detail page (e.g. `{ lead: '<uuid>' }`).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tinitialData: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Field keys rendered read-only (disabled) and immutable — typically the\n\t\t * parent reference seeded via `initialData` so the user can't repoint a\n\t\t * child away from the record it was created under.\n\t\t *\n\t\t * @type {string[]}\n\t\t */\n\t\tlockedFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Keep what the user typed and offer it back when this form reopens.\n\t\t *\n\t\t * Local only: nothing is written to the server until they save. On by\n\t\t * default because it changes nothing they did not type, and because the\n\t\t * case it solves is a closed tab.\n\t\t */\n\t\trecoverDraft: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Offer a \"Save draft\" button that stores the record with a draft\n\t\t * marker instead of validating it.\n\t\t *\n\t\t * Opt-in, and inert unless the schema carries `draftField`: a button\n\t\t * that writes a property the schema does not declare would have\n\t\t * OpenRegister drop it silently, and the record would come back\n\t\t * looking published.\n\t\t */\n\t\tallowDraft: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The boolean property that marks a record as a draft. */\n\t\tdraftField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'isDraft',\n\t\t},\n\n\t\t/**\n\t\t * The app the draft belongs to, and who is typing it.\n\t\t *\n\t\t * 🔴 THE USER IS PART OF THE DRAFT KEY. A shared browser profile at a\n\t\t * service desk is the ordinary case in a municipality, and a draft\n\t\t * keyed without the user hands the next person at the counter what the\n\t\t * last one typed. A host that passes nothing gets `anonymous`, which\n\t\t * is correct for a single-user context and wrong for a counter — so\n\t\t * the prop exists and is documented rather than guessed at.\n\t\t */\n\t\tdraftAppId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Who is typing, for the draft key. See `draftAppId`. */\n\t\tdraftUserId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Dialog title. Defaults to \"Create {schema.title}\" or \"Edit {schema.title}\". */\n\t\tdialogTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Manual field definitions. Overrides schema-generated fields when provided. */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Field keys to exclude from auto-generated form */\n\t\texcludeFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Field keys to include (whitelist mode) */\n\t\tincludeFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Per-field overrides passed to fieldsFromSchema */\n\t\tfieldOverrides: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Object context forwarded to integration single-entity\n\t\t * widgets rendered for fields that declare a `referenceType`\n\t\t * (AD-18): `{ register, schema, objectId }`. Optional.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\treferenceContext: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Which field is the \"name\".\n\t\t *\n\t\t * @deprecated Never read. The result note card renders whatever message the\n\t\t * parent hands to `setResult()`, so this field names nothing.\n\t\t */\n\t\tnameField: { // eslint-disable-line vue/no-unused-properties -- deprecated no-op, kept so apps still passing it do not leak it onto the DOM through $attrs.\n\t\t\ttype: String,\n\t\t\tdefault: 'title',\n\t\t},\n\n\t\t/** NcDialog size */\n\t\tsize: {\n\t\t\ttype: String,\n\t\t\tdefault: 'normal',\n\t\t},\n\n\t\t/**\n\t\t * How many columns the auto-generated fields flow into.\n\t\t *\n\t\t * `1` (the default) keeps every existing form exactly as it is. `2`\n\t\t * pairs the fields into two columns, which is worth it once a form\n\t\t * asks enough questions that the person has to scroll to see whether\n\t\t * there are more. Pair it with `size=\"large\"`, or the two columns are\n\t\t * merely two narrow ones.\n\t\t *\n\t\t * A field whose widget needs the room (a textarea, a JSON editor)\n\t\t * spans both columns regardless. Below 700px the layout collapses\n\t\t * back to one column, so this is safe on a narrow screen.\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1,\n\t\t\tvalidator: (value) => value === 1 || value === 2,\n\t\t},\n\n\t\t/** Success message. Defaults to \"Item saved successfully.\" */\n\t\tsuccessText: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label for the cancel button */\n\t\t/**\n\t\t * Text shown while the fields a chosen value brings with it are being\n\t\t * fetched. The default is deliberately generic; a host that knows the\n\t\t * domain can say what is actually loading (\"Loading the questions for\n\t\t * this case type.\").\n\t\t */\n\t\tdynamicLoadingLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Loading the fields this choice adds.'),\n\t\t},\n\n\t\t/** Label for the dismiss button while the form is still showing. */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the close button */\n\t\tcloseLabel: { type: String, default: () => t('nextcloud-vue', 'Close') },\n\t\t/**\n\t\t * Report the write with a toast: \"Saved {title}\" after a successful\n\t\t * save, the server's message after a failed one. `false` suppresses\n\t\t * the toasts and changes nothing else.\n\t\t */\n\t\tfeedback: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Confirm button label. Defaults to \"Create\" or \"Save\". */\n\t\tconfirmLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Keep the Confirm button disabled regardless of the form state, for a host that knows the form cannot be completed. */\n\t\tconfirmDisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['close', 'confirm', 'draft-saved'],\n\n\tsetup() {\n\t\t// Pluggable integration registry — used to resolve fields that\n\t\t// declare `referenceType: '<integration-id>'` (AD-18) to the\n\t\t// integration's single-entity widget. Cheap when no such\n\t\t// fields exist.\n\t\tconst { resolveWidget, getById } = useIntegrationRegistry()\n\t\treturn {\n\t\t\tresolveRegistryWidget: resolveWidget,\n\t\t\tgetRegistryIntegration: getById,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tformData: {},\n\t\t\terrors: {},\n\t\t\t/** Field key -> label of the answer a rule filled it in from (`assign`). */\n\t\t\tassignedFrom: {},\n\t\t\t/** The signed-in user's profile for `@me.*` defaults. */\n\t\t\tmeProfile: {},\n\t\t\t/** Files over the inline cap, by property key, uploaded once the object is saved. */\n\t\t\theldFiles: {},\n\t\t\t/** Entries a list file property keeps while its held files upload. */\n\t\t\theldKept: {},\n\t\t\t/** Names of files that did not upload (the dialog offers Retry). */\n\t\t\tuploadFailedNames: [],\n\t\t\t/** The save result the uploads belong to. */\n\t\t\tpendingResult: null,\n\t\t\t/** The file uploading now: `{ file, loaded, total }`, or null. */\n\t\t\tuploadProgress: null,\n\t\t\t/** Fill-over-typed-values question awaiting an answer: `{ changes, resolve }`, or null. */\n\t\t\tpendingReplace: null,\n\t\t\tloading: false,\n\t\t\tresult: null,\n\t\t\t/** Form-level error message (e.g. a server validation failure) shown above the fields without leaving the form phase. */\n\t\t\tformError: null,\n\t\t\tcloseTimeout: null,\n\t\t\t/** Per-field async state: { [fieldKey]: { options: [], loading: false, searchTimeout: null } } */\n\t\t\tasyncState: {},\n\t\t\t/** Per-field editor string for `json` widgets (preserves input between keystrokes even while invalid) */\n\t\t\tjsonDrafts: {},\n\t\t\t/** Per-field parse-error messages for `json` widgets (blocks confirm) */\n\t\t\tjsonErrors: {},\n\t\t\t/** Child rows as loaded, per child-records field, to diff against on save. */\n\t\t\tchildOriginals: {},\n\t\t\t/** Row problems reported by each child-records field. */\n\t\t\tchildProblems: {},\n\t\t\t/**\n\t\t\t * Resolved labels for `$ref` object-reference values, keyed by UUID:\n\t\t\t * `{ [uuid]: '<human label>' }`. Populated as reference options load\n\t\t\t * and when an edit-mode UUID is resolved by id, so the select shows\n\t\t\t * the human name for the currently-stored UUID(s). The stored value\n\t\t\t * itself always remains the UUID — this is display-only.\n\t\t\t */\n\t\t\treferenceLabels: {},\n\t\t\t/** Concept-scheme fields whose options request failed or came back empty. */\n\t\t\tcodedFailed: {},\n\t\t\t/** Scheme of each coded field, as OpenRegister reported it (for the notation route). */\n\t\t\tcodedSchemes: {},\n\t\t\t/**\n\t\t\t * Cross-app semantic-reference resolutions (ADR-048), keyed by the\n\t\t\t * semantic-type URI: `{ [uri]: { status, resolved, registerSlug,\n\t\t\t * schemaSlug, appId } }`. `status` is 'loading' | 'done'. Populated\n\t\t\t * once per URI by `resolveSemanticReferences()` (called from\n\t\t\t * `created()`), so the discovery endpoint is hit at most once per\n\t\t\t * distinct URI, not per render.\n\t\t\t */\n\t\t\tsemanticResolutions: {},\n\t\t\t/** Field keys the user has actually edited this session (used to avoid re-validating untouched persisted server values) */\n\t\t\ttouchedFields: {},\n\t\t\t/**\n\t\t\t * Schema properties contributed by the record's own data rather\n\t\t\t * than its schema — the extra questions a chosen case type (or\n\t\t\t * product line, or segment) asks. Keyed by the same prefixed id\n\t\t\t * the form data uses. Replaced wholesale whenever a driving value\n\t\t\t * changes; `{}` while nothing is chosen. See utils/dynamicProperties.\n\t\t\t */\n\t\t\tdynamicProperties: {},\n\t\t\t/** Prefixed keys among `dynamicProperties` that must be answered. */\n\t\t\tdynamicRequired: [],\n\t\t\t/**\n\t\t\t * Which declaration each dynamic field came from, keyed by its\n\t\t\t * prefixed key. A schema may declare more than one driving\n\t\t\t * property, and their answers go to different value schemas — an\n\t\t\t * answer that cannot name its own declaration would be written to\n\t\t\t * all of them.\n\t\t\t */\n\t\t\tdynamicOwners: {},\n\t\t\t/** Definition records behind the dynamic fields, for the confirm payload. */\n\t\t\tdynamicDefinitions: [],\n\t\t\t/** Whether a definitions fetch is in flight (the fields are announced as loading rather than absent). */\n\t\t\tdynamicLoading: false,\n\t\t\t/**\n\t\t\t * The driver signature the currently-loaded `dynamicProperties`\n\t\t\t * belong to. A fetch that resolves after its driver already moved\n\t\t\t * on is discarded against this, so a fast second pick cannot be\n\t\t\t * overwritten by the first pick's slower response.\n\t\t\t */\n\t\t\tdynamicToken: '',\n\t\t\t/**\n\t\t\t * The prefill signature the most recent prefill ran for. A slower\n\t\t\t * earlier fetch is discarded against this, the same way\n\t\t\t * `dynamicToken` guards the definitions fetch.\n\t\t\t */\n\t\t\tprefillToken: '',\n\t\t\t/**\n\t\t\t * The inline create in progress, or null: `{ field, schema,\n\t\t\t * register, slug, initialData, resolve }`. Set when \"Create\" is\n\t\t\t * chosen on a select-or-create reference; renders a nested form\n\t\t\t * for the referenced schema.\n\t\t\t */\n\t\t\tnestedCreate: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Where this form's draft lives, or '' when recovery is off.\n\t\t *\n\t\t * @return {string} The storage key.\n\t\t */\n\t\tformDraftKey() {\n\t\t\tif (this.recoverDraft !== true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\treturn draftKey({\n\t\t\t\tappId: this.draftAppId,\n\t\t\t\tschema: (this.schema?.slug || this.schema?.title || ''),\n\t\t\t\tobjectId: (this.item?.id || this.item?.uuid || ''),\n\t\t\t\tuserId: this.draftUserId,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether \"Save draft\" may be offered.\n\t\t *\n\t\t * Both halves are required. `allowDraft` is the host asking for it; the\n\t\t * schema declaring `draftField` is what makes it possible. Without the\n\t\t * property OpenRegister drops the marker on save with a 200 and an\n\t\t * object back, and the record reads as published.\n\t\t *\n\t\t * @return {boolean} True when the button is offered.\n\t\t */\n\t\tcanSaveDraft() {\n\t\t\tif (this.allowDraft !== true || !this.draftField) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn Object.hasOwn((this.schema?.properties || {}), this.draftField)\n\t\t},\n\n\t\t/**\n\t\t * What the draft indicator announces, or '' when it says nothing.\n\t\t * While the dialog shows a failed save it never says Saved: it says\n\t\t * the draft is kept on this device, so it cannot be read as the\n\t\t * server save that just failed.\n\t\t *\n\t\t * @return {string} The text.\n\t\t * @spec openspec/changes/cell-labels-and-draft-indicator/specs/cell-labels-and-draft-indicator/spec.md#requirement-the-draft-indicator-never-reads-as-a-server-save-after-a-failed-save\n\t\t */\n\t\tdraftIndicatorLabel() {\n\t\t\tconst saveFailed = this.result !== null && typeof this.result === 'object'\n\t\t\t\t&& (Boolean(this.result.error) || this.result.success === false)\n\t\t\treturn draftIndicatorText(this.draftState, this.draftSavedAt, Date.now(), { saveFailed })\n\t\t},\n\n\t\tisCreateMode() {\n\t\t\treturn !this.item\n\t\t},\n\n\t\tschemaTitle() {\n\t\t\t// Schema titles are authored in English as the canonical source, the\n\t\t\t// same as the property titles `fieldsFromSchema` translates — so the\n\t\t\t// heading of a Dutch form does not read \"Edit Time entry\".\n\t\t\tif (this.schema && this.schema.title) {\n\t\t\t\treturn this.cnTranslate(this.schema.title)\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Item')\n\t\t},\n\n\t\t/**\n\t\t * Whether the record being edited is a stored draft: drafts are on, the\n\t\t * schema declares the marker, and the record has it set. Such a record\n\t\t * shows \"(draft)\" in the title and is published, not merely saved.\n\t\t *\n\t\t * @return {boolean} True for a draft record.\n\t\t */\n\t\tisDraftRecord() {\n\t\t\treturn this.canSaveDraft && !!this.item && this.item[this.draftField] === true\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\tif (this.dialogTitle) {\n\t\t\t\treturn this.cnTranslate(this.dialogTitle)\n\t\t\t}\n\t\t\tif (this.isDraftRecord) {\n\t\t\t\treturn t('nextcloud-vue', 'Edit {title} (draft)', { title: this.schemaTitle })\n\t\t\t}\n\t\t\treturn this.isCreateMode\n\t\t\t\t? t('nextcloud-vue', 'Create {title}', { title: this.schemaTitle })\n\t\t\t\t: t('nextcloud-vue', 'Edit {title}', { title: this.schemaTitle })\n\t\t},\n\n\t\tresolvedConfirmLabel() {\n\t\t\tif (this.confirmLabel) {\n\t\t\t\treturn this.confirmLabel\n\t\t\t}\n\t\t\tif (this.isDraftRecord) {\n\t\t\t\treturn t('nextcloud-vue', 'Publish')\n\t\t\t}\n\t\t\treturn this.isCreateMode ? t('nextcloud-vue', 'Create') : t('nextcloud-vue', 'Save')\n\t\t},\n\n\t\tresolvedSuccessText() {\n\t\t\tif (this.successText) {\n\t\t\t\treturn this.successText\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{title} saved successfully.', { title: this.schemaTitle })\n\t\t},\n\n\t\t/** Whether all required fields have a non-empty value (hidden fields are skipped) */\n\t\trequiredFieldsFilled() {\n\t\t\treturn this.visibleFields\n\t\t\t\t.filter((f) => f.required)\n\t\t\t\t.every((f) => {\n\t\t\t\t\tconst val = this.formData[f.key]\n\t\t\t\t\tif (val === null || val === undefined || val === '') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (Array.isArray(val) && val.length === 0) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t},\n\n\t\t/** Whether every `json` widget currently parses successfully */\n\t\tjsonFieldsValid() {\n\t\t\treturn Object.keys(this.jsonErrors).every((k) => !this.jsonErrors[k])\n\t\t},\n\n\t\t/**\n\t\t * The schema's `x-openregister-extends-form` declarations — properties\n\t\t * whose chosen value brings further fields with it.\n\t\t *\n\t\t * Empty for the overwhelming majority of schemas, which is what keeps\n\t\t * the whole mechanism free: no declaration, no fetch, no watcher work.\n\t\t *\n\t\t * @return {Array<{key: string, config: object}>} The declarations.\n\t\t */\n\t\textendsDeclarations() {\n\t\t\treturn this.fields ? [] : extendsFormDeclarations(this.schema)\n\t\t},\n\n\t\t/**\n\t\t * A stable signature of every driving property's current value.\n\t\t *\n\t\t * Watched instead of the individual fields so one handler covers a\n\t\t * schema with several declarations, and so re-selecting the same value\n\t\t * does not re-fetch.\n\t\t *\n\t\t * @return {string} The signature ('' when nothing is chosen).\n\t\t */\n\t\tdynamicDriverToken() {\n\t\t\treturn this.extendsDeclarations\n\t\t\t\t.map(({ key }) => `${key}=${this.dynamicDriverValue(this.formData[key])}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * The schema's `x-openregister-prefill` declarations — properties\n\t\t * whose chosen record answers OTHER fields of this same form.\n\t\t *\n\t\t * Create mode only. In edit mode a blank field is a decision someone\n\t\t * already made about an existing record, and filling it because the\n\t\t * case type has an opinion would rewrite that decision on open.\n\t\t *\n\t\t * @return {Array<{key: string, config: object}>} The declarations.\n\t\t */\n\t\tprefillDecls() {\n\t\t\tif (this.fields || !this.isCreateMode) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn prefillDeclarations(this.schema)\n\t\t},\n\n\t\t/**\n\t\t * A stable signature of every prefilling property's current value.\n\t\t *\n\t\t * @return {string} The signature; changes drive a prefill.\n\t\t */\n\t\tprefillDriverToken() {\n\t\t\treturn this.prefillDecls\n\t\t\t\t.map(({ key }) => `${key}=${this.dynamicDriverValue(this.formData[key])}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * Every property whose `x-relation-filter` points at another FIELD of\n\t\t * this same form, paired with that field's key.\n\t\t *\n\t\t * `{ caseType: '@object.caseType' }` on a `status` picker says \"only\n\t\t * the statuses of the case type chosen above\". Only `@object.<key>`\n\t\t * tokens qualify: a literal value or `@objectId` cannot change while\n\t\t * the dialog is open, so neither can invalidate an option list.\n\t\t *\n\t\t * @return {Array<{key: string, drivers: string[]}>} Field key => the form keys it depends on.\n\t\t */\n\t\trelationFilterDecls() {\n\t\t\tconst properties = (this.schema && this.schema.properties) || {}\n\t\t\tconst decls = []\n\t\t\tfor (const [key, prop] of Object.entries(properties)) {\n\t\t\t\tconst filter = prop && prop['x-relation-filter']\n\t\t\t\tif (!filter || typeof filter !== 'object') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst drivers = Object.values(filter)\n\t\t\t\t\t.filter((v) => typeof v === 'string' && v.startsWith('@object.'))\n\t\t\t\t\t.map((v) => v.slice('@object.'.length))\n\t\t\t\tif (drivers.length > 0) {\n\t\t\t\t\tdecls.push({ key, drivers })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn decls\n\t\t},\n\n\t\t/**\n\t\t * A signature of the values concept-scheme fields take as their context.\n\t\t *\n\t\t * @return {string} A change means some coded field's options are stale.\n\t\t */\n\t\tcodedContextToken() {\n\t\t\treturn this.resolvedFields\n\t\t\t\t.filter((f) => f.codeList && f.codeList.contextProperty)\n\t\t\t\t.map((f) => `${f.key}:${String(this.formData[f.codeList.contextProperty] ?? '')}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * A stable signature of every value a relation filter depends on.\n\t\t *\n\t\t * @return {string} The signature; a change means some picker's options are stale.\n\t\t */\n\t\trelationFilterToken() {\n\t\t\treturn this.relationFilterDecls\n\t\t\t\t.flatMap(({ drivers }) => drivers)\n\t\t\t\t.map((key) => `${key}=${this.dynamicDriverValue(this.formData[key])}`)\n\t\t\t\t.join('&')\n\t\t},\n\n\t\t/**\n\t\t * Fields derived from `dynamicProperties`, through the SAME engine the\n\t\t * schema's own fields go through — so a definition gets its widget,\n\t\t * enum labels, validation and description handling from one place\n\t\t * rather than a parallel implementation that drifts.\n\t\t *\n\t\t * @return {object[]} The dynamic fields, ordered after the schema's own.\n\t\t */\n\t\tdynamicFields() {\n\t\t\tif (Object.keys(this.dynamicProperties).length === 0) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn fieldsFromSchema(\n\t\t\t\t{ properties: this.dynamicProperties, required: this.dynamicRequired },\n\t\t\t\t{ translate: this.cnTranslate },\n\t\t\t)\n\t\t},\n\n\t\tresolvedFields() {\n\t\t\t// Manual fields take priority\n\t\t\tconst base = this.fields\n\t\t\t\t? this.fields\n\t\t\t\t: fieldsFromSchema(this.schema, {\n\t\t\t\t\t\texclude: this.excludeFields,\n\t\t\t\t\t\tinclude: this.includeFields,\n\t\t\t\t\t\toverrides: this.fieldOverrides,\n\t\t\t\t\t\thideTenant: true,\n\t\t\t\t\t\ttranslate: this.cnTranslate,\n\t\t\t\t\t\tlanguage: getLanguage(),\n\t\t\t\t\t}).concat(this.dynamicFields)\n\n\t\t\t// Render locked fields (parent references seeded via initialData) as\n\t\t\t// read-only so the disabled binding on every widget branch applies.\n\t\t\tif (!this.lockedFields.length) {\n\t\t\t\treturn base\n\t\t\t}\n\t\t\treturn base.map((field) => (\n\t\t\t\tthis.lockedFields.includes(field.key)\n\t\t\t\t\t? { ...field, readOnly: true }\n\t\t\t\t\t: field\n\t\t\t))\n\t\t},\n\n\t\t/**\n\t\t * Fields filtered through their per-field `condition` / `visibleWhen`\n\t\t * descriptor. Fields without a condition are always visible.\n\t\t *\n\t\t * The template iterates this computed instead of `resolvedFields`,\n\t\t * so hidden fields don't render at all. A watcher (below) clears\n\t\t * the form-data value for any field that transitions from visible\n\t\t * to hidden, so stale state is never submitted.\n\t\t *\n\t\t * @return {object[]} The visible subset of `resolvedFields`.\n\t\t */\n\t\tvisibleFields() {\n\t\t\treturn this.resolvedFields\n\t\t\t\t.filter((field) => shouldShow(field, this.formData))\n\t\t\t\t.map((field) => this.applySemanticResolution(field))\n\t\t\t\t.map((field) => this.degradeUnresolvableReference(field))\n\t\t\t\t.map((field) => this.applyCodePicker(field))\n\t\t},\n\n\t\t/**\n\t\t * The schema properties that hold the record's tenant, which the form\n\t\t * hides (see `isTenantProperty`) and fills itself: from the record\n\t\t * being edited, else the tenant context the app provides. A key a `fieldOverrides[key].hidden === false`\n\t\t * brings back is a normal field again and is not listed here.\n\t\t *\n\t\t * @return {string[]} The hidden tenant property keys.\n\t\t */\n\t\ttenantKeys() {\n\t\t\tif (this.fields || !this.schema || !this.schema.properties) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn Object.entries(this.schema.properties)\n\t\t\t\t.filter(([key, prop]) => {\n\t\t\t\t\tconst override = this.fieldOverrides && this.fieldOverrides[key]\n\t\t\t\t\treturn !(override && override.hidden === false) && isTenantProperty(key, prop)\n\t\t\t\t})\n\t\t\t\t.map(([key]) => key)\n\t\t},\n\t},\n\n\twatch: {\n\t\t/**\n\t\t * Keep the draft in step with what is typed.\n\t\t *\n\t\t * Deep, because a field writes into `formData[key]` rather than\n\t\t * replacing the object. Debounced in the mixin so a long form is not\n\t\t * one storage write per keystroke.\n\t\t */\n\t\tformData: {\n\t\t\tdeep: true,\n\t\t\thandler(values) {\n\t\t\t\t// 🔴 THE FIRST CHANGE IS THE COMPONENT SEEDING THE FORM, NOT\n\t\t\t\t// SOMEBODY TYPING. Writing a draft for it stores the empty\n\t\t\t\t// form over a real one, and the indicator announces \"Saving\n\t\t\t\t// draft\" about work nobody did — an instrument reporting\n\t\t\t\t// activity that did not happen, which is the whole class of\n\t\t\t\t// defect this library keeps paying down.\n\t\t\t\tif (this.draftSeeded !== true) {\n\t\t\t\t\tthis.draftSeeded = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Nothing is written while a draft is being offered: the user\n\t\t\t\t// has not chosen yet, and overwriting the stored draft with the\n\t\t\t\t// empty form they are looking at would destroy the thing they\n\t\t\t\t// are about to be asked about.\n\t\t\t\tif (!this.formDraftKey || this.draftOffer !== null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tthis.scheduleDraftWrite(this.formDraftKey, values)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Re-seed the form when the record changes — WITHOUT discarding\n\t\t * edits the user has already made to the record now arriving.\n\t\t *\n\t\t * A host can bind `item` to a value that is not resolved yet:\n\t\t * CnDetailPage binds it to `currentObject`, a read of the object\n\t\t * store that is `null` until the page's own fetch lands. The dialog\n\t\t * therefore opens over a record that has not arrived, renders every\n\t\t * field blank (a null `item` is create mode), and re-seeds when the\n\t\t * record shows up. Re-seeding wholesale at that point overwrites\n\t\t * whatever was typed in between, so Save submits the PRISTINE record\n\t\t * and the edit is lost with a 200 and no error anywhere.\n\t\t *\n\t\t * Seed from the record, then put the user's own values back on top:\n\t\t * untouched fields take the record's value (a whole-object PUT must\n\t\t * not blank them), touched fields keep what the user typed.\n\t\t *\n\t\t * @param {object|null} newItem The record now bound.\n\t\t * @param {object|null} oldItem The record previously bound.\n\t\t */\n\t\titem: {\n\t\t\timmediate: true,\n\t\t\thandler(newItem, oldItem) {\n\t\t\t\t// Scoped to the record the values were typed into. Swapping\n\t\t\t\t// the dialog to a DIFFERENT record starts from that record,\n\t\t\t\t// so an edit can never leak from one row onto another.\n\t\t\t\tconst pending = this.isSameRecord(oldItem, newItem)\n\t\t\t\t\t? this.pendingUserEdits()\n\t\t\t\t\t: null\n\t\t\t\tthis.initFormData(newItem)\n\t\t\t\tthis.restoreUserEdits(pending)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * When a field transitions from visible to hidden, clear its\n\t\t * form-data value so a stale (now-irrelevant) value isn't\n\t\t * carried into the submitted payload. We diff key lists rather\n\t\t * than mutating during the computed itself.\n\t\t *\n\t\t * @param {object[]} newFields The new visible field set.\n\t\t * @param {object[]} oldFields The previous visible field set.\n\t\t */\n\t\t/**\n\t\t * Load the fields a newly-chosen driving value brings with it.\n\t\t *\n\t\t * Immediate, because edit mode opens with the value already set and\n\t\t * the stored answers are unreadable without their definitions.\n\t\t *\n\t\t * @param {string} token The new driver signature.\n\t\t */\n\t\tdynamicDriverToken: {\n\t\t\timmediate: true,\n\t\t\thandler(token) {\n\t\t\t\tthis.loadDynamicProperties(token)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Prefill the fields the chosen record answers.\n\t\t *\n\t\t * NOT immediate, unlike the definitions watcher above. Immediate would\n\t\t * fire once on open with an empty driver and, worse, would run against\n\t\t * whatever `initialData` seeded — so a value a caller passed in\n\t\t * deliberately could be treated as \"empty enough\" on the very first\n\t\t * tick. Prefill is a response to a person choosing something.\n\t\t *\n\t\t * @param {string} token The new driver signature.\n\t\t */\n\t\tprefillDriverToken(token) {\n\t\t\tthis.applyPrefill(token)\n\t\t},\n\n\t\t/**\n\t\t * Refetch a scoped picker's options when the value it is scoped BY\n\t\t * changes.\n\t\t *\n\t\t * `fetchReferenceOptions` resolves `x-relation-filter` against the\n\t\t * live form data, and `initAsyncFields` calls it once, at open. On a\n\t\t * CREATE the form is empty at that moment, so `@object.caseType`\n\t\t * resolves to nothing, the filter entry is dropped (\"unfiltered beats\n\t\t * an empty picker\") and the fetch returns the first hundred rows of\n\t\t * the referenced schema. Nothing ever fetched again, so choosing a\n\t\t * case type left the status picker offering every case type's\n\t\t * statuses, four of them named \"Received\" with nothing to tell them\n\t\t * apart. Reported against dossiq's New case form; the same shape sits\n\t\t * on four more of its shipped properties.\n\t\t *\n\t\t * NOT immediate. The open-time load is `initAsyncFields`'s job, and\n\t\t * firing here as well would send two identical requests on every open.\n\t\t *\n\t\t * The new signature is not read: which fields to refetch comes from\n\t\t * `relationFilterDecls`, and the values themselves are resolved from\n\t\t * `formData` by `fetchReferenceOptions`.\n\t\t */\n\t\tcodedContextToken() {\n\t\t\t// Ask again for the options that follow another field. The chosen\n\t\t\t// value is kept and marked, never cleared behind the user's back.\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field.codeList && field.codeList.contextProperty && this.asyncState[field.key]) {\n\t\t\t\t\tthis.loadAsyncOptions(field, '')\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\trelationFilterToken() {\n\t\t\tconst stale = new Set(this.relationFilterDecls.map(({ key }) => key))\n\t\t\tif (stale.size === 0) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// The TRANSFORMED fields, the same list initAsyncFields iterates:\n\t\t\t// a resolved cross-app semantic reference is a `$ref` picker by\n\t\t\t// then and scopes itself the same way.\n\t\t\tfor (const field of this.resolvedFields.map((f) => this.applySemanticResolution(f))) {\n\t\t\t\tif (!stale.has(field.key)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (!this.isAsyncEnum(field) && !this.isAsyncItemsEnum(field)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// OPTIONS ONLY, the value is left alone. Clearing a selection\n\t\t\t\t// that the new scope no longer offers looks tempting, and it\n\t\t\t\t// would fight `applyPrefill`: `x-openregister-prefill` reacts\n\t\t\t\t// to the same change, runs from a watcher declared above this\n\t\t\t\t// one, and its whole job is to WRITE the field this one would\n\t\t\t\t// then blank. Where a prefill exists it replaces the stale\n\t\t\t\t// value already; where none does, a value the picker cannot\n\t\t\t\t// show is the pre-existing behaviour and not what was reported.\n\t\t\t\tthis.loadAsyncOptions(field, '')\n\t\t\t}\n\t\t},\n\n\t\tvisibleFields(newFields, oldFields) {\n\t\t\tif (!Array.isArray(oldFields)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst newKeys = new Set(newFields.map((f) => f.key))\n\t\t\tfor (const oldField of oldFields) {\n\t\t\t\tif (!newKeys.has(oldField.key) && Object.hasOwn(this.formData, oldField.key)) {\n\t\t\t\t\tdelete this.formData[oldField.key]\n\t\t\t\t\tif (Object.hasOwn(this.errors, oldField.key)) {\n\t\t\t\t\t\tdelete this.errors[oldField.key]\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.hasOwn(this.jsonErrors, oldField.key)) {\n\t\t\t\t\t\tdelete this.jsonErrors[oldField.key]\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.hasOwn(this.jsonDrafts, oldField.key)) {\n\t\t\t\t\t\tdelete this.jsonDrafts[oldField.key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// 🔴 OFFERED, NEVER APPLIED. The values are held and the user decides.\n\t\t// Silently refilling a form is how somebody submits last week's answers\n\t\t// without noticing they were there, and a form that fills itself is\n\t\t// indistinguishable from one the server prefilled.\n\t\tif (this.formDraftKey) {\n\t\t\tthis.draftOffer = readDraft(this.formDraftKey, {\n\t\t\t\tobjectUpdated: (this.item?.updated || this.item?.['@self']?.updated || ''),\n\t\t\t})\n\t\t}\n\n\t\t// Non-reactive cache of built enum option lists, keyed by field key.\n\t\t// Kept off `data` so Vue doesn't make the option objects reactive —\n\t\t// stable identity is what lets NcSelect recognise the selected option.\n\t\tthis._enumOptionCache = {}\n\t\t// ADR-048: resolve any cross-app semantic-reference URIs once, up\n\t\t// front. Async — fields render disabled (loading) until each URI\n\t\t// resolves, then re-render as a picker (resolved) or disabled+tooltip\n\t\t// (unresolved).\n\t\tthis.resolveSemanticReferences()\n\t},\n\n\tbeforeUnmount() {\n\t\tfor (const state of Object.values(this.asyncState)) {\n\t\t\tif (state.searchTimeout) {\n\t\t\t\tclearTimeout(state.searchTimeout)\n\t\t\t}\n\t\t}\n\t\tif (this.closeTimeout) {\n\t\t\tclearTimeout(this.closeTimeout)\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Resolve a field's reference integration widget, if any.\n\t\t * Returns the integration's single-entity widget component\n\t\t * (AD-19 fallback to its main `widget`) when the field\n\t\t * declares a `referenceType` that maps to a registered\n\t\t * integration; null otherwise.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object|null} Vue component, or null.\n\t\t */\n\t\tresolveReferenceWidget(field) {\n\t\t\tif (!field || typeof field.referenceType !== 'string' || field.referenceType === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (typeof this.getRegistryIntegration === 'function' && this.getRegistryIntegration(field.referenceType) === null) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.resolveRegistryWidget(field.referenceType, 'single-entity')\n\t\t},\n\n\t\t/**\n\t\t * Props passed to a reference integration widget: the current\n\t\t * value, the rendering surface, and the object context (from\n\t\t * the `referenceContext` prop).\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} Props object for the widget component.\n\t\t */\n\t\treferenceWidgetProps(field) {\n\t\t\treturn {\n\t\t\t\tsurface: 'single-entity',\n\t\t\t\tvalue: this.formData[field.key],\n\t\t\t\tfield,\n\t\t\t\t...(this.referenceContext || {}),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve every distinct cross-app semantic-reference URI (ADR-048)\n\t\t * declared by the schema's fields against OpenRegister's discovery\n\t\t * endpoint. Fires once per URI (deduped via `semanticResolutions`),\n\t\t * kicked off from `created()`. Each entry moves 'loading' → 'done';\n\t\t * while loading, the field renders disabled. Never throws.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tresolveSemanticReferences() {\n\t\t\tconst uris = new Set()\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field && typeof field.referenceSemanticType === 'string' && field.referenceSemanticType !== '') {\n\t\t\t\t\turis.add(field.referenceSemanticType)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const uri of uris) {\n\t\t\t\tif (this.semanticResolutions[uri]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.semanticResolutions[uri] = { status: 'loading', resolved: false }\n\t\t\t\tthis.resolveSemanticReference(uri).then((result) => {\n\t\t\t\t\tthis.semanticResolutions[uri] = { status: 'done', ...result }\n\t\t\t\t\t// Re-init async fields so a newly-resolved reference picker\n\t\t\t\t\t// starts fetching its options, and resolve any edit-mode\n\t\t\t\t\t// label for a value already stored on the field.\n\t\t\t\t\tif (result.resolved) {\n\t\t\t\t\t\tthis.initAsyncFields()\n\t\t\t\t\t\tthis.resolveInitialReferenceLabels()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Call the OpenRegister discovery endpoint for one semantic-type URI.\n\t\t * Degrades to `{ resolved: false }` on any error (including a 404 when\n\t\t * the route isn't present yet) — never crashes the form.\n\t\t *\n\t\t * @param {string} uri The canonical semantic-type URI.\n\t\t * @return {Promise<{resolved: boolean, registerSlug: string|null, schemaSlug: string|null, appId: string|null}>}\n\t\t */\n\t\tasync resolveSemanticReference(uri) {\n\t\t\tconst empty = { resolved: false, registerSlug: null, schemaSlug: null, appId: null }\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl(SEMANTIC_RESOLVE_ENDPOINT)\n\t\t\t\tconst res = await axios.get(url, { params: { uri } })\n\t\t\t\tconst data = (res && res.data) || {}\n\t\t\t\tif (data.resolved === true && data.registerSlug && data.schemaSlug) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tresolved: true,\n\t\t\t\t\t\tregisterSlug: String(data.registerSlug),\n\t\t\t\t\t\tschemaSlug: String(data.schemaSlug),\n\t\t\t\t\t\tappId: data.appId !== null && data.appId !== undefined ? String(data.appId) : null,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn empty\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: semantic reference resolve failed for \"${uri}\":`, err)\n\t\t\t\treturn empty\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a semantic-reference field's URI has resolved to an\n\t\t * installed provider schema. False while loading or when nothing\n\t\t * implements the URI.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {boolean}\n\t\t */\n\t\tisSemanticResolved(field) {\n\t\t\tif (!field || !field.referenceSemanticType) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst entry = this.semanticResolutions[field.referenceSemanticType]\n\t\t\treturn !!(entry && entry.status === 'done' && entry.resolved)\n\t\t},\n\n\t\t/**\n\t\t * Whether a semantic-reference field's URI is still being resolved.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {boolean}\n\t\t */\n\t\tisSemanticLoading(field) {\n\t\t\tif (!field || !field.referenceSemanticType) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst entry = this.semanticResolutions[field.referenceSemanticType]\n\t\t\treturn !entry || entry.status === 'loading'\n\t\t},\n\n\t\t/**\n\t\t * Tooltip / helper copy for an unresolved semantic-reference field:\n\t\t * \"Install {App} to pick a {Type}.\" `Type` is\n\t\t * derived from the URI's last path segment; the app label from\n\t\t * `referenceSemanticApp`, or a shorter sentence naming only the type\n\t\t * when the property names no provider.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {string}\n\t\t */\n\t\tsemanticUnavailableText(field) {\n\t\t\tconst uri = (field && field.referenceSemanticType) || ''\n\t\t\tconst segment = uri.split(/[/#]/).filter(Boolean).pop() || uri\n\t\t\tconst typeLabel = segment || t('nextcloud-vue', 'this reference')\n\t\t\tconst appLabel = (field && field.referenceSemanticApp) || ''\n\t\t\t// TWO SENTENCES, NOT ONE WITH A FALLBACK NOUN. The single template\n\t\t\t// read \"The {appLabel} app that provides {typeLabel} is not\n\t\t\t// installed.\" and the no-app case substituted the literal\n\t\t\t// \"supporting app\", so a property that names no provider rendered\n\t\t\t// \"The supporting app app that provides Requester is not\n\t\t\t// installed.\" A filler noun that has to fit a slot built for a\n\t\t\t// proper name is how a doubled word ships. Where a provider IS\n\t\t\t// named the string can say what to do about it; where none is,\n\t\t\t// naming the missing type is all that is true.\n\t\t\tif (appLabel !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Install {appLabel} to pick a {typeLabel}.', { appLabel, typeLabel })\n\t\t\t}\n\n\t\t\treturn t('nextcloud-vue', 'No installed app provides {typeLabel}.', { typeLabel })\n\t\t},\n\n\t\t/**\n\t\t * Transform a resolved cross-app semantic-reference field (ADR-048)\n\t\t * into a normal `$ref` reference field so it flows through the\n\t\t * existing searchable-object-picker machinery, but pointed at the\n\t\t * PROVIDER'S register (cross-app — the provider lives in another\n\t\t * app's register). Unresolved/loading fields and non-semantic fields\n\t\t * are returned unchanged.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} The (possibly transformed) field.\n\t\t */\n\t\tapplySemanticResolution(field) {\n\t\t\tif (!field || !field.referenceSemanticType) {\n\t\t\t\treturn field\n\t\t\t}\n\t\t\tconst entry = this.semanticResolutions[field.referenceSemanticType]\n\t\t\tif (!entry || entry.status !== 'done' || !entry.resolved) {\n\t\t\t\treturn field\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...field,\n\t\t\t\twidget: 'select',\n\t\t\t\t// Carry the provider register on the reference so the fetch\n\t\t\t\t// targets registerSlug (cross-app), not the form's own register.\n\t\t\t\treference: { schema: entry.schemaSlug, multiple: false, register: entry.registerSlug },\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The id of a record, wherever it carries one. An OpenRegister\n\t\t * object holds its id in `@self`, not only at the top level.\n\t\t *\n\t\t * @param {object|null} record The record to read.\n\t\t * @return {string} The id, or '' when the record has none.\n\t\t */\n\t\trecordId(record) {\n\t\t\tif (!record || typeof record !== 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn String(record.id ?? record['@self']?.id ?? record.uuid ?? '')\n\t\t},\n\n\t\t/**\n\t\t * Whether two `item` values are the same record, for the purpose of\n\t\t * carrying pending edits across a re-seed.\n\t\t *\n\t\t * A record ARRIVING (null/id-less -> record) is the same record as\n\t\t * far as the user is concerned: they are filling in the form that is\n\t\t * already on screen. Two different ids are not.\n\t\t *\n\t\t * @param {object|null} oldItem The record previously bound.\n\t\t * @param {object|null} newItem The record now bound.\n\t\t * @return {boolean} True when edits may be carried over.\n\t\t */\n\t\tisSameRecord(oldItem, newItem) {\n\t\t\tconst oldId = this.recordId(oldItem)\n\t\t\tif (oldId === '') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn oldId === this.recordId(newItem)\n\t\t},\n\n\t\t/**\n\t\t * Snapshot the fields the user has edited by hand, so a record that\n\t\t * arrives later can be merged UNDER them rather than over them.\n\t\t *\n\t\t * @return {object|null} The pending edits, or null when there are none.\n\t\t */\n\t\tpendingUserEdits() {\n\t\t\tconst keys = Object.keys(this.touchedFields || {})\n\t\t\tif (keys.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst edits = {}\n\t\t\tfor (const key of keys) {\n\t\t\t\tif (Object.hasOwn(this.formData, key)) {\n\t\t\t\t\tedits[key] = this.formData[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Object.keys(edits).length > 0 ? edits : null\n\t\t},\n\n\t\t/**\n\t\t * Re-apply edits captured by `pendingUserEdits()` after a re-seed,\n\t\t * restoring their touched state with them.\n\t\t *\n\t\t * @param {object|null} edits The snapshot to re-apply.\n\t\t * @return {void}\n\t\t */\n\t\trestoreUserEdits(edits) {\n\t\t\tif (!edits) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor (const key of Object.keys(edits)) {\n\t\t\t\tthis.formData[key] = edits[key]\n\t\t\t\tthis.touchedFields[key] = true\n\t\t\t}\n\t\t},\n\n\t\tinitFormData(item) {\n\t\t\tif (item) {\n\t\t\t\t// Edit mode: clone item data, then normalise persisted\n\t\t\t\t// date/datetime values so an untouched field re-submits a\n\t\t\t\t// schema-valid value (the backend stores date-times\n\t\t\t\t// space-separated without a timezone, which would otherwise\n\t\t\t\t// fail the schema's `date-time` format on save).\n\t\t\t\tthis.formData = JSON.parse(JSON.stringify(item))\n\t\t\t\tthis.normalizePersistedDates()\n\t\t\t} else {\n\t\t\t\t// Create mode: initialize with field defaults\n\t\t\t\tconst data = {}\n\t\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\t\tconst tokenDefault = field.defaultToken ? resolveDefaultToken(field.defaultToken, field) : null\n\t\t\t\t\tif (field.default !== null && field.default !== undefined) {\n\t\t\t\t\t\tconst resolved = resolveFilterValue(field.default, { object: this.initialData || {}, me: this.meProfile })\n\t\t\t\t\t\t// A token that cannot resolve yet (the e-mail, until the profile loads) is not a value.\n\t\t\t\t\t\tdata[field.key] = typeof resolved === 'string' && resolved === field.default && resolved.startsWith('@') ? null : resolved\n\t\t\t\t\t} else if (tokenDefault !== null) {\n\t\t\t\t\t\t// `x-default: current-language` / `current-timezone`:\n\t\t\t\t\t\t// a NEW object starts with the user's own value.\n\t\t\t\t\t\tdata[field.key] = tokenDefault\n\t\t\t\t\t} else if (field.widget === 'checkbox') {\n\t\t\t\t\t\tdata[field.key] = false\n\t\t\t\t\t} else if (field.widget === 'tags' || field.widget === 'multiselect' || field.widget === 'user-multiselect' || field.widget === 'group-multiselect') {\n\t\t\t\t\t\tdata[field.key] = []\n\t\t\t\t\t} else if (field.widget === 'code') {\n\t\t\t\t\t\tdata[field.key] = ''\n\t\t\t\t\t} else if (field.widget === 'file' && field.file && field.file.multiple) {\n\t\t\t\t\t\tdata[field.key] = []\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdata[field.key] = null\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Seed create-mode values (parent pre-link from a detail page).\n\t\t\t\tfor (const key of Object.keys(this.initialData || {})) {\n\t\t\t\t\tif (this.initialData[key] !== undefined) {\n\t\t\t\t\t\tdata[key] = this.initialData[key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.formData = data\n\t\t\t}\n\t\t\t// Multi-tenancy auto-fill (multi-tenancy-context REQ-MT-4).\n\t\t\t// When the schema declares an `organisation` field and the\n\t\t\t// active form data does NOT already carry a value for it,\n\t\t\t// stamp the active organisation UUID from the shared\n\t\t\t// tenant context. Explicit values in `item` win — they are\n\t\t\t// already in `formData` at this point so the guard below\n\t\t\t// is correct on both create and edit paths.\n\t\t\tthis._autofillTenant()\n\t\t\tthis.errors = {}\n\t\t\tthis.formError = null\n\t\t\tthis.jsonDrafts = {}\n\t\t\tthis.jsonErrors = {}\n\t\t\tthis.touchedFields = {}\n\t\t\tthis.assignedFrom = {}\n\t\t\tthis.referenceLabels = {}\n\t\t\tthis.initAsyncFields()\n\t\t\tthis.resolveInitialReferenceLabels()\n\t\t\t// A new record opens with what the rules and defaults already know.\n\t\t\tif (!this.item) {\n\t\t\t\tthis.runAssignments(null)\n\t\t\t\tthis.loadProfileDefaults()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fill in `@me.displayName` / `@me.email` defaults the sync resolver could\n\t\t * not (the e-mail is on the profile). Asked for once, and only when a default needs it.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadProfileDefaults() {\n\t\t\tconst needs = this.resolvedFields.filter((f) => typeof f.default === 'string' && /^@me\\.(email|displayName)$/.test(f.default))\n\t\t\tif (needs.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.meProfile = await loadCurrentUserProfile()\n\t\t\tfor (const field of needs) {\n\t\t\t\tconst current = this.formData[field.key]\n\t\t\t\tconst value = resolveFilterValue(field.default, { me: this.meProfile })\n\t\t\t\tif ((current === null || current === undefined || current === '') && !this.touchedFields[field.key] && value !== field.default) {\n\t\t\t\t\tthis.formData[field.key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * For each reference field that already holds a UUID (edit mode),\n\t\t * resolve its label so the select shows the current selection's human\n\t\t * name. Runs after the options load; `resolveReferenceLabel` is a no-op\n\t\t * once the label is cached (so it doesn't re-fetch what the options\n\t\t * load already populated).\n\t\t */\n\t\tresolveInitialReferenceLabels() {\n\t\t\tfor (const field of this.resolvedFields.map((f) => this.applySemanticResolution(f))) {\n\t\t\t\tif (this.isReferenceField(field)) {\n\t\t\t\t\tconst uuid = this.formData[field.key]\n\t\t\t\t\tif (uuid) {\n\t\t\t\t\t\tthis.resolveReferenceLabel(field, uuid)\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isReferenceArrayField(field)) {\n\t\t\t\t\tconst uuids = this.formData[field.key]\n\t\t\t\t\tif (Array.isArray(uuids)) {\n\t\t\t\t\t\tfor (const uuid of uuids) {\n\t\t\t\t\t\t\tthis.resolveReferenceLabel(field, uuid)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isUserField(field)) {\n\t\t\t\t\tconst uid = this.formData[field.key]\n\t\t\t\t\tif (uid) {\n\t\t\t\t\t\tthis.resolveUserLabel(uid)\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isUserArrayField(field)) {\n\t\t\t\t\tconst uids = this.formData[field.key]\n\t\t\t\t\tif (Array.isArray(uids)) {\n\t\t\t\t\t\tfor (const uid of uids) {\n\t\t\t\t\t\t\tthis.resolveUserLabel(uid)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isGroupField(field) || this.isGroupArrayField(field)) {\n\t\t\t\t\tconst value = this.formData[field.key]\n\t\t\t\t\tfor (const gid of (Array.isArray(value) ? value : [value])) {\n\t\t\t\t\t\tif (gid) {\n\t\t\t\t\t\t\tthis.resolveGroupLabel(gid)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Request the options of a concept-scheme field from OpenRegister and\n\t\t * cache their labels. A failed or empty answer flips the field to the\n\t\t * text fallback; a held value the options no longer offer is resolved\n\t\t * through the concept route and marked.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @return {Promise<Array<{id: string, label: string}>>} The options in OpenRegister's order.\n\t\t */\n\t\tasync fetchCodedOptions(field) {\n\t\t\tconst code = field.codeList\n\t\t\tconst params = { schema: String((this.schema && (this.schema.id ?? this.schema.slug)) ?? ''), property: code.property, language: getLanguage() }\n\t\t\tif (code.contextProperty) {\n\t\t\t\tconst ctx = this.formData[code.contextProperty]\n\t\t\t\tif (ctx !== undefined && ctx !== null && ctx !== '') {\n\t\t\t\t\tparams.context = String(ctx)\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet options = []\n\t\t\ttry {\n\t\t\t\tconst response = await axios.get(generateUrl('/apps/openregister/api/vocabulary/options'), { params })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\tif (data.scheme) {\n\t\t\t\t\tthis.codedSchemes = { ...this.codedSchemes, [field.key]: data.scheme }\n\t\t\t\t}\n\t\t\t\toptions = (Array.isArray(data.results) ? data.results : [])\n\t\t\t\t\t.filter((o) => o && o.value !== undefined && o.value !== null)\n\t\t\t\t\t.map((o) => ({ id: String(o.value), label: o.label || o.notation || String(o.value) }))\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: vocabulary options failed for field \"${field.key}\":`, err)\n\t\t\t}\n\t\t\tthis.codedFailed = { ...this.codedFailed, [field.key]: options.length === 0 }\n\t\t\tconst labels = {}\n\t\t\tfor (const o of options) {\n\t\t\t\tlabels[o.id] = o.label\n\t\t\t}\n\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\tconst offered = new Set(options.map((o) => o.id))\n\t\t\tconst held = this.formData[field.key]\n\t\t\tfor (const value of (Array.isArray(held) ? held : [held])) {\n\t\t\t\tif (value !== undefined && value !== null && value !== '' && !offered.has(String(value))) {\n\t\t\t\t\tthis.resolveRetiredCode(field, String(value))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn options\n\t\t},\n\n\t\t/**\n\t\t * Show a held value the scheme no longer offers by its label, marked\n\t\t * \"no longer offered\", resolved through OpenRegister's concept route.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @param {string} value The stored value (uri, or notation per `store`).\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveRetiredCode(field, value) {\n\t\t\tconst marker = t('nextcloud-vue', 'no longer offered')\n\t\t\tlet label = value\n\t\t\ttry {\n\t\t\t\tconst url = field.codeList.store === 'notation'\n\t\t\t\t\t? generateUrl('/apps/openregister/api/vocabulary/concept/notation')\n\t\t\t\t\t: generateUrl('/apps/openregister/api/vocabulary/concept')\n\t\t\t\tconst params = field.codeList.store === 'notation'\n\t\t\t\t\t? { scheme: this.codedSchemes[field.key] || '', notation: value }\n\t\t\t\t\t: { uri: value }\n\t\t\t\tconst response = await axios.get(url, { params, headers: { 'Accept-Language': getLanguage() } })\n\t\t\t\tconst d = response && response.data ? response.data : {}\n\t\t\t\tconst concept = d.concept || d\n\t\t\t\tlabel = concept.label || concept.prefLabel || concept.notation || value\n\t\t\t} catch {\n\t\t\t\t// Unresolvable: the raw value is the best label left.\n\t\t\t}\n\t\t\tthis.referenceLabels = { ...this.referenceLabels, [value]: `${label} (${marker})` }\n\t\t},\n\n\t\t/**\n\t\t * The text shown in the fallback input of a coded field.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @return {string} The held value as text; several values are comma-separated.\n\t\t */\n\t\tcodedFallbackText(field) {\n\t\t\tconst v = this.formData[field.key]\n\t\t\tif (Array.isArray(v)) {\n\t\t\t\treturn v.join(', ')\n\t\t\t}\n\t\t\treturn v === undefined || v === null ? '' : String(v)\n\t\t},\n\n\t\t/**\n\t\t * Write the fallback input back: a string, or an array for several choices.\n\t\t *\n\t\t * @param {object} field The coded field.\n\t\t * @param {string} text The typed text.\n\t\t */\n\t\tonCodedFallbackInput(field, text) {\n\t\t\tif (field.codeList.multiple) {\n\t\t\t\tthis.updateField(field.key, text.split(',').map((x) => x.trim()).filter(Boolean))\n\t\t\t} else {\n\t\t\t\tthis.updateField(field.key, text)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve a stored group id to its display name and cache it in\n\t\t * `referenceLabels`. No-op once cached; falls back to the gid.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {string} gid The stored group id.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveGroupLabel(gid) {\n\t\t\tif (!gid || this.referenceLabels[gid]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst option = await resolveNextcloudGroup(gid)\n\t\t\tif (option && option.id) {\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [option.id]: option.label || String(option.id) }\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve a single user UID to its display name (edit mode) and cache it\n\t\t * in `referenceLabels` so the select shows the name instead of the UID.\n\t\t * No-op once the label is cached. Fails soft — `resolveNextcloudUser`\n\t\t * falls back to the UID itself when the name can't be looked up.\n\t\t *\n\t\t * @param {string} uid The stored user UID.\n\t\t */\n\t\tasync resolveUserLabel(uid) {\n\t\t\tif (!uid || this.referenceLabels[uid]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst option = await resolveNextcloudUser(uid)\n\t\t\tif (option && option.id) {\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [option.id]: option.label || String(option.id) }\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Auto-fill the `organisation` field with the active tenant UUID\n\t\t * when the schema declares such a field and no value is already\n\t\t * set (explicit `item` data wins).\n\t\t *\n\t\t * Spec: multi-tenancy-context REQ-MT-4.\n\t\t */\n\t\t_autofillTenant() {\n\t\t\tconst ctx = this._cnTenantContext\n\t\t\tif (!ctx) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst uuid = ctx.activeOrganisationUuid && ctx.activeOrganisationUuid.value\n\t\t\tif (!uuid) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst hasOrgField = this.resolvedFields.some((f) => f.key === 'organisation')\n\t\t\tif (!hasOrgField) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst current = this.formData.organisation\n\t\t\tif (current !== null && current !== undefined && current !== '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.formData.organisation = uuid\n\t\t},\n\n\t\t/**\n\t\t * Whether a form value is empty for tenant-fill purposes.\n\t\t *\n\t\t * @param {unknown} value The value.\n\t\t * @return {boolean} True for null, undefined and ''.\n\t\t */\n\t\t_isEmptyTenantValue(value) {\n\t\t\treturn value === null || value === undefined || value === ''\n\t\t},\n\n\t\t/**\n\t\t * The tenant a hidden tenant property (`tenantKeys`) takes when the\n\t\t * form holds no value for it: the active tenant from the tenant\n\t\t * context the app provides, and nothing else.\n\t\t *\n\t\t * There is deliberately no fallback to OpenRegister's active\n\t\t * organisation. An app's tenant is its own notion (learniq resolves\n\t\t * it from the user's `tenant_id` preference, else the instance id),\n\t\t * and stamping another value would make new records invisible next\n\t\t * to the ones the app wrote itself.\n\t\t *\n\t\t * @return {string|null} The tenant UUID, or null when the app provided none.\n\t\t */\n\t\t_platformTenant() {\n\t\t\tconst ctx = this._cnTenantContext\n\t\t\tconst fromContext = ctx && ctx.activeOrganisationUuid && ctx.activeOrganisationUuid.value\n\t\t\treturn fromContext || null\n\t\t},\n\n\t\t/**\n\t\t * Evaluate a field's conditional-visibility predicate (#327).\n\t\t *\n\t\t * A field is visible when it has no `condition`/`visibleWhen`, or when\n\t\t * its predicate evaluates true against the current `formData`. The\n\t\t * predicate references another field via `field` and one of the\n\t\t * supported operators: `equals`, `notEquals`, `in`, `notIn`,\n\t\t * `truthy`, `falsy`. An unrecognised predicate keeps the field visible\n\t\t * and logs a warning (fail-open — better to show a field than hide it\n\t\t * by accident).\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {boolean} Whether the field should be shown.\n\t\t */\n\t\tfieldVisible(field) {\n\t\t\tconst condition = field.condition || field.visibleWhen\n\t\t\tif (!condition || typeof condition !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tconst target = this.formData[condition.field]\n\n\t\t\tif (Object.hasOwn(condition, 'equals')) {\n\t\t\t\treturn target === condition.equals\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'notEquals')) {\n\t\t\t\treturn target !== condition.notEquals\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'in')) {\n\t\t\t\treturn Array.isArray(condition.in) && condition.in.includes(target)\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'notIn')) {\n\t\t\t\treturn !(Array.isArray(condition.notIn) && condition.notIn.includes(target))\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'truthy')) {\n\t\t\t\treturn condition.truthy ? !!target : !target\n\t\t\t}\n\t\t\tif (Object.hasOwn(condition, 'falsy')) {\n\t\t\t\treturn condition.falsy ? !target : !!target\n\t\t\t}\n\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(\n\t\t\t\t`[CnFormDialog] Field \"${field.key}\" has a condition with no recognised predicate; keeping it visible.`,\n\t\t\t\tcondition,\n\t\t\t)\n\t\t\treturn true\n\t\t},\n\n\t\tupdateField(key, value) {\n\t\t\tthis.formData[key] = value\n\t\t\tthis.touchedFields[key] = true\n\t\t\t// Clear errors when a field is edited\n\t\t\tif (this.errors[key]) {\n\t\t\t\tdelete this.errors[key]\n\t\t\t}\n\t\t\tthis.formError = null\n\t\t\t// A field change may flip the visibility of conditional fields.\n\t\t\t// Drop the form-data of any field that just became hidden so a\n\t\t\t// stale value isn't submitted (#327).\n\t\t\tthis.pruneHiddenFields()\n\t\t\t// A hand edit ends this field's own rules; other fields may follow it.\n\t\t\tdelete this.assignedFrom[key]\n\t\t\tthis.runAssignments([key])\n\t\t},\n\n\t\t/**\n\t\t * Fill in fields from other answers (`assign` rules on the field, for\n\t\t * example through `fieldOverrides`). `changed` names the answers that just\n\t\t * changed; `null` is the pass when the form opens, over empty fields. A\n\t\t * field the person edited by hand is left alone.\n\t\t *\n\t\t * @param {string[]|null} changed Keys that changed.\n\t\t */\n\t\trunAssignments(changed) {\n\t\t\tconst { values, from } = computeAssignments({\n\t\t\t\tfields: this.resolvedFields,\n\t\t\t\tanswers: this.formData,\n\t\t\t\tchanged,\n\t\t\t\tedited: Object.keys(this.touchedFields).filter((k) => this.touchedFields[k]),\n\t\t\t\tctx: { me: this.meProfile, object: this.formData },\n\t\t\t})\n\t\t\tfor (const key of Object.keys(values)) {\n\t\t\t\tthis.formData[key] = values[key]\n\t\t\t\tdelete this.errors[key]\n\t\t\t\tconst source = this.resolvedFields.find((f) => f.key === from[key])\n\t\t\t\tthis.assignedFrom[key] = source ? source.label : from[key]\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Remove `formData` (and any error) for fields that are currently\n\t\t * hidden by their conditional-visibility predicate (#327).\n\t\t */\n\t\tpruneHiddenFields() {\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (!this.fieldVisible(field) && Object.hasOwn(this.formData, field.key)) {\n\t\t\t\t\tdelete this.formData[field.key]\n\t\t\t\t\tif (this.errors[field.key]) {\n\t\t\t\t\t\tdelete this.errors[field.key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the string shown in the CnJsonViewer for a `json`-widget field.\n\t\t * Prefers the unparsed draft (so invalid typing isn't clobbered), falling\n\t\t * back to a pretty-printed stringification of the parsed value in formData.\n\t\t *\n\t\t * @param {object} field Field definition.\n\t\t * @return {string} JSON string for the editor.\n\t\t */\n\t\tjsonStringFor(field) {\n\t\t\tif (Object.hasOwn(this.jsonDrafts, field.key)) {\n\t\t\t\treturn this.jsonDrafts[field.key]\n\t\t\t}\n\t\t\tconst value = this.formData[field.key]\n\t\t\tif (value === null || value === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(value, null, 2)\n\t\t\t} catch {\n\t\t\t\treturn String(value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle input in a `json`-widget CnJsonViewer. Parses on the fly:\n\t\t * on success, the parsed value lands in formData and any previous error\n\t\t * is cleared; on failure, formData keeps its last-known-good value and\n\t\t * `jsonErrors[key]` is set, which surfaces inline and disables confirm.\n\t\t *\n\t\t * @param {object} field Field definition.\n\t\t * @param {string} newString Current editor content.\n\t\t */\n\t\tonJsonFieldInput(field, newString) {\n\t\t\tthis.jsonDrafts[field.key] = newString\n\t\t\tconst trimmed = (newString || '').trim()\n\t\t\tif (!trimmed) {\n\t\t\t\tthis.updateField(field.key, null)\n\t\t\t\tdelete this.jsonErrors[field.key]\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(trimmed)\n\t\t\t\tthis.updateField(field.key, parsed)\n\t\t\t\tdelete this.jsonErrors[field.key]\n\t\t\t} catch (e) {\n\t\t\t\tthis.jsonErrors[field.key] = t('nextcloud-vue', 'Invalid JSON: {msg}', { msg: e.message })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Parse a date/datetime field's stored string into a Date for\n\t\t * NcDateTimePickerNative (which operates in local time). Returns\n\t\t * null for empty/unparseable values.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Date|null}\n\t\t */\n\t\tdateValueFor(field) {\n\t\t\tconst raw = this.formData[field.key]\n\t\t\tif (!raw) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Accept both ISO ('2026-10-15T14:30:00Z') and OpenRegister's\n\t\t\t// space-separated persisted form ('2026-10-15 14:30:00'); the\n\t\t\t// optional trailing 'Z'/offset is ignored for the local-time picker.\n\t\t\tconst parts = String(raw).match(/^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2}))?/)\n\t\t\tif (parts) {\n\t\t\t\treturn new Date(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]), Number(parts[4] || 0), Number(parts[5] || 0))\n\t\t\t}\n\t\t\tconst fallback = new Date(raw)\n\t\t\treturn isNaN(fallback.getTime()) ? null : fallback\n\t\t},\n\n\t\t/**\n\t\t * Serialise a Date into the canonical string a date/datetime field\n\t\t * stores and submits: 'YYYY-MM-DD' for `date`, and a full RFC 3339\n\t\t * 'YYYY-MM-DDTHH:mm:ss±hh:mm' (local-offset, lossless) for `datetime`.\n\t\t *\n\t\t * The datetime form includes seconds and a timezone offset so it\n\t\t * satisfies the JSON-Schema `date-time` format (ajv-formats requires\n\t\t * an offset) — the backend would otherwise reject a bare\n\t\t * 'YYYY-MM-DDTHH:mm' value on save.\n\t\t *\n\t\t * @param {string} widget The field widget ('date' | 'datetime').\n\t\t * @param {Date} date A valid Date instance.\n\t\t * @return {string} The serialised value.\n\t\t */\n\t\tformatDateValue(widget, date) {\n\t\t\tconst yyyy = String(date.getFullYear()).padStart(4, '0')\n\t\t\tconst MM = String(date.getMonth() + 1).padStart(2, '0')\n\t\t\tconst dd = String(date.getDate()).padStart(2, '0')\n\t\t\tif (widget !== 'datetime') {\n\t\t\t\treturn `${yyyy}-${MM}-${dd}`\n\t\t\t}\n\t\t\tconst hh = String(date.getHours()).padStart(2, '0')\n\t\t\tconst mm = String(date.getMinutes()).padStart(2, '0')\n\t\t\tconst ss = String(date.getSeconds()).padStart(2, '0')\n\t\t\t// Local timezone offset as ±hh:mm (getTimezoneOffset is minutes\n\t\t\t// behind UTC, so the sign is inverted).\n\t\t\tconst offMin = -date.getTimezoneOffset()\n\t\t\tconst sign = offMin >= 0 ? '+' : '-'\n\t\t\tconst offAbs = Math.abs(offMin)\n\t\t\tconst offH = String(Math.floor(offAbs / 60)).padStart(2, '0')\n\t\t\tconst offM = String(offAbs % 60).padStart(2, '0')\n\t\t\treturn `${yyyy}-${MM}-${dd}T${hh}:${mm}:${ss}${sign}${offH}:${offM}`\n\t\t},\n\n\t\t/**\n\t\t * Convert the Date emitted by NcDateTimePickerNative back to the\n\t\t * stored string format (see `formatDateValue`). Stores null when cleared.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {Date|null} date The chosen date\n\t\t */\n\t\tonDateFieldInput(field, date) {\n\t\t\tif (!(date instanceof Date) || isNaN(date.getTime())) {\n\t\t\t\tthis.updateField(field.key, null)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.updateField(field.key, this.formatDateValue(field.widget, date))\n\t\t},\n\n\t\t/**\n\t\t * Rewrite any persisted date/datetime field value in formData into\n\t\t * the canonical, schema-valid string produced by `formatDateValue`.\n\t\t * Runs once on edit-open so a field the user never touches still\n\t\t * re-submits a value the schema's `date`/`date-time` format accepts.\n\t\t * Leaves empty/unparseable values untouched.\n\t\t */\n\t\tnormalizePersistedDates() {\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field.widget !== 'date' && field.widget !== 'datetime') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst raw = this.formData[field.key]\n\t\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// Leave a value that ALREADY satisfies the schema format alone.\n\t\t\t\t//\n\t\t\t\t// This pass exists for OpenRegister's persisted shape\n\t\t\t\t// ('2026-10-15 14:30:00' — space-separated, no zone), which fails\n\t\t\t\t// the `date-time` format on save. A value that arrives as valid\n\t\t\t\t// RFC 3339 does not need it, and rewriting one CORRUPTS it:\n\t\t\t\t// dateValueFor() reads the wall-clock digits and DISCARDS the\n\t\t\t\t// offset (right for the local-time picker), then formatDateValue()\n\t\t\t\t// stamps the LOCAL offset back on. In Europe/Amsterdam a stored\n\t\t\t\t// '2026-10-15T14:30:00Z' came back as '2026-10-15T14:30:00+02:00'\n\t\t\t\t// — the same digits, two hours earlier, silently, on an edit that\n\t\t\t\t// never touched the field. It is schema-valid either way, so\n\t\t\t\t// nothing downstream objects (nextcloud-vue#835).\n\t\t\t\tif (this.isCanonicalDateValue(field.widget, raw)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst date = this.dateValueFor(field)\n\t\t\t\tif (date instanceof Date && !isNaN(date.getTime())) {\n\t\t\t\t\tthis.formData[field.key] = this.formatDateValue(field.widget, date)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a stored value is already in the shape this field submits.\n\t\t *\n\t\t * `date` wants a bare `YYYY-MM-DD`; `datetime` wants a full RFC 3339\n\t\t * timestamp carrying a zone (`Z` or `±hh:mm`), which is what\n\t\t * `formatDateValue` emits and what ajv-formats requires. Anything else\n\t\t * — notably OpenRegister's space-separated persisted form — still gets\n\t\t * normalised.\n\t\t *\n\t\t * @param {string} widget The field widget ('date' | 'datetime').\n\t\t * @param {unknown} raw The stored value.\n\t\t * @return {boolean} True when the value needs no rewrite.\n\t\t */\n\t\tisCanonicalDateValue(widget, raw) {\n\t\t\tif (typeof raw !== 'string') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (widget === 'date') {\n\t\t\t\treturn /^\\d{4}-\\d{2}-\\d{2}$/.test(raw)\n\t\t\t}\n\t\t\treturn /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/.test(raw)\n\t\t},\n\n\t\t/**\n\t\t * Display label for one enum value. The stored value (`id`) always stays\n\t\t * the raw schema value — only what the user reads is translated, first\n\t\t * through the field's `enumLabels` map when it has an entry, then through\n\t\t * the host app's catalogue. A schema enum is a set of English source\n\t\t * codes, so a Dutch session otherwise reads `submitted` in a form whose\n\t\t * every other string is Dutch.\n\t\t *\n\t\t * @param {object} field The field descriptor.\n\t\t * @param {unknown} val The raw enum value.\n\t\t * @return {string} The label to render.\n\t\t */\n\t\tenumOptionLabel(field, val) {\n\t\t\tconst labels = field.enumLabels || {}\n\t\t\treturn this.cnTranslate(labels[val] || String(val))\n\t\t},\n\n\t\tgetEnumOptions(field) {\n\t\t\tif (!field.enum) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\t// Cache the built option list per field so the same option object\n\t\t\t// references are reused across renders. NcSelect/vue-select match\n\t\t\t// the selected model against options by identity — returning fresh\n\t\t\t// objects each call left the selection unrecognised (the headless\n\t\t\t// \"chip renders but value never commits\" defect).\n\t\t\tconst cached = this._enumOptionCache[field.key]\n\t\t\tif (cached && cached.enum === field.enum && cached.enumLabels === (field.enumLabels || null)) {\n\t\t\t\treturn cached.options\n\t\t\t}\n\t\t\tconst options = field.enum.map((val) => ({\n\t\t\t\tid: val,\n\t\t\t\tlabel: this.enumOptionLabel(field, val),\n\t\t\t}))\n\t\t\tthis._enumOptionCache[field.key] = { enum: field.enum, enumLabels: field.enumLabels || null, options }\n\t\t\treturn options\n\t\t},\n\n\t\tgetSelectedEnumOption(field) {\n\t\t\tconst val = this.formData[field.key]\n\t\t\tif (val === null || val === undefined) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Return the SAME option object reference from the field's option\n\t\t\t// list so NcSelect/vue-select recognises it as selected (it matches\n\t\t\t// the model against options by identity). Returning a fresh\n\t\t\t// `{ id, label }` each render left the model unrecognised, which —\n\t\t\t// under headless Chromium — could drop the committed value and keep\n\t\t\t// the submit button disabled. Fall back to a constructed option for\n\t\t\t// values not present in the list.\n\t\t\tconst options = this.getEnumOptions(field)\n\t\t\tconst match = options.find((o) => o.id === val)\n\t\t\tif (match) {\n\t\t\t\treturn match\n\t\t\t}\n\t\t\treturn { id: val, label: this.enumOptionLabel(field, val) }\n\t\t},\n\n\t\tonSelectChange(key, option) {\n\t\t\tthis.updateField(key, option ? option.id : null)\n\t\t},\n\n\t\tgetArrayEnumOptions(field) {\n\t\t\tif (!field.items || !field.items.enum) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn field.items.enum.map((val) => ({\n\t\t\t\tid: val,\n\t\t\t\tlabel: this.enumOptionLabel(field, val),\n\t\t\t}))\n\t\t},\n\n\t\tgetSelectedArrayOptions(field) {\n\t\t\tconst val = this.formData[field.key]\n\t\t\tif (!Array.isArray(val)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn val.map((v) => ({ id: v, label: this.enumOptionLabel(field, v) }))\n\t\t},\n\n\t\tonMultiSelectChange(key, options) {\n\t\t\tthis.updateField(key, (options || []).map((o) => o.id))\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a single-value OpenRegister object reference\n\t\t * (`$ref`) we can resolve — i.e. it carries a `reference` descriptor,\n\t\t * is not multi-value, and a `register` is available to fetch against.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisReferenceField(field) {\n\t\t\treturn !!(field && field.reference && !field.reference.multiple && this.referenceRegister(field))\n\t\t},\n\n\t\t/**\n\t\t * The register a reference field fetches against: the reference's own\n\t\t * `register` (cross-app semantic references, ADR-048) when present,\n\t\t * otherwise the form's `register` prop.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {string} The register slug, or '' when none is available.\n\t\t */\n\t\treferenceRegister(field) {\n\t\t\treturn (field && field.reference && field.reference.register) || this.register || ''\n\t\t},\n\n\t\t/**\n\t\t * Degrade a reference field whose reference can't be resolved (no\n\t\t * `register` available) to a plain text input so it remains editable\n\t\t * instead of rendering an empty, optionless dropdown. Non-reference\n\t\t * fields and resolvable references are returned unchanged.\n\t\t *\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} The (possibly downgraded) field.\n\t\t */\n\t\tdegradeUnresolvableReference(field) {\n\t\t\tif (field && field.reference && !this.referenceRegister(field)) {\n\t\t\t\treturn { ...field, widget: 'text', reference: null }\n\t\t\t}\n\t\t\treturn field\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a multi-value OpenRegister object reference\n\t\t * (`items.$ref`) we can resolve.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisReferenceArrayField(field) {\n\t\t\treturn !!(field && field.reference && field.reference.multiple && this.referenceRegister(field))\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a single-value Nextcloud user reference — a\n\t\t * property the schema marked with `referenceType: 'nextcloud-user'`\n\t\t * (or `format: 'user'`/`'username'`). Renders as a searchable dropdown\n\t\t * of real Nextcloud users (label = display name, value = UID). Needs no\n\t\t * `register` (users come from the core autocomplete OCS endpoint).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\t/**\n\t\t * The text a select option shows: its label, else a display name, a\n\t\t * name or the id. A plain string option (a tag) is its own text.\n\t\t *\n\t\t * @spec openspec/changes/audit-round-lib-fixes/specs/schema-utilities/spec.md\n\t\t * @param {object|string} option The option (slot props).\n\t\t * @return {string} The text.\n\t\t */\n\t\t/**\n\t\t * The mark a required field carries in the label: \" *\" in the Nextcloud\n\t\t * look, nothing in the board look (optional fields say \"(optional)\").\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The suffix.\n\t\t */\n\t\trequiredSuffix(field) {\n\t\t\treturn !this.isBoardLook && field.required ? ' *' : ''\n\t\t},\n\n\t\t/**\n\t\t * The label text handed to a control that draws or announces its own.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The label.\n\t\t */\n\t\tfieldLabelText(field) {\n\t\t\treturn field.label + this.requiredSuffix(field)\n\t\t},\n\n\t\t/**\n\t\t * Whether the board look draws this field's label above the control. The\n\t\t * switch and checkbox keep their inline label, and the widgets that draw\n\t\t * their own label inside the control (file, duration, child records, sub\n\t\t * objects, property source, a reference widget) keep it too.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {boolean} True when the label sits above the control.\n\t\t */\n\t\tshowsOuterLabel(field) {\n\t\t\tif (!this.isBoardLook || this.$slots['field-' + field.key]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (['switch', 'checkbox', 'file', 'duration', 'child-records', 'sub-objects', 'property-source'].includes(field.widget)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn !this.resolveReferenceWidget(field)\n\t\t},\n\n\t\t/**\n\t\t * The id of a text control the board label points at (`for`); unset in the\n\t\t * Nextcloud look so the control keeps its generated id.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string|undefined} The id.\n\t\t */\n\t\tfieldControlId(field) {\n\t\t\treturn this.showsOuterLabel(field) ? 'cn-form-' + field.key : undefined\n\t\t},\n\n\t\t/**\n\t\t * The id of the field's error element.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The id.\n\t\t */\n\t\terrorIdFor(field) {\n\t\t\treturn 'cn-form-' + field.key + '-error'\n\t\t},\n\n\t\t/**\n\t\t * The id of the field's hint (the helper line).\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string} The id.\n\t\t */\n\t\thelpId(field) {\n\t\t\treturn 'cn-form-' + field.key + '-help'\n\t\t},\n\n\t\t/**\n\t\t * The error the helper line shows: in the board look the error sits above\n\t\t * the control instead, so the helper keeps only the hint.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {string|undefined} The error, or '' in the board look.\n\t\t */\n\t\thelperError(field) {\n\t\t\treturn this.showsOuterLabel(field) ? '' : this.errors[field.key]\n\t\t},\n\n\t\t/**\n\t\t * The accessibility attributes of a text control. In both looks an invalid\n\t\t * control is `aria-invalid` and `aria-describedby` lists the error before\n\t\t * the hint; `aria-required` is set in the board look, where the asterisk is\n\t\t * gone.\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @return {object} The attributes to bind.\n\t\t */\n\t\tfieldAria(field) {\n\t\t\tconst error = this.errors[field.key]\n\t\t\tconst boardError = this.showsOuterLabel(field)\n\t\t\tconst hasHint = !!(field.description || field.descriptionLong || field.help)\n\t\t\tconst ids = []\n\t\t\tif (error) {\n\t\t\t\tids.push(boardError ? this.errorIdFor(field) : this.helpId(field))\n\t\t\t}\n\t\t\tif (hasHint && (boardError || !error)) {\n\t\t\t\tids.push(this.helpId(field))\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t'aria-invalid': error ? 'true' : undefined,\n\t\t\t\t'aria-describedby': ids.length > 0 ? ids.join(' ') : undefined,\n\t\t\t\t'aria-required': this.isBoardLook && field.required ? 'true' : undefined,\n\t\t\t}\n\t\t},\n\n\t\toptionText(option) {\n\t\t\tif (option === null || option === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof option !== 'object') {\n\t\t\t\treturn String(option)\n\t\t\t}\n\t\t\tconst text = [option.label, option.displayName, option.name, option.id]\n\t\t\t\t.find((v) => v !== undefined && v !== null && v !== '')\n\t\t\treturn text === undefined ? '' : String(text)\n\t\t},\n\n\t\tisUserField(field) {\n\t\t\treturn !!(field && field.userPicker && !field.userPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a multi-value Nextcloud user reference (an array\n\t\t * of users) → a searchable multi-select of real Nextcloud users.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisUserArrayField(field) {\n\t\t\treturn !!(field && field.userPicker && field.userPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is a single Nextcloud group (`format: 'nc-group'` or\n\t\t * `referenceType: 'nextcloud-group'`). Stores the group id.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisGroupField(field) {\n\t\t\treturn !!(field && field.groupPicker && !field.groupPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field is an array of Nextcloud groups.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisGroupArrayField(field) {\n\t\t\treturn !!(field && field.groupPicker && field.groupPicker.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Whether a single-value field stores an id that maps to a label\n\t\t * fetched elsewhere: an object reference, a user or a group.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisIdPickerField(field) {\n\t\t\treturn this.isReferenceField(field) || this.isUserField(field) || this.isGroupField(field) || this.isCodedField(field)\n\t\t},\n\n\t\t/**\n\t\t * A single choice bound to an OpenRegister concept scheme.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisCodedField(field) {\n\t\t\treturn !!(field.codeList && !field.codeList.multiple)\n\t\t},\n\n\t\t/**\n\t\t * Several choices bound to an OpenRegister concept scheme.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisCodedArrayField(field) {\n\t\t\treturn !!(field.codeList && field.codeList.multiple)\n\t\t},\n\n\t\t/**\n\t\t * The multi-value sibling of `isIdPickerField`.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisIdPickerArrayField(field) {\n\t\t\treturn this.isReferenceArrayField(field) || this.isUserArrayField(field) || this.isGroupArrayField(field) || this.isCodedArrayField(field)\n\t\t},\n\n\t\t/**\n\t\t * Turn a language or time zone field into a searchable select over\n\t\t * a fixed code list: labels in the user's language, the code stored.\n\t\t * Other fields pass through unchanged.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} field A resolved field descriptor.\n\t\t * @return {object} The field, as a `select` when it is a code picker.\n\t\t */\n\t\tapplyCodePicker(field) {\n\t\t\tif (!field || (field.widget !== 'language' && field.widget !== 'timezone')) {\n\t\t\t\treturn field\n\t\t\t}\n\t\t\tconst list = field.widget === 'language' ? languageOptions() : timezoneOptions()\n\t\t\treturn { ...field, widget: 'select', codePicker: field.widget, enum: list.codes, enumLabels: list.labels }\n\t\t},\n\n\t\t/**\n\t\t * Check if a field has an async enum (function instead of static array).\n\t\t * Object references (`$ref`) and Nextcloud user references are treated\n\t\t * as async so they reuse the async-select load/search/loading machinery.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisAsyncEnum(field) {\n\t\t\treturn typeof field.enum === 'function' || this.isIdPickerField(field)\n\t\t},\n\n\t\t/**\n\t\t * Check if an array field has an async items enum. Multi-value object\n\t\t * references (`items.$ref`) and multi-value user references are treated\n\t\t * as async multiselects.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisAsyncItemsEnum(field) {\n\t\t\treturn !!(field.items && typeof field.items.enum === 'function') || this.isIdPickerArrayField(field)\n\t\t},\n\n\t\t/**\n\t\t * Resolve a human-readable label for an OpenRegister object.\n\t\t *\n\t\t * Delegates to the shared `objectDisplayName`, which asks `@self.name`\n\t\t * FIRST — the display name OpenRegister derived from whichever property\n\t\t * the schema actually uses — and type-checks every candidate.\n\t\t *\n\t\t * This used to be its own chain, and it took the first TRUTHY key\n\t\t * starting at `obj.title`, `obj.name`. That is wrong for any schema\n\t\t * whose `name` is structured rather than a string: Haal Centraal naming\n\t\t * gives a person `name: { givenNames, namePrefix, surname }`, so the\n\t\t * label became that OBJECT and the picker rendered `[object Object]`\n\t\t * where the requester's name belonged. `@self.name` held \"Stephan\n\t\t * Janssen\" the whole time, four candidates further down a list that\n\t\t * never reached it because `obj.name` was truthy.\n\t\t *\n\t\t * @param {object} obj An OpenRegister object.\n\t\t * @return {string} The display label.\n\t\t */\n\t\tdisplayLabel(obj) {\n\t\t\tif (!obj || typeof obj !== 'object') {\n\t\t\t\treturn String(obj)\n\t\t\t}\n\t\t\treturn objectDisplayName(obj)\n\t\t},\n\n\t\t/**\n\t\t * The object field CnResourceSelect uses as the option label and writes\n\t\t * on inline create. Reads `field.reference.labelField` when the schema\n\t\t * declares one, else defaults to `name`.\n\t\t *\n\t\t * @param {object} field A resolved reference field descriptor.\n\t\t * @return {string} The label field name.\n\t\t */\n\t\treferenceLabelField(field) {\n\t\t\treturn (field && field.reference && field.reference.labelField) || 'name'\n\t\t},\n\n\t\t/**\n\t\t * Cache the label of an object just created inline via CnResourceSelect,\n\t\t * so any read-side that resolves the stored UUID shows its name. The\n\t\t * value itself is set by CnResourceSelect's `update:modelValue`.\n\t\t *\n\t\t * @param {object} field The reference field.\n\t\t * @param {object} obj The freshly-created object (carries id + label field).\n\t\t * @return {void}\n\t\t */\n\t\tonReferenceCreated(field, obj) {\n\t\t\tif (obj && obj.id) {\n\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [obj.id]: this.displayLabel(obj) }\n\t\t\t}\n\t\t\t// A freshly-created template carries its own fields — fill from it directly.\n\t\t\tthis.applyTemplateFill(field, obj)\n\t\t},\n\n\t\t/**\n\t\t * Handle selection of a create-capable (`CnResourceSelect`) reference:\n\t\t * store the chosen UUID, then run any `fillFrom` template copy.\n\t\t *\n\t\t * @param {object} field The reference field descriptor.\n\t\t * @param {string|null} value The chosen object's UUID (or null on clear).\n\t\t * @return {void}\n\t\t */\n\t\tonReferenceSelected(field, value) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tthis.updateField(field.key, value)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.updateField(field.key, value || null)\n\t\t\tif (value) {\n\t\t\t\tthis.applyTemplateFill(field, value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The value CnResourceSelect binds for a select-or-create reference:\n\t\t * an array of ids for a multi-value reference, else the id string.\n\t\t *\n\t\t * @param {object} field The reference field descriptor.\n\t\t * @return {string|string[]} The bound value.\n\t\t */\n\t\treferenceModelValue(field) {\n\t\t\tconst value = this.formData[field.key]\n\t\t\tif (field.reference && field.reference.multiple) {\n\t\t\t\treturn Array.isArray(value) ? value.map((v) => String(v)) : []\n\t\t\t}\n\t\t\treturn value !== null && value !== undefined ? String(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * \"Create\" on a select-or-create reference: open the referenced\n\t\t * schema's own form, prefilled with the typed term, on top of this one.\n\t\t *\n\t\t * Resolves with the created object once that form is saved, or with\n\t\t * null when it is closed without saving. CnResourceSelect then selects\n\t\t * the new object. When the referenced schema cannot be loaded, the\n\t\t * term alone is saved as before, so the field never dead-ends.\n\t\t *\n\t\t * The form is the one the schema's index page shows for its own Add\n\t\t * button: that page's `createOverride` saves it and its form settings\n\t\t * shape it, with the typed term in the name.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {object} field The reference field descriptor.\n\t\t * @param {string} term The typed search term.\n\t\t * @return {Promise<object|null>} The created object, or null.\n\t\t */\n\t\tasync openNestedCreate(field, term) {\n\t\t\tconst store = this.getObjectStore()\n\t\t\tconst register = this.referenceRegister(field)\n\t\t\tif (!store || !register || !field.reference) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst schemaRef = String(field.reference.schema)\n\t\t\tconst slug = store.createObjectTypeSlug(register, schemaRef)\n\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\tstore.registerObjectType(slug, schemaRef, register)\n\t\t\t}\n\t\t\tconst labelField = this.referenceLabelField(field)\n\t\t\tconst loadSchema = async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await store.fetchSchema(slug)\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The app may own how this schema is created (pipelinq's client and\n\t\t\t// contact need a contact-first endpoint; a plain save 400s). A\n\t\t\t// create override keeps this form and saves through the app's\n\t\t\t// handler; a create modal (only when no override exists) opens the\n\t\t\t// app's own dialog instead of the generic form.\n\t\t\t//\n\t\t\t// A stored schema usually names its reference by id (`$ref: 28`)\n\t\t\t// while the manifest page names it by slug (`schema: 'client'`), so\n\t\t\t// when the reference itself matches no page, the referenced schema\n\t\t\t// is loaded and its slug, id and uuid are tried too.\n\t\t\tlet schema\n\t\t\tlet appCreate = this.appCreateFor(register, [schemaRef])\n\t\t\tif (!appCreate.page) {\n\t\t\t\tschema = await loadSchema()\n\t\t\t\tif (schema) {\n\t\t\t\t\tappCreate = this.appCreateFor(register, [schemaRef, schema.slug, schema.id, schema.uuid])\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!appCreate.override && appCreate.modal) {\n\t\t\t\treturn this.openAppCreateModal(appCreate.modal, store, slug, labelField, term)\n\t\t\t}\n\t\t\tconst override = appCreate.override\n\t\t\tif (schema === undefined) {\n\t\t\t\tschema = await loadSchema()\n\t\t\t}\n\t\t\tif (!schema || !schema.properties) {\n\t\t\t\tconst payload = { [labelField]: term }\n\t\t\t\treturn override\n\t\t\t\t\t? override(payload, { register, schema: schemaRef, objectType: slug, effectiveSchema: null })\n\t\t\t\t\t: store.saveObject(slug, payload)\n\t\t\t}\n\t\t\tconst initialData = {}\n\t\t\tconst prefillKey = this.nestedPrefillKey(schema, labelField)\n\t\t\tif (term && prefillKey) {\n\t\t\t\tinitialData[prefillKey] = term\n\t\t\t}\n\t\t\tconst form = appCreate.form || this.nestedFormConfig(null)\n\t\t\treturn new Promise((resolve) => {\n\t\t\t\tthis.nestedCreate = { field, schema, register, slug, schemaRef, initialData, override, form, resolve }\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The property of the referenced schema the typed term belongs in: the\n\t\t * reference's label field when the schema has it, else its name, title\n\t\t * or label. Visibility is decided by the form (a page `fieldOverrides`\n\t\t * entry can make a read-only name editable), not here.\n\t\t *\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {object} schema The referenced schema.\n\t\t * @param {string} labelField The reference's label field.\n\t\t * @return {string|null} The property key, or null when none fits.\n\t\t */\n\t\tnestedPrefillKey(schema, labelField) {\n\t\t\tconst properties = (schema && schema.properties) || {}\n\t\t\treturn [labelField, 'name', 'title', 'label'].find((key) => properties[key]) || null\n\t\t},\n\n\t\t/**\n\t\t * The create-form settings of the index page that lists a schema, in\n\t\t * the shape the nested CnFormDialog binds: the same `excludeFields`,\n\t\t * `includeFields`, `fieldOverrides`, `formSize` and `formColumns`\n\t\t * CnIndexPage hands its own Add form, so creating from a picker shows\n\t\t * the form the page shows. Without a page, the form's defaults.\n\t\t *\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {object|null} config The page's `config`, or null.\n\t\t * @return {{excludeFields: string[], includeFields: (string[]|null), fieldOverrides: object, size: string, columns: number}} The form settings.\n\t\t */\n\t\tnestedFormConfig(config) {\n\t\t\tconst c = config || {}\n\t\t\tconst isMap = (v) => v !== null && typeof v === 'object' && !Array.isArray(v)\n\t\t\treturn {\n\t\t\t\texcludeFields: Array.isArray(c.excludeFields) ? c.excludeFields : [],\n\t\t\t\tincludeFields: Array.isArray(c.includeFields) ? c.includeFields : null,\n\t\t\t\tfieldOverrides: isMap(c.fieldOverrides) ? c.fieldOverrides : {},\n\t\t\t\tsize: typeof c.formSize === 'string' && c.formSize !== '' ? c.formSize : 'normal',\n\t\t\t\tcolumns: c.formColumns === 2 ? 2 : 1,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * How the app creates objects of a schema, read off the manifest page\n\t\t * that lists them: the index page whose `config.schema` (and, when it\n\t\t * names one, `config.register`) matches. Its `config.createOverride` is\n\t\t * resolved to the registered handler; its `config.createModal` is the\n\t\t * registry key of a `kind: 'modal'` dialog; its form settings\n\t\t * (`fieldOverrides` and friends) shape the nested form.\n\t\t *\n\t\t * The schema is matched on any of the given references, so a caller\n\t\t * holding the referenced schema can pass its slug, id and uuid: the\n\t\t * manifest names a schema by slug, a stored `$ref` often by id.\n\t\t *\n\t\t * @spec openspec/changes/nested-create-uses-page-config/specs/schema-utilities/spec.md\n\t\t * @param {string} register The reference's register.\n\t\t * @param {string|Array<string|number>} schemaRefs The schema's slug, id or uuid, one or several.\n\t\t * @return {{page: (object|null), override: (((formData: object, ctx: object) => Promise<object>)|null), modal: (string|null), form: (object|null)}} What the app declared.\n\t\t */\n\t\tappCreateFor(register, schemaRefs) {\n\t\t\tconst none = { page: null, override: null, modal: null, form: null }\n\t\t\tconst pages = this.cnManifest && Array.isArray(this.cnManifest.pages) ? this.cnManifest.pages : []\n\t\t\tconst wanted = (Array.isArray(schemaRefs) ? schemaRefs : [schemaRefs])\n\t\t\t\t.filter((ref) => ref !== undefined && ref !== null && ref !== '')\n\t\t\t\t.map((ref) => String(ref).toLowerCase())\n\t\t\tconst page = pages.find((p) => {\n\t\t\t\tconst config = p && p.config\n\t\t\t\tif (!config || config.schema === undefined || config.schema === null) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif (!wanted.includes(String(config.schema).toLowerCase())) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn !config.register || String(config.register) === String(register)\n\t\t\t})\n\t\t\tif (!page) {\n\t\t\t\treturn none\n\t\t\t}\n\t\t\tconst config = page.config\n\t\t\tconst override = typeof config.createOverride === 'function'\n\t\t\t\t? config.createOverride\n\t\t\t\t: resolveCreateOverrideHandler(config.createOverride, this.cnRegistry, this.cnCustomComponents)\n\t\t\tconst modalEntry = typeof config.createModal === 'string' && config.createModal !== ''\n\t\t\t\t? (this.cnRegistry || {})[config.createModal]\n\t\t\t\t: null\n\t\t\tconst modal = modalEntry && modalEntry.kind === 'modal' && typeof this.cnOpenModal === 'function'\n\t\t\t\t? config.createModal\n\t\t\t\t: null\n\t\t\treturn { page, override: override || null, modal, form: this.nestedFormConfig(config) }\n\t\t},\n\n\t\t/**\n\t\t * Open the app's own create dialog (a registry modal) for a picker and\n\t\t * wait for it. The dialog reports the new object (or its id) with a\n\t\t * `created` event; `close` without one resolves null so the field stays\n\t\t * as it was. A reported id is fetched so the picker can label it.\n\t\t *\n\t\t * @spec openspec/changes/review-round-two/specs/schema-utilities/spec.md\n\t\t * @param {string} key The registry key of the modal.\n\t\t * @param {object} store The object store.\n\t\t * @param {string} slug The object type slug.\n\t\t * @param {string} labelField The property the typed term belongs in.\n\t\t * @param {string} term The typed search term.\n\t\t * @return {Promise<object|null>} The created object, or null.\n\t\t */\n\t\topenAppCreateModal(key, store, slug, labelField, term) {\n\t\t\treturn new Promise((resolve) => {\n\t\t\t\tlet settled = false\n\t\t\t\tlet reported = false\n\t\t\t\tconst settle = (value) => {\n\t\t\t\t\tif (!settled) {\n\t\t\t\t\t\tsettled = true\n\t\t\t\t\t\tresolve(value)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.cnOpenModal(key, {\n\t\t\t\t\tinitialData: term ? { [labelField]: term } : {},\n\t\t\t\t\tonCreated: async (created) => {\n\t\t\t\t\t\tif (created && typeof created === 'object') {\n\t\t\t\t\t\t\tsettle(created)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (typeof created !== 'string' && typeof created !== 'number') {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\treported = true\n\t\t\t\t\t\tlet obj\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tobj = await store.fetchObject(slug, String(created))\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tobj = null\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsettle(obj || { id: String(created), [labelField]: term })\n\t\t\t\t\t},\n\t\t\t\t\tonClose: () => {\n\t\t\t\t\t\t// A dialog often emits `close` right after `created`,\n\t\t\t\t\t\t// while the id is still being fetched: that fetch settles.\n\t\t\t\t\t\tif (!reported) {\n\t\t\t\t\t\t\tsettle(null)\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The nested create form was confirmed: save the object, hand it to\n\t\t * the waiting picker and close the nested form. A refused save keeps\n\t\t * the nested form open with the server's message.\n\t\t *\n\t\t * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n\t\t * @param {object} formData The nested form's values.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onNestedCreateConfirm(formData) {\n\t\t\tconst pending = this.nestedCreate\n\t\t\tif (!pending) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tlet created = null\n\t\t\tlet thrown = null\n\t\t\tif (pending.override) {\n\t\t\t\t// The app's create override owns persistence (same contract as\n\t\t\t\t// CnIndexPage's `createOverride`).\n\t\t\t\ttry {\n\t\t\t\t\tcreated = await pending.override(formData, {\n\t\t\t\t\t\tregister: pending.register,\n\t\t\t\t\t\tschema: pending.schemaRef,\n\t\t\t\t\t\tobjectType: pending.slug,\n\t\t\t\t\t\teffectiveSchema: pending.schema,\n\t\t\t\t\t})\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthrown = (err && err.response && err.response.data && err.response.data.error) || (err && err.message) || null\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcreated = store ? await store.saveObject(pending.slug, formData) : null\n\t\t\t}\n\t\t\tif (!created) {\n\t\t\t\tconst error = store && store.errors ? store.errors[pending.slug] : null\n\t\t\t\tconst message = thrown\n\t\t\t\t\t|| (!pending.override && error && error.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The item could not be created.')\n\t\t\t\tconst dialog = this.$refs.nestedCreateDialog\n\t\t\t\tif (dialog && typeof dialog.setValidationErrors === 'function') {\n\t\t\t\t\tdialog.setValidationErrors({}, message)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.nestedCreate = null\n\t\t\tpending.resolve(created)\n\t\t},\n\n\t\t/**\n\t\t * The nested create form was closed without saving: leave the field\n\t\t * as it was.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonNestedCreateClose() {\n\t\t\tconst pending = this.nestedCreate\n\t\t\tthis.nestedCreate = null\n\t\t\tif (pending) {\n\t\t\t\tpending.resolve(null)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a value at a plain or dotted key; a dotted key reaches a field\n\t\t * of an object property.\n\t\t *\n\t\t * @param {string} key Field key, or `parent.child`.\n\t\t * @param {*} value The value to write.\n\t\t */\n\t\twriteDotted(key, value) {\n\t\t\tif (Object.hasOwn(this.formData, key) || !key.includes('.')) {\n\t\t\t\tthis.updateField(key, value)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst [head, ...rest] = key.split('.')\n\t\t\tconst unsafe = ['__proto__', 'constructor', 'prototype']\n\t\t\tif (unsafe.includes(head) || rest.some((k) => unsafe.includes(k))) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst clone = JSON.parse(JSON.stringify(this.formData[head] ?? {}))\n\t\t\tlet cur = clone\n\t\t\trest.slice(0, -1).forEach((k) => {\n\t\t\t\tif (cur[k] === null || typeof cur[k] !== 'object') {\n\t\t\t\t\tcur[k] = {}\n\t\t\t\t}\n\t\t\t\tcur = cur[k]\n\t\t\t})\n\t\t\tcur[rest[rest.length - 1]] = value\n\t\t\tthis.updateField(head, clone)\n\t\t},\n\n\t\t/**\n\t\t * After a registry pick resolved, fill empty sibling fields from the\n\t\t * declaration's `config.fill` map and ask once before replacing typed\n\t\t * values. Mode `live` fills nothing.\n\t\t *\n\t\t * @param {object} field The property-source field.\n\t\t * @param {{value: *}} payload The resolved answer.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPropertySourceResolved(field, payload) {\n\t\t\tconst source = field.propertySource\n\t\t\tif (!source || source.mode !== 'default' || !source.config || !source.config.fill) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst known = new Set(this.resolvedFields.map((f) => f.key))\n\t\t\tconst { apply, conflicts } = planPropertySourceFill(\n\t\t\t\tsource.config.fill,\n\t\t\t\tpayload.value,\n\t\t\t\tthis.formData,\n\t\t\t\t(key) => known.has(key) || known.has(String(key).split('.')[0]),\n\t\t\t)\n\t\t\tapply.forEach(({ key, value }) => this.writeDotted(key, value))\n\t\t\tif (conflicts.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst labelOf = (key) => {\n\t\t\t\tconst f = this.resolvedFields.find((x) => x.key === key)\n\t\t\t\treturn f ? f.label : key\n\t\t\t}\n\t\t\tconst replace = await new Promise((resolve) => {\n\t\t\t\tthis.pendingReplace = {\n\t\t\t\t\tchanges: conflicts.map((c) => ({ ...c, label: labelOf(c.key), oldValue: getDotted(this.formData, c.key) })),\n\t\t\t\t\tresolve,\n\t\t\t\t}\n\t\t\t})\n\t\t\tif (replace) {\n\t\t\t\tconflicts.forEach(({ key, newValue }) => this.writeDotted(key, newValue))\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Answer the pending replace question.\n\t\t *\n\t\t * @param {boolean} replace True to overwrite the typed values.\n\t\t */\n\t\tresolveReplace(replace) {\n\t\t\tconst pending = this.pendingReplace\n\t\t\tthis.pendingReplace = null\n\t\t\tif (pending) {\n\t\t\t\tpending.resolve(replace)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Copy template values off a selected/created reference object into the\n\t\t * form, per the field's `fillFrom` map (`{ formKey: sourceKey }`). When\n\t\t * `source` is an id, the object is fetched first. Overwrites existing\n\t\t * values so re-selecting a template refreshes them; no-op without a map.\n\t\t *\n\t\t * @param {object} field The reference field carrying `fillFrom`.\n\t\t * @param {object|string} source The selected object, or its UUID.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync applyTemplateFill(field, source) {\n\t\t\tif (!field || !field.fillFrom || !source) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet obj = (typeof source === 'object') ? source : null\n\t\t\tif (!obj) {\n\t\t\t\tconst register = this.referenceRegister(field)\n\t\t\t\tconst store = this.getObjectStore()\n\t\t\t\tif (!register || !store || !field.reference) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst slug = store.createObjectTypeSlug(register, field.reference.schema)\n\t\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\t\tstore.registerObjectType(slug, field.reference.schema, register)\n\t\t\t\t\t}\n\t\t\t\t\tobj = await store.fetchObject(slug, String(source))\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error(`CnFormDialog: template fill fetch failed for \"${field.key}\":`, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!obj) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor (const formKey of Object.keys(field.fillFrom)) {\n\t\t\t\tconst sourceKey = field.fillFrom[formKey]\n\t\t\t\tconst value = obj[sourceKey]\n\t\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\t\tthis.updateField(formKey, value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an enum `widget:\"switch\"` field is currently \"on\" — i.e. its\n\t\t * value equals the last enum value (off → first enum value).\n\t\t *\n\t\t * @param {object} field The switch field descriptor (carries `enum`).\n\t\t * @return {boolean}\n\t\t */\n\t\tisSwitchOn(field) {\n\t\t\tconst values = Array.isArray(field.enum) ? field.enum : []\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn !!this.formData[field.key]\n\t\t\t}\n\t\t\treturn this.formData[field.key] === values[values.length - 1]\n\t\t},\n\n\t\t/**\n\t\t * Map a switch toggle to the field's enum value: on → last enum value,\n\t\t * off → first enum value.\n\t\t *\n\t\t * @param {object} field The switch field descriptor (carries `enum`).\n\t\t * @param {boolean} on The new switch state.\n\t\t * @return {unknown} The enum value to store.\n\t\t */\n\t\tswitchValueFor(field, on) {\n\t\t\tconst values = Array.isArray(field.enum) ? field.enum : []\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn on\n\t\t\t}\n\t\t\treturn on ? values[values.length - 1] : values[0]\n\t\t},\n\n\t\t/**\n\t\t * Fetch the options for a `$ref` reference field: the objects of the\n\t\t * referenced schema in the form's register, mapped to\n\t\t * `{ id: <uuid>, label: <human name> }`. Server-filters by the search\n\t\t * term when present. Resolved labels are cached in `referenceLabels`\n\t\t * so the select can display the current selection's name. Fails soft\n\t\t * (returns `[]`) when no register is set or the fetch errors.\n\t\t *\n\t\t * @param {object} field The field definition (must carry `field.reference`).\n\t\t * @param {string} query The NcSelect search term.\n\t\t * @return {Promise<Array<{id: string, label: string}>>}\n\t\t */\n\t\t/**\n\t\t * Lazily resolve the generic OpenRegister object store. Acquired on\n\t\t * demand (not in `setup()`) so mounting CnFormDialog without an active\n\t\t * Pinia — e.g. forms with no reference fields, or unit tests — never\n\t\t * fails. Returns null when no Pinia is available.\n\t\t *\n\t\t * @return {object|null} The object store, or null.\n\t\t */\n\t\tgetObjectStore() {\n\t\t\tif (this._objectStore) {\n\t\t\t\treturn this._objectStore\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tthis._objectStore = useObjectStore()\n\t\t\t\treturn this._objectStore\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The comparable form of a driving property's value.\n\t\t *\n\t\t * A `$ref` picker may hold either the bare UUID or the whole option\n\t\t * object, depending on whether the user has just chosen it or it came\n\t\t * back from the server. Both must produce the same signature, or\n\t\t * merely opening an existing record would look like a change and\n\t\t * refetch its definitions.\n\t\t *\n\t\t * @param {unknown} value The raw form value.\n\t\t * @return {string} The value's identity, '' when unset.\n\t\t */\n\t\tdynamicDriverValue(value) {\n\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof value === 'object') {\n\t\t\t\treturn String(value.id || value.uuid || '')\n\t\t\t}\n\t\t\treturn String(value)\n\t\t},\n\n\t\t/**\n\t\t * Fetch and install the extra fields the current driving values ask\n\t\t * for. A driver that is unset clears them; a fetch that fails leaves\n\t\t * them cleared rather than half-populated, because a form missing a\n\t\t * required question it never showed is worse than one that shows none.\n\t\t *\n\t\t * @param {string} token The driver signature this load is for.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadDynamicProperties(token) {\n\t\t\tthis.dynamicToken = token\n\t\t\tconst active = this.extendsDeclarations\n\t\t\t\t.filter(({ key }) => this.dynamicDriverValue(this.formData[key]) !== '')\n\t\t\tif (active.length === 0) {\n\t\t\t\tthis.dynamicProperties = {}\n\t\t\t\tthis.dynamicRequired = []\n\t\t\t\tthis.dynamicOwners = {}\n\t\t\t\tthis.dynamicLoading = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.dynamicLoading = true\n\t\t\tconst properties = {}\n\t\t\tconst required = []\n\t\t\tconst owners = {}\n\t\t\tconst definitions = []\n\t\t\ttry {\n\t\t\t\tfor (const { key, config } of active) {\n\t\t\t\t\tconst schemaSlug = config.definitions && config.definitions.schema\n\t\t\t\t\t// A definitions schema normally lives beside the object's own,\n\t\t\t\t\t// so the form's register is the default; `register` names\n\t\t\t\t\t// another app's when it does not (ADR-066).\n\t\t\t\t\tconst register = (config.definitions && config.definitions.register) || this.register\n\t\t\t\t\tif (!schemaSlug || !register) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst slug = store.createObjectTypeSlug(register, schemaSlug)\n\t\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\t\tstore.registerObjectType(slug, schemaSlug, register)\n\t\t\t\t\t}\n\t\t\t\t\tconst params = definitionQueryParams(\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tthis.dynamicDriverValue(this.formData[key]),\n\t\t\t\t\t\tthis.formData,\n\t\t\t\t\t)\n\t\t\t\t\tconst records = await store.fetchCollectionForOptions(slug, params)\n\t\t\t\t\t// Kept for the confirm payload. Array-mode declarations\n\t\t\t\t\t// store the definition's NAME beside its value on the\n\t\t\t\t\t// parent, and the answers alone carry only ids.\n\t\t\t\t\tif (Array.isArray(records)) {\n\t\t\t\t\t\tdefinitions.push(...records)\n\t\t\t\t\t}\n\t\t\t\t\t// `orderFrom` keeps each declaration's block together and\n\t\t\t\t\t// after the schema's own fields, whose `order` values are\n\t\t\t\t\t// authored well below this.\n\t\t\t\t\tconst mappedProps = propertiesFromDefinitions(\n\t\t\t\t\t\tArray.isArray(records) ? records : [],\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t{ orderFrom: 1000 + Object.keys(properties).length, create: this.isCreateMode },\n\t\t\t\t\t)\n\t\t\t\t\tObject.assign(properties, mappedProps.properties)\n\t\t\t\t\trequired.push(...mappedProps.required)\n\t\t\t\t\tfor (const propKey of Object.keys(mappedProps.properties)) {\n\t\t\t\t\t\towners[propKey] = key\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnFormDialog: could not load the fields for this selection:', err)\n\t\t\t}\n\t\t\t// A slower earlier fetch must not overwrite a later selection's\n\t\t\t// fields; the signature it started under is the only way to tell.\n\t\t\tif (this.dynamicToken !== token) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dynamicDefinitions = definitions\n\t\t\tthis.dynamicProperties = properties\n\t\t\tthis.dynamicRequired = required\n\t\t\tthis.dynamicOwners = owners\n\t\t\tthis.dynamicLoading = false\n\t\t\tthis.seedDynamicDefaults(properties)\n\t\t},\n\n\t\t/**\n\t\t * Whether a field takes the full width of a two-column form.\n\t\t *\n\t\t * A textarea or a JSON editor in a half-width column is worse than no\n\t\t * columns at all, and a checkbox pair reads as one control rather than\n\t\t * two. The list is deliberately about the WIDGET, not the field name,\n\t\t * so it needs no per-app configuration to behave.\n\t\t *\n\t\t * @param {object} field The resolved field.\n\t\t * @return {boolean} True when the field spans both columns.\n\t\t */\n\t\tfieldSpansBothColumns(field) {\n\t\t\tif (this.columns !== 2 || !field) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn WIDE_WIDGETS.includes(field.widget)\n\t\t},\n\n\t\t/**\n\t\t * Copy what the chosen record already answers into the empty fields\n\t\t * that ask the same question.\n\t\t *\n\t\t * Only empty targets are written. Someone who has typed a title before\n\t\t * picking a case type keeps their title, and switching case type after\n\t\t * that keeps it too: the field is no longer empty, so it is no longer\n\t\t * a candidate. That is the whole rule, and it is what makes the\n\t\t * feature safe to run on every change rather than only the first.\n\t\t *\n\t\t * A field the person deliberately cleared reads as empty and will be\n\t\t * filled again by the next change of case type. That is the accepted\n\t\t * cost of not tracking per-field provenance; the alternative is a\n\t\t * dirty-flag per field, which is a great deal of state for a case that\n\t\t * ends with the person clearing the field again.\n\t\t *\n\t\t * @param {string} token The prefill signature this run is for.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync applyPrefill(token) {\n\t\t\tthis.prefillToken = token\n\t\t\tconst active = this.prefillDecls\n\t\t\t\t.filter(({ key }) => this.dynamicDriverValue(this.formData[key]) !== '')\n\t\t\tif (active.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst resolved = {}\n\t\t\ttry {\n\t\t\t\tfor (const { key, config } of active) {\n\t\t\t\t\t// The declaration may name its own schema; otherwise the\n\t\t\t\t\t// picker's own `$ref` target is the record being chosen.\n\t\t\t\t\tconst field = this.resolvedFields.find((f) => f.key === key)\n\t\t\t\t\tconst schemaSlug = config.schema\n\t\t\t\t\t\t|| (field && field.reference && field.reference.schema)\n\t\t\t\t\tconst register = config.register\n\t\t\t\t\t\t|| (field ? this.referenceRegister(field) : this.register)\n\t\t\t\t\tif (!schemaSlug || !register) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tconst slug = store.createObjectTypeSlug(register, schemaSlug)\n\t\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\t\tstore.registerObjectType(slug, schemaSlug, register)\n\t\t\t\t\t}\n\t\t\t\t\tconst record = await store.fetchObject(\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\tthis.dynamicDriverValue(this.formData[key]),\n\t\t\t\t\t)\n\t\t\t\t\tObject.assign(resolved, prefillValues(record, config))\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// A prefill that fails leaves the person typing the values\n\t\t\t\t// themselves, which is exactly where they were before.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnFormDialog: could not prefill from this selection:', err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (this.prefillToken !== token) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor (const [target, value] of Object.entries(resolved)) {\n\t\t\t\tconst current = this.formData[target]\n\t\t\t\tconst empty = current === null || current === undefined || current === ''\n\t\t\t\t\t|| (Array.isArray(current) && current.length === 0)\n\t\t\t\tif (empty) {\n\t\t\t\t\tthis.formData[target] = value\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Seed a newly-arrived dynamic field with its default, without\n\t\t * touching one the user (or the record) already answered.\n\t\t *\n\t\t * @param {object} properties The installed dynamic properties.\n\t\t */\n\t\tseedDynamicDefaults(properties) {\n\t\t\tfor (const [key, prop] of Object.entries(properties)) {\n\t\t\t\tif (prop.default === undefined) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (this.formData[key] !== undefined && this.formData[key] !== null && this.formData[key] !== '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.formData[key] = prop.default\n\t\t\t}\n\t\t},\n\n\t\tasync fetchReferenceOptions(field, query) {\n\t\t\tconst register = this.referenceRegister(field)\n\t\t\tif (!register || !field.reference || !field.reference.schema) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst params = { _limit: 100 }\n\t\t\t\tif (query) {\n\t\t\t\t\tparams._search = query\n\t\t\t\t}\n\t\t\t\t// Declarative option scoping (`x-relation-filter`): narrow the\n\t\t\t\t// picker to objects that fit the form's CURRENT values — e.g. a\n\t\t\t\t// line item's `product` scoped to the chosen leadProduct. Mirrors\n\t\t\t\t// CnObjectDataWidget: values are token-resolved (@object.<field> /\n\t\t\t\t// @objectId) against the live form data; an entry whose token stays\n\t\t\t\t// unresolved is dropped (unfiltered beats an empty picker).\n\t\t\t\tconst prop = (this.schema && this.schema.properties && this.schema.properties[field.key]) || null\n\t\t\t\tconst rawFilter = prop && prop['x-relation-filter']\n\t\t\t\tif (rawFilter && typeof rawFilter === 'object') {\n\t\t\t\t\tconst ctx = { objectId: this.formData.id, object: { ...this.formData } }\n\t\t\t\t\tconst filter = resolveFilterTokens(rawFilter, ctx)\n\t\t\t\t\tfor (const [fk, fv] of Object.entries(filter)) {\n\t\t\t\t\t\tif (typeof fv === 'string' && fv.charAt(0) === '@') {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (fv && typeof fv === 'object') {\n\t\t\t\t\t\t\tfor (const [op, ov] of Object.entries(fv)) {\n\t\t\t\t\t\t\t\tparams[`${fk}[${op}]`] = ov\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (fv !== '' && fv !== null && fv !== undefined) {\n\t\t\t\t\t\t\tparams[fk] = fv\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst slug = store.createObjectTypeSlug(register, field.reference.schema)\n\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\tstore.registerObjectType(slug, field.reference.schema, register)\n\t\t\t\t}\n\t\t\t\tconst results = await store.fetchCollectionForOptions(slug, params)\n\t\t\t\tconst list = Array.isArray(results) ? results : []\n\t\t\t\tconst labels = {}\n\t\t\t\tconst options = list\n\t\t\t\t\t.filter((obj) => obj && obj.id)\n\t\t\t\t\t.map((obj) => {\n\t\t\t\t\t\tconst label = this.displayLabel(obj)\n\t\t\t\t\t\tlabels[obj.id] = label\n\t\t\t\t\t\treturn { id: obj.id, label }\n\t\t\t\t\t})\n\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t}\n\t\t\t\treturn options\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: reference fetch failed for field \"${field.key}\":`, err)\n\t\t\t\treturn []\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve a single reference UUID to its `{ id, label }` option,\n\t\t * fetching the object by id when its label isn't cached yet (edit mode).\n\t\t *\n\t\t * @param {object} field The field definition.\n\t\t * @param {string} uuid The stored UUID.\n\t\t */\n\t\tasync resolveReferenceLabel(field, uuid) {\n\t\t\tconst register = this.referenceRegister(field)\n\t\t\tif (!uuid || this.referenceLabels[uuid] || !register || !field.reference) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = this.getObjectStore()\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst slug = store.createObjectTypeSlug(register, field.reference.schema)\n\t\t\t\tif (!store.objectTypeRegistry[slug]) {\n\t\t\t\t\tstore.registerObjectType(slug, field.reference.schema, register)\n\t\t\t\t}\n\t\t\t\tconst obj = await store.fetchObject(slug, uuid)\n\t\t\t\tif (obj && obj.id) {\n\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [obj.id]: this.displayLabel(obj) }\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: reference label resolve failed for \"${uuid}\":`, err)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Initialize async state for all async fields and trigger initial load.\n\t\t */\n\t\tinitAsyncFields() {\n\t\t\t// Clean up existing timeouts\n\t\t\tfor (const state of Object.values(this.asyncState)) {\n\t\t\t\tif (state.searchTimeout) {\n\t\t\t\t\tclearTimeout(state.searchTimeout)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Iterate the *transformed* fields so a resolved cross-app\n\t\t\t// semantic reference (now a `$ref` picker) gets async state too.\n\t\t\tconst fields = this.resolvedFields.map((field) => this.applySemanticResolution(field))\n\t\t\tconst newState = {}\n\t\t\tfor (const field of fields) {\n\t\t\t\tif (this.isAsyncEnum(field) || this.isAsyncItemsEnum(field)) {\n\t\t\t\t\tnewState[field.key] = { options: [], loading: false, searchTimeout: null }\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.asyncState = newState\n\n\t\t\t// Trigger initial load for each async field. A select-or-create\n\t\t\t// reference loads its own options (CnResourceSelect), so it is\n\t\t\t// skipped here rather than fetched twice.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tfor (const field of fields) {\n\t\t\t\t\tif (field.allowCreate && field.reference) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (this.isAsyncEnum(field) || this.isAsyncItemsEnum(field)) {\n\t\t\t\t\t\tthis.loadAsyncOptions(field, '')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Load async options for a field by calling its enum function.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {string} query Search query\n\t\t */\n\t\tasync loadAsyncOptions(field, query) {\n\t\t\tconst state = this.asyncState[field.key]\n\t\t\tif (!state) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tstate.loading = true\n\n\t\t\ttry {\n\t\t\t\tlet results\n\t\t\t\tif (this.isCodedField(field) || this.isCodedArrayField(field)) {\n\t\t\t\t\t// Options OpenRegister serves for the bound scheme: its order,\n\t\t\t\t\t// its validity window, its labels in the user's language.\n\t\t\t\t\tresults = await this.fetchCodedOptions(field)\n\t\t\t\t} else if (this.isReferenceField(field) || this.isReferenceArrayField(field)) {\n\t\t\t\t\t// OpenRegister object reference — fetch the referenced objects.\n\t\t\t\t\tresults = await this.fetchReferenceOptions(field, query)\n\t\t\t\t} else if (this.isUserField(field) || this.isUserArrayField(field)) {\n\t\t\t\t\t// Nextcloud user reference — search real users via the core\n\t\t\t\t\t// autocomplete OCS endpoint. Cache labels so the current\n\t\t\t\t\t// selection still displays its display name.\n\t\t\t\t\tresults = await searchNextcloudUsers(query)\n\t\t\t\t\tconst labels = {}\n\t\t\t\t\tfor (const opt of results) {\n\t\t\t\t\t\tif (opt && opt.id) {\n\t\t\t\t\t\t\tlabels[opt.id] = opt.label || String(opt.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t\t}\n\t\t\t\t} else if (this.isGroupField(field) || this.isGroupArrayField(field)) {\n\t\t\t\t\t// Nextcloud group: search groups, store the gid, show the name.\n\t\t\t\t\tresults = await searchNextcloudGroups(query)\n\t\t\t\t\tconst labels = {}\n\t\t\t\t\tfor (const opt of results) {\n\t\t\t\t\t\tif (opt && opt.id) {\n\t\t\t\t\t\t\tlabels[opt.id] = opt.label || String(opt.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst enumFn = typeof field.enum === 'function' ? field.enum : field.items.enum\n\t\t\t\t\tresults = await enumFn(query)\n\t\t\t\t}\n\t\t\t\tstate.options = Array.isArray(results) ? results : []\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error(`CnFormDialog: async enum error for field \"${field.key}\":`, err)\n\t\t\t\tstate.options = []\n\t\t\t} finally {\n\t\t\t\tstate.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle search input on an async select with debounce.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {string} query Search query\n\t\t */\n\t\tonAsyncSearch(field, query) {\n\t\t\tconst state = this.asyncState[field.key]\n\t\t\tif (!state) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (state.searchTimeout) {\n\t\t\t\tclearTimeout(state.searchTimeout)\n\t\t\t}\n\n\t\t\tconst debounceMs = field.debounce || 300\n\n\t\t\tstate.searchTimeout = setTimeout(() => {\n\t\t\t\tthis.loadAsyncOptions(field, query || '')\n\t\t\t}, debounceMs)\n\t\t},\n\n\t\t/**\n\t\t * Get the effective options for a select field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Array}\n\t\t */\n\t\tgetEffectiveOptions(field) {\n\t\t\tif (this.isAsyncEnum(field)) {\n\t\t\t\t// TODO: restore to `this.asyncState[field.key]?.options || []` once on Vue 3 (buble doesn't support optional chaining)\n\t\t\t\treturn (this.asyncState[field.key] && this.asyncState[field.key].options) || []\n\t\t\t}\n\t\t\treturn this.getEnumOptions(field)\n\t\t},\n\n\t\t/**\n\t\t * Get the effective selected value for a select field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {object|null}\n\t\t */\n\t\tgetEffectiveSelectedOption(field) {\n\t\t\tif (this.isIdPickerField(field)) {\n\t\t\t\t// Reference / user fields store the UUID/UID — resolve it to a\n\t\t\t\t// display option `{ id, label }` (label from the resolved-labels\n\t\t\t\t// cache, falling back to the id until it loads).\n\t\t\t\tconst uuid = this.formData[field.key]\n\t\t\t\tif (uuid === null || uuid === undefined || uuid === '') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst label = this.referenceLabels[uuid] || String(uuid)\n\t\t\t\t// NcSelectUsers reads `displayName`, not `label` — a user field's\n\t\t\t\t// selected option needs both.\n\t\t\t\treturn this.isUserField(field) ? { id: uuid, label, displayName: label } : { id: uuid, label }\n\t\t\t}\n\t\t\tif (this.isAsyncEnum(field)) {\n\t\t\t\t// For async fields, formData stores the full option object\n\t\t\t\treturn this.formData[field.key] || null\n\t\t\t}\n\t\t\treturn this.getSelectedEnumOption(field)\n\t\t},\n\n\t\t/**\n\t\t * Handle select change for both async and static fields.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {object|null} option The selected option\n\t\t */\n\t\tonEffectiveSelectChange(field, option) {\n\t\t\tif (this.isIdPickerField(field)) {\n\t\t\t\t// Reference / user fields store the chosen id (UUID / UID),\n\t\t\t\t// not the full option. Cache its label so the selection displays.\n\t\t\t\t// A user option can carry only `displayName` (NcSelectUsers' own\n\t\t\t\t// shape), and without that fallback the chip showed the uid.\n\t\t\t\tif (option && option.id) {\n\t\t\t\t\tconst name = option.label || option.displayName || this.referenceLabels[option.id]\n\t\t\t\t\tif (name) {\n\t\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, [option.id]: name }\n\t\t\t\t\t} else if (this.isUserField(field)) {\n\t\t\t\t\t\tthis.resolveUserLabel(option.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.updateField(field.key, option ? option.id : null)\n\t\t\t\t// Reference options are label-only ({id,label}) — pass the id so\n\t\t\t\t// template pre-fill fetches the full object.\n\t\t\t\tif (option && option.id && this.isReferenceField(field)) {\n\t\t\t\t\tthis.applyTemplateFill(field, String(option.id))\n\t\t\t\t}\n\t\t\t} else if (this.isAsyncEnum(field)) {\n\t\t\t\t// Store full option object for async selects\n\t\t\t\tthis.updateField(field.key, option || null)\n\t\t\t} else {\n\t\t\t\tthis.onSelectChange(field.key, option)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Get effective options for a multiselect field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Array}\n\t\t */\n\t\tgetEffectiveArrayOptions(field) {\n\t\t\tif (this.isAsyncItemsEnum(field)) {\n\t\t\t\t// TODO: restore to `this.asyncState[field.key]?.options || []` once on Vue 3 (buble doesn't support optional chaining)\n\t\t\t\treturn (this.asyncState[field.key] && this.asyncState[field.key].options) || []\n\t\t\t}\n\t\t\treturn this.getArrayEnumOptions(field)\n\t\t},\n\n\t\t/**\n\t\t * Get effective selected values for a multiselect field (async or static).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {Array}\n\t\t */\n\t\tgetEffectiveSelectedArrayOptions(field) {\n\t\t\tif (this.isIdPickerArrayField(field)) {\n\t\t\t\t// Reference / user arrays store an array of ids (UUIDs / UIDs) —\n\t\t\t\t// resolve each to a display option `{ id, label }`.\n\t\t\t\tconst uuids = this.formData[field.key]\n\t\t\t\tif (!Array.isArray(uuids)) {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\t\t\t\treturn uuids.map((uuid) => ({ id: uuid, label: this.referenceLabels[uuid] || String(uuid) }))\n\t\t\t}\n\t\t\tif (this.isAsyncItemsEnum(field)) {\n\t\t\t\t// For async fields, formData stores array of full option objects\n\t\t\t\treturn this.formData[field.key] || []\n\t\t\t}\n\t\t\treturn this.getSelectedArrayOptions(field)\n\t\t},\n\n\t\t/**\n\t\t * Handle multiselect change for both async and static fields.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @param {Array} options The selected options\n\t\t */\n\t\tonEffectiveMultiSelectChange(field, options) {\n\t\t\tif (this.isIdPickerArrayField(field)) {\n\t\t\t\t// Reference / user arrays store an array of ids (UUIDs / UIDs).\n\t\t\t\t// Cache labels so the chips still display the human names.\n\t\t\t\tconst list = options || []\n\t\t\t\tconst labels = {}\n\t\t\t\tfor (const o of list) {\n\t\t\t\t\tif (o && o.id) {\n\t\t\t\t\t\tlabels[o.id] = o.label || o.displayName || this.referenceLabels[o.id] || String(o.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Object.keys(labels).length > 0) {\n\t\t\t\t\tthis.referenceLabels = { ...this.referenceLabels, ...labels }\n\t\t\t\t}\n\t\t\t\tthis.updateField(field.key, list.map((o) => o.id))\n\t\t\t} else if (this.isAsyncItemsEnum(field)) {\n\t\t\t\t// Store full option objects for async multiselect\n\t\t\t\tthis.updateField(field.key, options || [])\n\t\t\t} else {\n\t\t\t\tthis.onMultiSelectChange(field.key, options)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a field's async options are currently loading.\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisFieldLoading(field) {\n\t\t\t// TODO: restore to `this.asyncState[field.key]?.loading || false` once on Vue 3 (buble doesn't support optional chaining)\n\t\t\treturn (this.asyncState[field.key] && this.asyncState[field.key].loading) || false\n\t\t},\n\n\t\t/**\n\t\t * Whether a field has any async behavior (enum or items.enum is a function).\n\t\t *\n\t\t * @param {object} field The field definition\n\t\t * @return {boolean}\n\t\t */\n\t\tisFieldAsync(field) {\n\t\t\treturn this.isAsyncEnum(field) || this.isAsyncItemsEnum(field)\n\t\t},\n\n\t\t/**\n\t\t * Run client-side validation on all form fields.\n\t\t * Checks required, minLength, maxLength, pattern, minimum, maximum.\n\t\t *\n\t\t * @return {boolean} True if all fields pass validation\n\t\t * @public\n\t\t */\n\t\tvalidate() {\n\t\t\tconst newErrors = {}\n\t\t\tfor (const field of this.visibleFields) {\n\t\t\t\tconst value = this.formData[field.key]\n\n\t\t\t\t// Child rows: a failing row blocks the submit and names row and field.\n\t\t\t\tif (field.widget === 'child-records') {\n\t\t\t\t\tconst problems = this.childProblems[field.key]\n\t\t\t\t\tif (Array.isArray(problems) && problems.length > 0) {\n\t\t\t\t\t\tnewErrors[field.key] = describeRowProblem(problems)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Required check\n\t\t\t\tif (field.required) {\n\t\t\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\t\t\tnewErrors[field.key] = `${field.label} is required.`\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (Array.isArray(value) && value.length === 0) {\n\t\t\t\t\t\tnewErrors[field.key] = `${field.label} is required.`\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Skip further validation if empty and not required\n\t\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Sub-objects: every row must carry the row schema's required values.\n\t\t\t\tif (field.widget === 'sub-objects' && Array.isArray(value) && field.items && Array.isArray(field.items.required)) {\n\t\t\t\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\t\t\t\tconst missing = field.items.required.find((k) => {\n\t\t\t\t\t\t\tconst cell = value[i] ? value[i][k] : undefined\n\t\t\t\t\t\t\treturn cell === undefined || cell === null || cell === ''\n\t\t\t\t\t\t})\n\t\t\t\t\t\tif (missing !== undefined) {\n\t\t\t\t\t\t\tnewErrors[field.key] = t('nextcloud-vue', 'Row {n}: {field} is required.', { n: i + 1, field: missing })\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Duration: schema minimum / maximum given as ISO strings compare in seconds.\n\t\t\t\tif (field.widget === 'duration' && typeof value === 'string') {\n\t\t\t\t\tconst v0 = field.validation || {}\n\t\t\t\t\tconst secs = durationSeconds(value)\n\t\t\t\t\tif (secs !== null && typeof v0.minimum === 'string' && durationSeconds(v0.minimum) !== null && secs < durationSeconds(v0.minimum)) {\n\t\t\t\t\t\tnewErrors[field.key] = t('nextcloud-vue', 'Minimum duration is {min}.', { min: v0.minimum })\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (secs !== null && typeof v0.maximum === 'string' && durationSeconds(v0.maximum) !== null && secs > durationSeconds(v0.maximum)) {\n\t\t\t\t\t\tnewErrors[field.key] = t('nextcloud-vue', 'Maximum duration is {max}.', { max: v0.maximum })\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst v = field.validation || {}\n\n\t\t\t\t// Whether the user actually edited this field this session.\n\t\t\t\t// Persisted server values that the user never touched are\n\t\t\t\t// trusted as-is — re-running format/pattern checks against\n\t\t\t\t// them would block editing an unrelated field (e.g. a stored\n\t\t\t\t// uuid relation or a normalised date-time).\n\t\t\t\tconst touched = !!this.touchedFields[field.key]\n\n\t\t\t\t// String length / pattern checks. minLength/maxLength only\n\t\t\t\t// apply when they are real numbers — an explicit `null`\n\t\t\t\t// (or undefined) means \"no limit\", not a zero cap.\n\t\t\t\tif (typeof value === 'string') {\n\t\t\t\t\tif (typeof v.minLength === 'number' && value.length < v.minLength) {\n\t\t\t\t\t\tnewErrors[field.key] = `Minimum ${v.minLength} characters.`\n\t\t\t\t\t} else if (typeof v.maxLength === 'number' && value.length > v.maxLength) {\n\t\t\t\t\t\tnewErrors[field.key] = `Maximum ${v.maxLength} characters.`\n\t\t\t\t\t} else if (v.pattern !== undefined && v.pattern !== null && touched) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (!new RegExp(v.pattern).test(value)) {\n\t\t\t\t\t\t\t\tnewErrors[field.key] = 'Invalid format.'\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Ignore invalid regex patterns\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Number range checks (numeric bounds only)\n\t\t\t\tif (typeof value === 'number') {\n\t\t\t\t\tif (typeof v.minimum === 'number' && value < v.minimum) {\n\t\t\t\t\t\tnewErrors[field.key] = `Minimum value is ${v.minimum}.`\n\t\t\t\t\t} else if (typeof v.maximum === 'number' && value > v.maximum) {\n\t\t\t\t\t\tnewErrors[field.key] = `Maximum value is ${v.maximum}.`\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.errors = newErrors\n\t\t\treturn Object.keys(newErrors).length === 0\n\t\t},\n\n\t\texecuteConfirm() {\n\t\t\tif (!this.validate()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.jsonFieldsValid) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.formError = null\n\t\t\tthis.loading = true\n\t\t\t// The object's own fields and the answers to its data-driven\n\t\t\t// questions go out separately: a value row references the parent,\n\t\t\t// so it cannot be written in the same call, and posting a dynamic\n\t\t\t// key to the parent schema would have OpenRegister drop it\n\t\t\t// silently — a 200, an object back, and the answer gone. A host\n\t\t\t// that ignores the second argument therefore still posts a clean\n\t\t\t// payload rather than losing declared fields to undeclared ones.\n\t\t\t// Publishing a draft runs the full validation above and clears the marker.\n\t\t\tconst submit = this.buildSubmitPayload()\n\t\t\tif (this.isDraftRecord) {\n\t\t\t\tsubmit[this.draftField] = false\n\t\t\t}\n\t\t\tconst { base, answers: raw } = splitDynamicFormData(submit)\n\t\t\tconst answers = raw.map((answer) => ({\n\t\t\t\t...answer,\n\t\t\t\tdeclarationKey: this.dynamicOwners[DYNAMIC_KEY_PREFIX + answer.definitionId] || '',\n\t\t\t}))\n\t\t\t/**\n\t\t\t * @event confirm Emitted when the user confirms the form.\n\t\t\t * Payload: form data object. Includes `id` when editing. A second\n\t\t\t * argument carries the data-driven answers (`{ answers, declarations }`)\n\t\t\t * when the schema declares `x-openregister-extends-form`; it is\n\t\t\t * `null` otherwise, which is every schema that declares nothing.\n\t\t\t */\n\t\t\tthis.$emit('confirm', base, answers.length > 0\n\t\t\t\t? { answers, declarations: this.extendsDeclarations, definitions: this.dynamicDefinitions }\n\t\t\t\t: null)\n\t\t},\n\n\t\t/**\n\t\t * Take the offered draft into the form.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\trestoreDraft() {\n\t\t\tif (this.draftOffer === null) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.formData = { ...this.formData, ...this.draftOffer.values }\n\t\t\tthis.draftOffer = null\n\t\t},\n\n\t\t/**\n\t\t * Decline the offered draft and forget it.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdiscardDraft() {\n\t\t\tthis.forgetDraft(this.formDraftKey)\n\t\t},\n\n\t\t/**\n\t\t * Save the record as a draft, without validating it.\n\t\t *\n\t\t * 🔴 THE ONE PATH AROUND VALIDATION, AND IT IS EXPLICIT. A half-filled\n\t\t * form is the whole point of a draft, so required fields are not\n\t\t * enforced. It is reachable only by pressing a button that says so, and\n\t\t * only on a schema that declares somewhere to record it.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tsaveDraft() {\n\t\t\tif (!this.canSaveDraft) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.formError = null\n\t\t\tthis.loading = true\n\t\t\tconst payload = { ...this.buildSubmitPayload(), [this.draftField]: true }\n\n\t\t\t/**\n\t\t\t * @event draft-saved Emitted when the user asks to store the record as a draft. Payload: the form data with the draft field set. The host saves it and calls `setResult()` as it would for a confirm.\n\t\t\t */\n\t\t\tthis.$emit('draft-saved', payload)\n\t\t},\n\n\t\t/**\n\t\t * Build the payload emitted on confirm. A shallow clone of formData\n\t\t * with one normalisation: an empty-string value on a field that\n\t\t * carries a `format` or `pattern` constraint is coerced to `null`.\n\t\t * An empty string is not a valid `uuid` / `email` / `date-time` etc.,\n\t\t * so re-submitting a persisted-but-blank constrained field as `''`\n\t\t * would trip the backend's format validator; `null` (no value) does\n\t\t * not. Plain unconstrained string fields are left as-is.\n\t\t *\n\t\t * @return {object} The payload to emit.\n\t\t */\n\t\tbuildSubmitPayload() {\n\t\t\tconst payload = { ...this.formData }\n\t\t\t// Hidden tenant keys: an empty one takes the platform's tenant; one\n\t\t\t// nobody can fill goes out absent rather than as '', so the\n\t\t\t// server's own default or validation handles it.\n\t\t\tconst tenant = this._platformTenant()\n\t\t\tfor (const key of this.tenantKeys) {\n\t\t\t\tif (!this._isEmptyTenantValue(payload[key])) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (tenant) {\n\t\t\t\t\tpayload[key] = tenant\n\t\t\t\t} else {\n\t\t\t\t\tdelete payload[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Child records are saved as their own objects after the parent,\n\t\t\t// never nested in the parent's payload.\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (field.widget === 'child-records') {\n\t\t\t\t\tdelete payload[field.key]\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tif (payload[field.key] !== '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst v = field.validation || {}\n\t\t\t\tconst hasFormatConstraint = !!field.format\n\t\t\t\t\t|| (v.pattern !== undefined && v.pattern !== null)\n\t\t\t\tif (hasFormatConstraint) {\n\t\t\t\t\tpayload[field.key] = null\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Files over the inline cap upload after the object is saved.\n\t\t\tconst split = splitHeldFiles(payload, this.resolvedFields.filter((f) => f.widget === 'file').map((f) => f.key))\n\t\t\tthis.heldFiles = split.held\n\t\t\tthis.heldKept = split.kept\n\t\t\treturn split.payload\n\t\t},\n\n\t\t/**\n\t\t * Size options for a file field: the property's limit, and the inline cap when the limit is above it.\n\t\t *\n\t\t * @param {object} field The resolved field.\n\t\t * @return {object} Props for CnFileField.\n\t\t */\n\t\tfileLimits(field) {\n\t\t\tconst max = field.file && field.file.maxSize\n\t\t\tif (!max) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn max > INLINE_FILE_CAP ? { maxSize: max, inlineMax: INLINE_FILE_CAP } : { maxSize: max }\n\t\t},\n\n\t\t/**\n\t\t * Upload the held files of the saved object and report any that failed.\n\t\t *\n\t\t * @param {{id?: string, object?: object}} resultData The parent's result; its id is the saved object's id.\n\t\t * @return {Promise<object>} The result, with an `error` naming files that were not uploaded.\n\t\t */\n\t\tasync uploadHeld(resultData) {\n\t\t\tconst objectId = resultData.id || resultData.object?.id || resultData.object?.uuid || this.item?.id || this.item?.uuid\n\t\t\tconst schemaId = (this.schema && (this.schema.slug || this.schema.id)) || ''\n\t\t\tif (!objectId || !schemaId || Object.keys(this.heldFiles).length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\tthis.uploadProgress = { loaded: 0, total: 0, file: '' }\n\t\t\tconst outcome = await uploadHeldFiles({\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: String(schemaId),\n\t\t\t\tobjectId,\n\t\t\t\theld: this.heldFiles,\n\t\t\t\tkept: this.heldKept,\n\t\t\t\tonProgress: (p) => {\n\t\t\t\t\tthis.uploadProgress = p\n\t\t\t\t},\n\t\t\t})\n\t\t\tthis.uploadProgress = null\n\t\t\tthis.heldKept = { ...this.heldKept, ...Object.fromEntries(Object.entries(outcome.attached).filter(([, v]) => Array.isArray(v))) }\n\t\t\tthis.heldFiles = heldFromFailures(outcome.failed)\n\t\t\tthis.uploadFailedNames = outcome.failed.map((f) => f.file.name)\n\t\t\tthis.pendingResult = resultData\n\t\t\tif (outcome.failed.length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\treturn { ...resultData, error: [resultData.error, t('nextcloud-vue', 'Saved, but some files were not uploaded: {files}', { files: this.uploadFailedNames.join(', ') })].filter(Boolean).join(' ') }\n\t\t},\n\n\t\t/** Try the files that did not upload again. */\n\t\tasync retryUploads() {\n\t\t\tthis.loading = true\n\t\t\tconst merged = await this.uploadHeld(this.pendingResult || { success: true })\n\t\t\tthis.loading = false\n\t\t\tthis.applyResult(merged)\n\t\t},\n\n\t\t/**\n\t\t * Set the result of the save operation. Call this from the parent\n\t\t * after the API call completes.\n\t\t *\n\t\t * @param {{ success?: boolean, error?: string }} resultData - Result data to pass to the dialog\n\t\t * @public\n\t\t */\n\t\tsetResult(resultData) {\n\t\t\tthis.loading = false\n\t\t\tif (resultData && resultData.success && Object.keys(this.heldFiles).length > 0) {\n\t\t\t\tconst rows = this.resolvedFields.some((f) => f.widget === 'child-records') ? this.saveChildRecords(resultData) : Promise.resolve(resultData)\n\t\t\t\treturn rows.then((merged) => this.uploadHeld(merged)).then((merged) => this.applyResult(merged))\n\t\t\t}\n\t\t\tif (resultData && resultData.success && this.resolvedFields.some((f) => f.widget === 'child-records')) {\n\t\t\t\treturn this.saveChildRecords(resultData).then((merged) => this.applyResult(merged))\n\t\t\t}\n\t\t\treturn this.applyResult(resultData)\n\t\t},\n\n\t\t/**\n\t\t * Toast the outcome of the write, unless `feedback` is off: success\n\t\t * names the object's title (or the schema's), a failure carries the\n\t\t * server's message.\n\t\t *\n\t\t * @param {{ success?: boolean, error?: string }} resultData The result being shown.\n\t\t */\n\t\treportWrite(resultData) {\n\t\t\tif (!this.feedback || !resultData) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst feedback = useWriteFeedback()\n\t\t\tif (resultData.success) {\n\t\t\t\tconst title = objectDisplayName(this.formData || {}) || String((this.schema && this.schema.title) || '').toLowerCase()\n\t\t\t\tfeedback.success(title !== '' ? t('nextcloud-vue', 'Saved {title}', { title }) : t('nextcloud-vue', 'Saved'))\n\t\t\t}\n\t\t\tif (resultData.error) {\n\t\t\t\tfeedback.error(resultData.error)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Show a result and, on success, auto-close.\n\t\t *\n\t\t * @param {{ success?: boolean, error?: string }} resultData The result to show.\n\t\t */\n\t\tapplyResult(resultData) {\n\t\t\tthis.result = resultData\n\t\t\tthis.reportWrite(resultData)\n\t\t\t// A file that did not upload keeps the dialog open for Retry.\n\t\t\tif (resultData.success && Object.keys(this.heldFiles).length === 0) {\n\t\t\t\t// The values are on the server now, so the local copy has\n\t\t\t\t// nothing left to protect. Cleared only on SUCCESS: a failed\n\t\t\t\t// save is exactly when somebody needs their typing back.\n\t\t\t\tthis.forgetDraft(this.formDraftKey)\n\t\t\t\tthis.closeTimeout = setTimeout(() => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event close Emitted when the dialog should close: the user dismissed it, or a successful save auto-closed it.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('close')\n\t\t\t\t}, 2000)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Save the child-records tables after the parent: one bulk save and one\n\t\t * bulk delete per field. A refused row is named in the result and the\n\t\t * parent stays saved.\n\t\t *\n\t\t * @param {{success?: boolean, error?: string, id?: string, object?: object}} resultData The parent's result; its id (or `object.id`) is the parent id.\n\t\t * @return {Promise<object>} The result, with an `error` listing rows that were not saved.\n\t\t */\n\t\tasync saveChildRecords(resultData) {\n\t\t\tconst fields = this.resolvedFields.filter((f) => f.widget === 'child-records' && f.childRecords)\n\t\t\tif (fields.length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\tconst parentId = resultData.id || resultData.object?.id || resultData.object?.uuid || this.item?.id || this.item?.uuid\n\t\t\tif (!parentId) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\tconst { save } = useChildRecords()\n\t\t\tconst lines = []\n\t\t\tfor (const field of fields) {\n\t\t\t\tconst outcome = await save({\n\t\t\t\t\tregister: this.register,\n\t\t\t\t\tschema: field.childRecords.schema,\n\t\t\t\t\tparentField: field.childRecords.parentField,\n\t\t\t\t\tparentId,\n\t\t\t\t\toriginal: this.childOriginals[field.key] || [],\n\t\t\t\t\trows: Array.isArray(this.formData[field.key]) ? this.formData[field.key] : [],\n\t\t\t\t})\n\t\t\t\tfor (const failure of outcome.failed) {\n\t\t\t\t\tlines.push(`${field.label}: ${failure.reason}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lines.length === 0) {\n\t\t\t\treturn resultData\n\t\t\t}\n\t\t\treturn { ...resultData, error: [resultData.error, t('nextcloud-vue', 'Saved, but some rows were not saved: {rows}', { rows: lines.join('; ') })].filter(Boolean).join(' ') }\n\t\t},\n\n\t\t/**\n\t\t * Set validation errors from the server WITHOUT leaving the form phase,\n\t\t * so the user can correct the data. Call this from the parent (instead\n\t\t * of `setResult`) when the API returns a validation error.\n\t\t *\n\t\t * @param {object} [fieldErrors] Object keyed by field key with per-field error messages\n\t\t * @param {string} [message] Form-level message shown in an error note above the fields\n\t\t * @public\n\t\t */\n\t\tsetValidationErrors(fieldErrors = {}, message = null) {\n\t\t\tthis.loading = false\n\t\t\tthis.result = null\n\t\t\tthis.errors = { ...this.errors, ...fieldErrors }\n\t\t\tthis.formError = message\n\t\t\tif (this.feedback && message) {\n\t\t\t\tuseWriteFeedback().error(message)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* A select option breaks between words only, never inside one. */\n.cn-form-dialog__option {\n\tdisplay: block;\n\tmin-width: 0;\n\twhite-space: normal;\n\toverflow-wrap: normal;\n\tword-break: normal;\n\thyphens: manual;\n}\n\n.cn-form-dialog__draft-actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n/* The state is announced in WORDS, and the colour only repeats them. A\n reader who cannot see it still hears \"Draft saved\" (WCAG 2.2 SC 1.4.1). */\n.cn-form-dialog__draft-state {\n\talign-self: center;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tmargin-inline-end: auto;\n}\n\n.cn-form-dialog__switch-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-form-dialog__form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-form-dialog__field {\n\tmargin-bottom: 8px;\n}\n\n/* Two-column layout. The grid lives on a wrapper that only exists when\n `columns` is 2, so a single-column form keeps its plain block flow and\n cannot be reflowed by a rule meant for the other mode.\n\n `min-width: 0` on the items is what stops a long select or a wide input\n from pushing its column past its share: a grid item's default `auto`\n minimum is its content, so without it one long option label widens the\n whole column and the two stop being equal. */\n.cn-form-dialog__form--two-column {\n\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr;\n\tcolumn-gap: 16px;\n\talign-items: start;\n}\n\n.cn-form-dialog__form--two-column > * {\n\tmin-width: 0;\n}\n\n/* Anything that is not a field (the form-level error note, the slots above\n and below the fields) spans the full width, so a note card never sits in\n a half-width column beside an input. */\n.cn-form-dialog__form--two-column > :not(.cn-form-dialog__field),\n.cn-form-dialog__form--two-column > .cn-form-dialog__field--wide {\n\tgrid-column: 1 / -1;\n}\n\n/* A narrow viewport gets the single column back. Two columns of roughly\n 150px each are worse than one readable one. */\n@media (max-width: 700px) {\n\t.cn-form-dialog__form--two-column {\n\t\tdisplay: block;\n\t}\n}\n\n.cn-form-dialog__textarea-wrapper,\n.cn-form-dialog__select-wrapper,\n.cn-form-dialog__json-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-form-dialog__label {\n\tfont-weight: 600;\n\tfont-size: 0.9em;\n\tcolor: var(--color-main-text);\n}\n\n/* Cross-app semantic reference (ADR-048) whose provider isn't installed —\n rendered disabled; the wrapper carries the mouse-over tooltip. */\n.cn-form-dialog__semantic-unresolved {\n\tcursor: not-allowed;\n}\n\n.cn-form-dialog__textarea {\n\twidth: 100%;\n\tmin-height: 80px;\n\tpadding: 8px;\n\tborder: 2px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-large);\n\tbackground-color: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tresize: vertical;\n}\n\n.cn-form-dialog__textarea:focus {\n\tborder-color: var(--color-primary-element);\n\toutline: none;\n}\n\n.cn-form-dialog__textarea:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n.cn-form-dialog__helper {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-form-dialog__helper--error {\n\tcolor: var(--color-error);\n}\n\n.cn-form-dialog__dynamic-loading {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 0;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_createBlock","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","_createTextVNode","_normalizeClass","_renderSlot","_Fragment","_renderList","_resolveDynamicComponent","_mergeProps","_withCtx"],"mappings":"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"}
|