feat(14.2.1-05): cover mlmarkdown, child adopt, and regen admin artifacts

MLMarkdownField and relation-child forms share the enabled-locale
inject and adopt merge; OpenAPI, types, docs, and boardwalk dist match.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-10-06 15:41:46 +02:00
parent 0f8d13738d
commit 424fe9ccc0
14 changed files with 104 additions and 28 deletions

View File

@@ -4506,6 +4506,7 @@ export interface components {
update: components["schemas"]["cabana.MessageForms"];
};
"cabana.FormMeta": {
enabledLocales?: string[];
locale: string;
};
"cabana.FormOption": {

View File

@@ -1,16 +1,18 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { computed, inject, onMounted, onUnmounted, ref } from 'vue'
import type { FieldControlProps } from '../control'
import MarkdownField from './MarkdownField.vue'
import { FORM_ENABLED_LOCALES } from '../formContext'
import { ML_LOCALE_CHANGE, broadcastMLLocale, localeRecord } from '../mlLocale'
const props = defineProps<FieldControlProps>()
const emit = defineEmits<{ 'update:modelValue': [value: Record<string, string>] }>()
const values = computed(() => localeRecord(props.modelValue))
const enabledLocales = inject(FORM_ENABLED_LOCALES, null)
const locales = computed(() => {
const keys = Object.keys(values.value)
return keys.length > 0 ? keys : ['en']
const codes = enabledLocales?.value
return codes && codes.length > 0 ? [...codes] : []
})
const activeLocale = ref(locales.value[0] ?? 'en')
const copyFrom = ref('')

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, nextTick, provide, readonly, ref, watch } from 'vue'
import { computed, inject, nextTick, provide, readonly, ref, watch } from 'vue'
import {
DialogClose,
DialogContent,
@@ -17,7 +17,7 @@ import { CHILD_SESSION_HEADER, SESSION_HEADER, newSessionKey } from '../../app/s
import FormErrorBanner from '../form/FormErrorBanner.vue'
import FormGrid from '../form/FormGrid.vue'
import FormTabs from '../form/FormTabs.vue'
import { FORM_PATCH, FORM_SESSION, FORM_VALUES, createUploadGate, type FormSession } from '../form/formContext'
import { FORM_ENABLED_LOCALES, FORM_PATCH, FORM_SESSION, FORM_VALUES, createUploadGate, type FormSession } from '../form/formContext'
import {
DEFAULT_TAB,
contextAllows,
@@ -25,6 +25,7 @@ import {
fieldErrors,
focusField,
initialValues,
mergeMLValue,
panelDomId,
snapshot,
tabDomId,
@@ -198,6 +199,8 @@ const session: FormSession = {
provide(FORM_SESSION, session)
provide(FORM_VALUES, computed(() => values.value))
provide(FORM_PATCH, update)
const enabledLocales = inject(FORM_ENABLED_LOCALES, computed(() => [] as readonly string[]))
provide(FORM_ENABLED_LOCALES, enabledLocales)
function update(name: string, value: unknown): void {
values.value = { ...values.value, [name]: value }
@@ -209,7 +212,16 @@ function update(name: string, value: unknown): void {
}
function adopt(record: AdminRecord, meta?: RecordMeta): void {
values.value = { ...record }
const locales = enabledLocales.value
const next: AdminRecord = { ...record }
for (const field of fields.value) {
if (field.type === 'password') {
next[field.name] = ''
} else if (field.type === 'mltext' || field.type === 'mlmarkdown') {
next[field.name] = mergeMLValue(record[field.name], locales)
}
}
values.value = next
labels.value = meta?.labels ?? {}
saved.value = snapshot(editablePayload(fields.value, values.value))
}
@@ -239,7 +251,7 @@ async function load(): Promise<void> {
activeTab.value = DEFAULT_TAB
if (props.mode === 'create' || props.childId === null) {
loading.value = false
adopt(initialValues(fields.value))
adopt(initialValues(fields.value, enabledLocales.value))
activeTab.value = tabs.value[0]?.key ?? DEFAULT_TAB
await focusFirst()
return

View File

@@ -228,7 +228,7 @@ async function goNext(): Promise<void> {
return
}
step.value = 2
pivotValues.value = initialValues(pivotFields.value)
pivotValues.value = initialValues(pivotFields.value, [])
pivotErrors.value = {}
await nextTick()
stepTwo.value?.querySelector<HTMLElement>('input, select, textarea, button, [contenteditable="true"]')?.focus()