import { computed, reactive, ref, watch, type ComputedRef, type Ref, } from 'vue' import type { PillNavItem } from '../types' import { createBioTemplatePresetCollectionFromControls, getBioTemplatePresetInfo, type BioTemplateControlValues, type BioTemplatePresetEntry, type TemplateCollectionEnvelope, type TemplatePresetId, } from '../templates' import type { ControlWorkspaceOptions, ControlWorkspaceFormBinding, ControlWorkspacePillNavBinding, ControlWorkspaceSidebarBinding, ControlWorkspaceTopBarBinding, ControlWorkspaceTopBarSettingsBinding, UseControlSchemaReturn, } from './controlSchemaTypes' import { useTemplateCollection, type UseTemplateCollectionOptions, type UseTemplateCollectionReturn, } from './useTemplateCollection' import { useBioTemplateWorkspace, type BioTemplateRendererBinding, type UseBioTemplateWorkspaceReturn, } from './useBioTemplateWorkspace' import { liftWorkspaceComponentBindings, } from './flattenWorkspaceBindings' import type { BioTemplateComponentBinding, BioTemplateComponentBindingsById, BioTemplateComponentImport, BioTemplateComponentPropsByComponent, BioTemplateComponentPropsBinding, BioTemplateComponentPropsById, BioTemplateComponentSnippet, BioTemplateComponentUsage, } from './useBioTemplateComponents' import type { BioTemplateControlSchema, } from './useBioTemplateControls' export interface UseBioTemplatePresetWorkspaceOptions extends UseTemplateCollectionOptions { /** Optional schema-rendering options for the generated FormBuilder/AppSidebar controls. */ controlOptions?: ControlWorkspaceOptions /** Initial control values for the generated preset. Merged over controlOptions.initialValues. */ initialValues?: BioTemplateControlValues } export interface UseBioTemplatePresetWorkspaceReturn { presetId: TemplatePresetId presetInfo: ComputedRef presetLabel: ComputedRef collection: Ref templates: UseTemplateCollectionReturn['templates'] templateIds: UseTemplateCollectionReturn['templateIds'] currentExperimentId: UseTemplateCollectionReturn['currentExperimentId'] hasCurrentExperiment: UseTemplateCollectionReturn['hasCurrentExperiment'] saving: UseTemplateCollectionReturn['isSaving'] error: UseTemplateCollectionReturn['error'] lastSavedAt: UseTemplateCollectionReturn['lastSavedAt'] controls: UseControlSchemaReturn controlSchema: BioTemplateControlSchema controlValues: Ref controlViewIds: ComputedRef controlViewItems: ComputedRef activeControlView: Ref form: ControlWorkspaceFormBinding sidebar: ControlWorkspaceSidebarBinding topBar: ComputedRef pillNav: ControlWorkspacePillNavBinding topBarSettings: ControlWorkspaceTopBarSettingsBinding bindings: BioTemplatePresetWorkspaceBindings renderer: ComputedRef workspace: ComputedRef componentBindings: ComputedRef componentBindingsById: ComputedRef componentImports: ComputedRef componentProps: ComputedRef componentPropsById: ComputedRef componentPropsByComponent: ComputedRef componentSnippets: ComputedRef componentUsage: ComputedRef getComponentProps: UseBioTemplateWorkspaceReturn['getComponentProps'] setActiveControlView: (viewId: string) => void setControlValues: (values: BioTemplateControlValues) => TemplateCollectionEnvelope updateControlValues: (values: Partial) => TemplateCollectionEnvelope applyCollection: (value: TemplateCollectionEnvelope) => TemplateCollectionEnvelope resetToDefaultCollection: () => TemplateCollectionEnvelope save: UseTemplateCollectionReturn['save'] saveCurrent: UseTemplateCollectionReturn['saveCurrent'] } export interface BioTemplatePresetWorkspaceBindings { renderer: ComputedRef form: ControlWorkspaceFormBinding sidebar: ControlWorkspaceSidebarBinding topBar: ComputedRef topBarSettings: ControlWorkspaceTopBarSettingsBinding pillNav: ControlWorkspacePillNavBinding componentBindings: ComputedRef componentBindingsById: ComputedRef componentProps: ComputedRef componentPropsById: ComputedRef componentPropsByComponent: ComputedRef componentSnippets: ComputedRef componentUsage: ComputedRef getComponentProps: UseBioTemplateWorkspaceReturn['getComponentProps'] } /** * Create a complete editable WellPlate/DoseCalculator workspace for dose design from a built-in biology template preset with current-experiment save wiring. * * @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */ export function useBioTemplatePresetWorkspace( presetId: TemplatePresetId, options: UseBioTemplatePresetWorkspaceOptions = {}, ): UseBioTemplatePresetWorkspaceReturn { const { controlOptions, initialValues, ...collectionOptions } = options const resolvedControlOptions = mergeInitialValues(controlOptions, initialValues) const presetWorkspace = useBioTemplateWorkspace(presetId, resolvedControlOptions) const presetInfo = computed(() => getBioTemplatePresetInfo(presetId)) const presetLabel = computed(() => presetInfo.value?.label ?? humanizePresetId(presetId)) const controls = presetWorkspace.controls const controlValues = ref({ ...controls.initialValues }) const controlViewIds = computed(() => controls.viewIds) const controlViewItems = computed(() => controls.viewItems) const activeControlView = ref(controls.defaultView) const templateCollection = useTemplateCollection({ ...collectionOptions, initial: createDefaultCollection, }) const collection = templateCollection.templateCollection const workspace = computed(() => useBioTemplateWorkspace(collection.value, resolvedControlOptions)) const renderer = computed(() => ({ target: collection.value })) const { componentBindings, componentBindingsById, componentImports, componentProps, componentPropsById, componentPropsByComponent, componentSnippets, componentUsage, getComponentProps, } = liftWorkspaceComponentBindings(workspace) const form = reactive({ ...controls.form, modelValue: controlValues.value, 'onUpdate:modelValue': setControlValues, }) as ControlWorkspaceFormBinding const sidebar = reactive({ ...controls.sidebar, activeView: activeControlView.value, modelValue: controlValues.value, 'onUpdate:modelValue': setControlValues, values: controlValues.value, 'onUpdate:values': setControlValues, }) as ControlWorkspaceSidebarBinding const pillNav = reactive({ items: controls.viewItems, currentItemId: activeControlView.value, onSelect: (item: { id: string }) => setActiveControlView(item.id), }) as ControlWorkspacePillNavBinding const topBarSettings = reactive({ showSettings: true, settingsConfig: { ...controls.topBarSettingsConfig, values: controlValues.value, }, onSettingsValuesChange: setControlValues, }) as ControlWorkspaceTopBarSettingsBinding const topBarProps = computed(() => ({ pillNav: pillNav.items, currentPillId: pillNav.currentItemId, onPillSelect: pillNav.onSelect, ...topBarSettings, })) const bindings: BioTemplatePresetWorkspaceBindings = { renderer, form, sidebar, topBar: topBarProps, topBarSettings, pillNav, componentBindings, componentBindingsById, componentProps, componentPropsById, componentPropsByComponent, componentSnippets, componentUsage, getComponentProps, } function createDefaultCollection(): TemplateCollectionEnvelope { return createBioTemplatePresetCollectionFromControls(presetId, controls.initialValues) } watch( controlValues, (values) => { syncControlValueBindings(values) templateCollection.apply(createBioTemplatePresetCollectionFromControls(presetId, values)) }, { deep: true }, ) watch(activeControlView, (viewId) => { sidebar.activeView = viewId pillNav.currentItemId = viewId }, { flush: 'sync' }) watch(() => sidebar.activeView, setActiveControlView, { flush: 'sync' }) watch(() => pillNav.currentItemId, setActiveControlView, { flush: 'sync' }) function applyCollection(value: TemplateCollectionEnvelope): TemplateCollectionEnvelope { return templateCollection.apply(value) } function setActiveControlView(viewId: string): void { if (!controlViewIds.value.includes(viewId)) return activeControlView.value = viewId } function setControlValues(values: BioTemplateControlValues): TemplateCollectionEnvelope { const nextValues = { ...values } controlValues.value = nextValues syncControlValueBindings(nextValues) return templateCollection.apply(createBioTemplatePresetCollectionFromControls(presetId, nextValues)) } function updateControlValues(values: Partial): TemplateCollectionEnvelope { return setControlValues({ ...controlValues.value, ...values, }) } function resetToDefaultCollection(): TemplateCollectionEnvelope { return setControlValues({ ...controls.initialValues }) } function syncControlValueBindings(values: BioTemplateControlValues): void { form.modelValue = values sidebar.modelValue = values sidebar.values = values topBarSettings.settingsConfig = { ...topBarSettings.settingsConfig, values, } } return { presetId, presetInfo, presetLabel, collection, templates: templateCollection.templates, templateIds: templateCollection.templateIds, currentExperimentId: templateCollection.currentExperimentId, hasCurrentExperiment: templateCollection.hasCurrentExperiment, saving: templateCollection.isSaving, error: templateCollection.error, lastSavedAt: templateCollection.lastSavedAt, controls, controlSchema: controls.controls, controlValues, controlViewIds, controlViewItems, activeControlView, form, sidebar, topBar: topBarProps, pillNav, topBarSettings, bindings, renderer, workspace, componentBindings, componentBindingsById, componentImports, componentProps, componentPropsById, componentPropsByComponent, componentSnippets, componentUsage, getComponentProps, setActiveControlView, setControlValues, updateControlValues, applyCollection, resetToDefaultCollection, save: templateCollection.save, saveCurrent: templateCollection.saveCurrent, } } function humanizePresetId(value: string): string { return value .replace(/[-_]+/g, ' ') .replace(/\b\w/g, match => match.toUpperCase()) } function mergeInitialValues( controlOptions: ControlWorkspaceOptions | undefined, initialValues: BioTemplateControlValues | undefined, ): ControlWorkspaceOptions | undefined { if (initialValues === undefined) return controlOptions return { ...(controlOptions ?? {}), initialValues: { ...(controlOptions?.initialValues ?? {}), ...initialValues, }, } }