@morscherlab/mint-sdk 1.0.0-rc.9 → 1.0.1
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/BaseModal-B9UA8Y_I.js +165 -0
- package/dist/BaseModal-B9UA8Y_I.js.map +1 -0
- package/dist/BaseSelect-DksaKYq_.js +176 -0
- package/dist/BaseSelect-DksaKYq_.js.map +1 -0
- package/dist/ExperimentPopover-CCYB1oWp.js +361 -0
- package/dist/ExperimentPopover-CCYB1oWp.js.map +1 -0
- package/dist/ExperimentPopover-D0bg_fqM.js +3 -0
- package/dist/ExperimentSelectorModal-B_kPbXcg.js +4 -0
- package/dist/ExperimentSelectorModal-wm7yUdAr.js +720 -0
- package/dist/ExperimentSelectorModal-wm7yUdAr.js.map +1 -0
- package/dist/SettingsModal-L7Ejny45.js +5 -0
- package/dist/SettingsModal-LEKI6Ebl.js +521 -0
- package/dist/SettingsModal-LEKI6Ebl.js.map +1 -0
- package/dist/{auth-BulIv_km.js → auth-D9q2GIcv.js} +3 -80
- package/dist/auth-D9q2GIcv.js.map +1 -0
- package/dist/components/DataFrame.vue.d.ts +3 -0
- package/dist/components/ExperimentDataViewer.vue.d.ts +2 -0
- package/dist/components/PluginWorkspaceView.vue.d.ts +2 -2
- package/dist/components/index.js +7 -2
- package/dist/{components-DtX3LDLq.js → components-CdjRzHI2.js} +533 -2025
- package/dist/components-CdjRzHI2.js.map +1 -0
- package/dist/composables/index.js +9 -3
- package/dist/composables/usePluginClient.d.ts +2 -1
- package/dist/{composables-wNt7VtkF.js → composables-DJgqPrlR.js} +7 -12
- package/dist/{composables-wNt7VtkF.js.map → composables-DJgqPrlR.js.map} +1 -1
- package/dist/experiment-utils-hGXMHlAc.js +109 -0
- package/dist/experiment-utils-hGXMHlAc.js.map +1 -0
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/dist/install.js +7 -2
- package/dist/install.js.map +1 -1
- package/dist/permissions.js +81 -0
- package/dist/permissions.js.map +1 -0
- package/dist/stores/index.js +1 -1
- package/dist/styles.css +3233 -3185
- package/dist/templates/index.js +3 -1
- package/dist/templates-Do43ZIMb.js +5065 -0
- package/dist/templates-Do43ZIMb.js.map +1 -0
- package/dist/{templates-DSbHJC4v.js → useControlSchema-0n8Bcftq.js} +10 -5335
- package/dist/useControlSchema-0n8Bcftq.js.map +1 -0
- package/dist/useDropdownState-Ben4DnjJ.js +47 -0
- package/dist/useDropdownState-Ben4DnjJ.js.map +1 -0
- package/dist/useEventListener-CfVkP9Xz.js +57 -0
- package/dist/useEventListener-CfVkP9Xz.js.map +1 -0
- package/dist/useExperimentSelector-BpZklTbV.js +469 -0
- package/dist/useExperimentSelector-BpZklTbV.js.map +1 -0
- package/dist/useFormBuilder-COfYWDuC.js +729 -0
- package/dist/useFormBuilder-COfYWDuC.js.map +1 -0
- package/dist/{useProtocolTemplates-DwBhEPPU.js → useProtocolTemplates-TUQO_F3n.js} +8 -1298
- package/dist/useProtocolTemplates-TUQO_F3n.js.map +1 -0
- package/dist/utils/pluginIcon.d.ts +29 -2
- package/package.json +5 -1
- package/src/__tests__/components/DataFrame.test.ts +37 -0
- package/src/__tests__/components/PluginIcon.test.ts +77 -0
- package/src/__tests__/composables/usePluginClient.test.ts +11 -10
- package/src/components/AppTopBar.vue +7 -6
- package/src/components/DataFrame.vue +27 -2
- package/src/components/ExperimentDataViewer.vue +5 -1
- package/src/components/PluginIcon.story.vue +31 -1
- package/src/components/PluginIcon.vue +94 -4
- package/src/composables/usePluginClient.ts +3 -12
- package/src/styles/components/dataframe.css +26 -0
- package/src/styles/components/plugin-icon.css +5 -0
- package/src/utils/pluginIcon.ts +159 -2
- package/dist/auth-BulIv_km.js.map +0 -1
- package/dist/components-DtX3LDLq.js.map +0 -1
- package/dist/templates-DSbHJC4v.js.map +0 -1
- package/dist/useProtocolTemplates-DwBhEPPU.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useProtocolTemplates-DwBhEPPU.js","names":[],"sources":["../src/composables/useSortedItems.ts","../src/composables/useTextSearch.ts","../src/composables/useToast.ts","../src/composables/useTheme.ts","../src/composables/useFormValidation.ts","../src/composables/useForm.ts","../src/composables/formBuilderSchema.ts","../src/composables/useFormBuilder.ts","../src/composables/usePlatformContext.ts","../src/composables/experiment-utils.ts","../src/composables/useApi.ts","../src/composables/useDebouncedWatch.ts","../src/composables/useRequestSyncState.ts","../src/composables/useExperimentSelector.ts","../src/composables/useAppExperiment.ts","../src/composables/useDoseCalculator.ts","../src/composables/useWellPlateEditor.ts","../src/composables/autoGroup/tokenize.ts","../src/composables/autoGroup/classKey.ts","../src/composables/autoGroup/vocab.json","../src/composables/autoGroup/classify.ts","../src/composables/autoGroup/columns.ts","../src/composables/autoGroup/cooccurrence.ts","../src/composables/autoGroup/colors.ts","../src/composables/autoGroup/compose.ts","../src/composables/autoGroup/fingerprint.ts","../src/composables/autoGroup/template.ts","../src/composables/autoGroup/replicatePreGroup.ts","../src/composables/experimentDesignData.ts","../src/composables/autoGroup/csv-shim.ts","../src/composables/useAutoGroup.ts","../src/utils/color.ts","../src/composables/useSampleGroups.ts","../src/composables/useExpansionSet.ts","../src/composables/platformContextHelpers.ts","../src/composables/useExperimentData.ts","../src/composables/useExperimentSamples.ts","../src/composables/useScheduleDrag.ts","../src/composables/useExperimentSave.ts","../src/composables/useTemplateCollection.ts","../src/composables/useBioTemplateControls.ts","../src/composables/useBioTemplateComponents.ts","../src/composables/useBioTemplateWorkspace.ts","../src/composables/useBioTemplatePackWorkspace.ts","../src/composables/useBioTemplatePresetWorkspace.ts","../src/composables/useRackEditor.ts","../src/composables/useGroupAssignment.ts","../src/composables/useReagentSeries.ts","../src/composables/protocolTemplateCatalog.ts","../src/composables/useProtocolTemplates.ts"],"sourcesContent":["import { computed, toValue, type ComputedRef, type Ref } from 'vue'\n\nexport type SortedItemsSource<T> =\n | T\n | Ref<T>\n | ComputedRef<T>\n | (() => T)\n\nexport type SortOrder = 'asc' | 'desc'\n\nexport interface SortDescriptor<TKey extends string = string> {\n key: TKey\n direction?: SortOrder | null\n}\n\nexport interface CompareSortValuesOptions {\n caseSensitive?: boolean\n}\n\nexport interface SortComparatorContext<TItem, TKey extends string = string> {\n key: TKey\n direction: SortOrder\n a: TItem\n b: TItem\n}\n\nexport type SortComparator<TItem, TKey extends string = string> = (\n aValue: unknown,\n bValue: unknown,\n context: SortComparatorContext<TItem, TKey>,\n) => number\n\nexport interface UseSortedItemsOptions<TItem, TKey extends string = string> {\n items: SortedItemsSource<readonly TItem[]>\n sort: SortedItemsSource<SortDescriptor<TKey> | null | undefined>\n enabled?: SortedItemsSource<boolean | null | undefined>\n caseSensitive?: SortedItemsSource<boolean | null | undefined>\n getValue: (item: TItem, key: TKey) => unknown\n compare?: SortComparator<TItem, TKey>\n}\n\nexport interface UseSortedItemsReturn<TItem, TKey extends string = string> {\n sort: ComputedRef<SortDescriptor<TKey> | null | undefined>\n sortedItems: ComputedRef<TItem[]>\n}\n\n/** Shared sorting for SDK tables and lists with stable empty-value handling. */\nexport function useSortedItems<TItem, TKey extends string = string>(\n options: UseSortedItemsOptions<TItem, TKey>,\n): UseSortedItemsReturn<TItem, TKey> {\n const sort = computed(() => toValue(options.sort))\n const enabled = computed(() => toValue(options.enabled) ?? true)\n\n const sortedItems = computed(() => {\n const items = [...toValue(options.items)]\n const activeSort = sort.value\n\n if (!enabled.value || !activeSort?.key || !activeSort.direction) return items\n\n const direction = activeSort.direction\n const caseSensitive = toValue(options.caseSensitive) ?? true\n\n return items.sort((a, b) => {\n const aValue = options.getValue(a, activeSort.key)\n const bValue = options.getValue(b, activeSort.key)\n\n if (options.compare) {\n return options.compare(aValue, bValue, {\n key: activeSort.key,\n direction,\n a,\n b,\n })\n }\n\n return compareSortValues(aValue, bValue, direction, { caseSensitive })\n })\n })\n\n return {\n sort,\n sortedItems,\n }\n}\n\nexport function compareSortValues(\n aValue: unknown,\n bValue: unknown,\n direction: SortOrder = 'asc',\n options: CompareSortValuesOptions = {},\n): number {\n const aEmpty = aValue === null || aValue === undefined\n const bEmpty = bValue === null || bValue === undefined\n\n if (aEmpty && bEmpty) return 0\n if (aEmpty) return 1\n if (bEmpty) return -1\n\n const aComparable = toComparableSortValue(aValue, options)\n const bComparable = toComparableSortValue(bValue, options)\n\n let comparison = 0\n if (typeof aComparable === 'number' && typeof bComparable === 'number') {\n comparison = aComparable - bComparable\n } else {\n comparison = String(aComparable).localeCompare(String(bComparable))\n }\n\n return direction === 'asc' ? comparison : -comparison\n}\n\nfunction toComparableSortValue(value: unknown, options: CompareSortValuesOptions): unknown {\n if (value instanceof Date) return value.getTime()\n if (typeof value === 'string' && options.caseSensitive === false) {\n return value.toLowerCase()\n }\n return value\n}\n","import { computed, toValue, type ComputedRef, type Ref } from 'vue'\n\nexport type TextSearchSource<T> =\n | T\n | Ref<T>\n | ComputedRef<T>\n | (() => T)\n\nexport type TextSearchCandidate = unknown | readonly unknown[]\n\nexport interface UseTextSearchOptions<TItem> {\n items: TextSearchSource<readonly TItem[]>\n query: TextSearchSource<string | null | undefined>\n enabled?: TextSearchSource<boolean | null | undefined>\n getText: (item: TItem) => TextSearchCandidate\n}\n\nexport interface UseTextSearchReturn<TItem> {\n query: ComputedRef<string>\n filteredItems: ComputedRef<TItem[]>\n matches: (item: TItem, query?: string | null | undefined) => boolean\n}\n\n/** Shared text-search filtering for tables, lists, selectors, and chip inputs. */\nexport function useTextSearch<TItem>(options: UseTextSearchOptions<TItem>): UseTextSearchReturn<TItem> {\n const query = computed(() => normalizeSearchQuery(toValue(options.query)))\n const enabled = computed(() => toValue(options.enabled) ?? true)\n\n function matches(item: TItem, overrideQuery?: string | null | undefined): boolean {\n const activeQuery = overrideQuery === undefined ? query.value : normalizeSearchQuery(overrideQuery)\n if (!activeQuery) return true\n return candidateMatchesSearch(options.getText(item), activeQuery)\n }\n\n const filteredItems = computed(() => {\n const items = [...toValue(options.items)]\n if (!enabled.value || !query.value) return items\n return items.filter(item => matches(item))\n })\n\n return {\n query,\n filteredItems,\n matches,\n }\n}\n\nexport function normalizeSearchQuery(value: string | null | undefined): string {\n return (value ?? '').trim().toLowerCase()\n}\n\nexport function candidateMatchesSearch(candidate: TextSearchCandidate, query: string): boolean {\n const normalizedQuery = normalizeSearchQuery(query)\n if (!normalizedQuery) return true\n const values = Array.isArray(candidate) ? candidate : [candidate]\n return values.some((value) => {\n if (value === null || value === undefined) return false\n return String(value).toLowerCase().includes(normalizedQuery)\n })\n}\n","import { ref } from 'vue'\nimport type { Toast } from '../types'\n\nconst toasts = ref<Toast[]>([])\nlet nextId = 0\n\n/** Reactive global toast queue with success/error/warning/info variants and auto-dismiss timers. */\nexport function useToast() {\n function show(message: string, type: Toast['type'] = 'success', duration = 3500) {\n const id = nextId++\n toasts.value.push({ id, message, type, duration })\n setTimeout(() => dismiss(id), duration)\n }\n\n function success(message: string, duration = 3500) {\n show(message, 'success', duration)\n }\n\n function error(message: string, duration = 5000) {\n show(message, 'error', duration)\n }\n\n function warning(message: string, duration = 4000) {\n show(message, 'warning', duration)\n }\n\n function info(message: string, duration = 3500) {\n show(message, 'info', duration)\n }\n\n function dismiss(id: number) {\n toasts.value = toasts.value.filter(t => t.id !== id)\n }\n\n function clear() {\n toasts.value = []\n }\n\n return { toasts, show, success, error, warning, info, dismiss, clear }\n}\n","import { computed, type Ref } from 'vue'\nimport { useSettingsStore } from '../stores/settings'\n\nexport interface UseThemeReturn {\n isDark: Ref<boolean>\n toggleTheme: () => void\n setTheme: (theme: 'light' | 'dark') => void\n}\n\n/** Reads and toggles the active theme (light/dark/system) with reactivity to OS preference changes. */\nexport function useTheme(): UseThemeReturn {\n const settings = useSettingsStore()\n\n // Load persisted theme from localStorage (idempotent — safe to call multiple times)\n settings.initialize()\n\n const isDark = computed(() => settings.isDark())\n\n function toggleTheme() {\n settings.theme = isDark.value ? 'light' : 'dark'\n }\n\n function setTheme(theme: 'light' | 'dark') {\n settings.theme = theme\n }\n\n return {\n isDark,\n toggleTheme,\n setTheme,\n }\n}\n","/**\n * Validation rule function type.\n * Returns error message string if invalid, undefined/null if valid.\n */\nexport type ValidationRule<T = unknown> = (value: T, formData: Record<string, unknown>) => string | undefined | null\n\n/**\n * Field validation rules configuration.\n */\nexport interface FieldRules<T = unknown> {\n required?: boolean | string\n minLength?: number | { value: number; message: string }\n maxLength?: number | { value: number; message: string }\n min?: number | { value: number; message: string }\n max?: number | { value: number; message: string }\n pattern?: RegExp | { value: RegExp; message: string }\n email?: boolean | string\n custom?: ValidationRule<T> | ValidationRule<T>[]\n}\n\nconst validators = {\n required: (value: unknown, message = 'This field is required'): string | null => {\n if (value === null || value === undefined || value === '') {\n return message\n }\n if (Array.isArray(value) && value.length === 0) {\n return message\n }\n return null\n },\n\n minLength: (value: unknown, min: number, message?: string): string | null => {\n if (typeof value !== 'string') return null\n if (value.length < min) {\n return message || `Must be at least ${min} characters`\n }\n return null\n },\n\n maxLength: (value: unknown, max: number, message?: string): string | null => {\n if (typeof value !== 'string') return null\n if (value.length > max) {\n return message || `Must be at most ${max} characters`\n }\n return null\n },\n\n min: (value: unknown, min: number, message?: string): string | null => {\n if (typeof value !== 'number') return null\n if (value < min) {\n return message || `Must be at least ${min}`\n }\n return null\n },\n\n max: (value: unknown, max: number, message?: string): string | null => {\n if (typeof value !== 'number') return null\n if (value > max) {\n return message || `Must be at most ${max}`\n }\n return null\n },\n\n pattern: (value: unknown, pattern: RegExp, message?: string): string | null => {\n if (typeof value !== 'string') return null\n if (!pattern.test(value)) {\n return message || 'Invalid format'\n }\n return null\n },\n\n email: (value: unknown, message = 'Invalid email address'): string | null => {\n if (typeof value !== 'string' || !value) return null\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n if (!emailRegex.test(value)) {\n return message\n }\n return null\n },\n}\n\nexport function validateFieldValue(\n value: unknown,\n fieldRules: FieldRules | undefined,\n formData: Record<string, unknown>,\n): string | null {\n if (!fieldRules) return null\n\n if (fieldRules.required) {\n const message = typeof fieldRules.required === 'string' ? fieldRules.required : undefined\n const error = validators.required(value, message)\n if (error) return error\n }\n\n if (value === null || value === undefined || value === '') {\n return null\n }\n\n if (fieldRules.minLength !== undefined) {\n const config = typeof fieldRules.minLength === 'number'\n ? { value: fieldRules.minLength, message: undefined }\n : fieldRules.minLength\n const error = validators.minLength(value, config.value, config.message)\n if (error) return error\n }\n\n if (fieldRules.maxLength !== undefined) {\n const config = typeof fieldRules.maxLength === 'number'\n ? { value: fieldRules.maxLength, message: undefined }\n : fieldRules.maxLength\n const error = validators.maxLength(value, config.value, config.message)\n if (error) return error\n }\n\n if (fieldRules.min !== undefined) {\n const config = typeof fieldRules.min === 'number'\n ? { value: fieldRules.min, message: undefined }\n : fieldRules.min\n const error = validators.min(value, config.value, config.message)\n if (error) return error\n }\n\n if (fieldRules.max !== undefined) {\n const config = typeof fieldRules.max === 'number'\n ? { value: fieldRules.max, message: undefined }\n : fieldRules.max\n const error = validators.max(value, config.value, config.message)\n if (error) return error\n }\n\n if (fieldRules.pattern !== undefined) {\n const config = fieldRules.pattern instanceof RegExp\n ? { value: fieldRules.pattern, message: undefined }\n : fieldRules.pattern\n const error = validators.pattern(value, config.value, config.message)\n if (error) return error\n }\n\n if (fieldRules.email) {\n const message = typeof fieldRules.email === 'string' ? fieldRules.email : undefined\n const error = validators.email(value, message)\n if (error) return error\n }\n\n if (fieldRules.custom) {\n const customRules = Array.isArray(fieldRules.custom) ? fieldRules.custom : [fieldRules.custom]\n for (const rule of customRules) {\n const error = rule(value, formData)\n if (error) return error\n }\n }\n\n return null\n}\n","import { ref, reactive, computed, toRaw, watch, type Ref } from 'vue'\nimport { validateFieldValue, type FieldRules } from './useFormValidation'\n\nexport type { FieldRules, ValidationRule } from './useFormValidation'\n\n/**\n * Field validation rules configuration.\n */\nexport interface FieldState {\n value: unknown\n error: string | null\n touched: boolean\n dirty: boolean\n}\n\n/**\n * Form state and methods.\n */\nexport interface UseFormReturn<T extends Record<string, unknown>> {\n // Form data (reactive)\n data: T\n\n // Field errors\n errors: Record<string, string | null>\n\n // Field touched state\n touched: Record<string, boolean>\n\n // Field dirty state (value changed from initial)\n dirty: Record<string, boolean>\n\n // Overall form state\n isValid: Ref<boolean>\n isDirty: Ref<boolean>\n isSubmitting: Ref<boolean>\n\n // Methods\n setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void\n setFieldError: (field: string, error: string | null) => void\n setFieldTouched: (field: string, touched?: boolean) => void\n validateField: (field: string) => boolean\n validate: () => boolean\n reset: (values?: Partial<T>) => void\n /** Replace the full form state and treat the provided values as the new clean baseline. */\n replaceState: (values: T) => void\n handleSubmit: (onSubmit: (data: T) => Promise<void> | void) => (e?: Event) => Promise<void>\n getFieldProps: <K extends keyof T>(field: K) => {\n modelValue: T[K]\n 'onUpdate:modelValue': (value: T[K]) => void\n onBlur: () => void\n error: string | null\n }\n}\n\n/**\n * Form state management composable with validation.\n *\n * @param initialValues - Initial form values\n * @param rules - Validation rules for each field\n *\n * @example\n * ```typescript\n * const { data, errors, isValid, handleSubmit, getFieldProps } = useForm(\n * { email: '', password: '' },\n * {\n * email: { required: true, email: true },\n * password: { required: true, minLength: 8 },\n * }\n * )\n *\n * // In template\n * <BaseInput v-bind=\"getFieldProps('email')\" label=\"Email\" />\n * <BaseInput v-bind=\"getFieldProps('password')\" type=\"password\" label=\"Password\" />\n * <BaseButton @click=\"handleSubmit(onSubmit)\" :disabled=\"!isValid\">Submit</BaseButton>\n * ```\n */\n/** Reactive form state with field-level validation, dirty tracking, and submit handling. */\nexport function useForm<T extends Record<string, unknown>>(\n initialValues: T,\n rules: Partial<Record<keyof T, FieldRules>> = {}\n): UseFormReturn<T> {\n const cloneableInitialValues = deepToRaw(initialValues) as T\n\n // Deep copy initial values so nested objects are not shared\n let _initialValues = structuredClone(cloneableInitialValues)\n\n // Reactive form data\n const data = reactive(structuredClone(cloneableInitialValues)) as T\n\n // Field state - use simple Record types for better TS compatibility\n const errors = reactive<Record<string, string | null>>(\n Object.keys(initialValues).reduce((acc, key) => {\n acc[key] = null\n return acc\n }, {} as Record<string, string | null>)\n )\n\n const touched = reactive<Record<string, boolean>>(\n Object.keys(initialValues).reduce((acc, key) => {\n acc[key] = false\n return acc\n }, {} as Record<string, boolean>)\n )\n\n const dirty = reactive<Record<string, boolean>>(\n Object.keys(initialValues).reduce((acc, key) => {\n acc[key] = false\n return acc\n }, {} as Record<string, boolean>)\n )\n\n const isSubmitting = ref(false)\n\n // Watch data changes to track dirty state\n watch(\n () => ({ ...data }),\n (newData) => {\n for (const key of Object.keys(newData)) {\n dirty[key] = newData[key as keyof T] !== _initialValues[key as keyof T]\n }\n },\n { deep: true }\n )\n\n // Validate a single field\n function validateField(field: string): boolean {\n const value = data[field as keyof T]\n const fieldRules = rules[field as keyof T]\n const error = validateFieldValue(value, fieldRules, data as Record<string, unknown>)\n errors[field] = error\n return error === null\n }\n\n // Validate all fields\n function validate(): boolean {\n let isAllValid = true\n for (const field of Object.keys(data)) {\n if (!validateField(field)) {\n isAllValid = false\n }\n }\n return isAllValid\n }\n\n // Computed overall validity\n const isValid = computed(() => {\n return Object.values(errors).every(error => error === null)\n })\n\n // Computed overall dirty state\n const isDirty = computed(() => {\n return Object.values(dirty).some(d => d)\n })\n\n // Set field value\n function setFieldValue<K extends keyof T>(field: K, value: T[K]): void {\n ;(data as Record<string, unknown>)[field as string] = value\n if (touched[field as string]) {\n validateField(field as string)\n }\n }\n\n // Set field error\n function setFieldError(field: string, error: string | null): void {\n errors[field] = error\n }\n\n // Set field touched\n function setFieldTouched(field: string, isTouched = true): void {\n touched[field] = isTouched\n if (isTouched) {\n validateField(field)\n }\n }\n\n // Reset form\n function reset(values?: Partial<T>): void {\n const resetValues = values ? { ..._initialValues, ...values } : _initialValues\n for (const key of Object.keys(data)) {\n ;(data as Record<string, unknown>)[key] = structuredClone(deepToRaw(resetValues[key as keyof T]))\n errors[key] = null\n touched[key] = false\n dirty[key] = false\n }\n }\n\n function replaceState(values: T): void {\n const nextValues = structuredClone(deepToRaw(values)) as T\n _initialValues = structuredClone(nextValues)\n\n for (const key of Object.keys(data)) {\n if (key in nextValues) continue\n delete (data as Record<string, unknown>)[key]\n delete errors[key]\n delete touched[key]\n delete dirty[key]\n }\n\n for (const key of Object.keys(nextValues)) {\n ;(data as Record<string, unknown>)[key] = structuredClone(deepToRaw(nextValues[key as keyof T]))\n errors[key] = null\n touched[key] = false\n dirty[key] = false\n }\n }\n\n // Handle submit\n function handleSubmit(onSubmit: (data: T) => Promise<void> | void) {\n return async (e?: Event): Promise<void> => {\n e?.preventDefault()\n\n // Mark all fields as touched\n for (const field of Object.keys(data)) {\n touched[field] = true\n }\n\n if (!validate()) {\n return\n }\n\n isSubmitting.value = true\n try {\n await onSubmit(data)\n } finally {\n isSubmitting.value = false\n }\n }\n }\n\n // Get props for a field (for v-bind)\n function getFieldProps<K extends keyof T>(field: K) {\n const fieldStr = field as string\n return {\n modelValue: data[field],\n 'onUpdate:modelValue': (value: T[K]) => setFieldValue(field, value),\n onBlur: () => setFieldTouched(fieldStr),\n error: touched[fieldStr] ? errors[fieldStr] : null,\n }\n }\n\n return {\n data,\n errors,\n touched,\n dirty,\n isValid,\n isDirty,\n isSubmitting,\n setFieldValue,\n setFieldError,\n setFieldTouched,\n validateField,\n validate,\n reset,\n replaceState,\n handleSubmit,\n getFieldProps,\n }\n}\n\nfunction deepToRaw(value: unknown): unknown {\n const raw = toRaw(value)\n\n if (Array.isArray(raw)) {\n return raw.map(item => deepToRaw(item))\n }\n\n if (isPlainRecord(raw)) {\n return Object.fromEntries(\n Object.entries(raw).map(([key, item]) => [key, deepToRaw(item)])\n )\n }\n\n return raw\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return Object.prototype.toString.call(value) === '[object Object]'\n}\n","import { getTypeDefault } from './formBuilderRegistry'\nimport type { FieldRules } from './useForm'\nimport type {\n FieldCondition,\n FieldValidation,\n FormEnhancements,\n FormFieldSchema,\n FormSchema,\n FormSectionSchema,\n} from '../types/form-builder'\n\n/**\n * Evaluate a JSON-serializable field condition against the current form data.\n *\n * Supports logical operators (`and`, `or`, `not`) and comparison operators\n * (`eq`, `neq`, `gt`, `lt`, `gte`, `lte`, `in`, `notIn`, `truthy`, `falsy`,\n * `contains`). Returns `true` if the condition passes.\n */\nexport function evaluateCondition(\n condition: FieldCondition,\n data: Record<string, unknown>,\n): boolean {\n if ('and' in condition) {\n return condition.and.every((c) => evaluateCondition(c, data))\n }\n if ('or' in condition) {\n return condition.or.some((c) => evaluateCondition(c, data))\n }\n if ('not' in condition) {\n return !evaluateCondition(condition.not, data)\n }\n\n const value = data[condition.field]\n\n if ('eq' in condition) return value === condition.eq\n if ('neq' in condition) return value !== condition.neq\n if ('gt' in condition) return typeof value === 'number' && value > condition.gt\n if ('lt' in condition) return typeof value === 'number' && value < condition.lt\n if ('gte' in condition) return typeof value === 'number' && value >= condition.gte\n if ('lte' in condition) return typeof value === 'number' && value <= condition.lte\n if ('in' in condition) return condition.in.includes(value)\n if ('notIn' in condition) return !condition.notIn.includes(value)\n if ('truthy' in condition) return !!value\n if ('falsy' in condition) return !value\n if ('contains' in condition) {\n if (typeof value === 'string') return value.includes(condition.contains)\n if (Array.isArray(value)) return value.includes(condition.contains)\n return false\n }\n\n return true\n}\n\n/** Return all sections across steps (wizard) or directly from a flat schema. */\nexport function collectSections(schema: FormSchema): FormSectionSchema[] {\n return schema.steps ? schema.steps.flatMap((step) => step.sections) : schema.sections\n}\n\n/** Return all field schemas in schema order, flattening sections and steps. */\nexport function flattenFields(schema: FormSchema): FormFieldSchema[] {\n return collectSections(schema).flatMap((section) => section.fields)\n}\n\n/** Convert a JSON-safe `FieldValidation` descriptor to a runtime `FieldRules` object. */\nfunction convertValidation(v: FieldValidation): FieldRules {\n const rules: FieldRules = {}\n\n if (v.required !== undefined) rules.required = v.required\n if (v.minLength !== undefined) rules.minLength = v.minLength\n if (v.maxLength !== undefined) rules.maxLength = v.maxLength\n if (v.min !== undefined) rules.min = v.min\n if (v.max !== undefined) rules.max = v.max\n if (v.email !== undefined) rules.email = v.email\n\n if (v.pattern !== undefined) {\n rules.pattern =\n typeof v.pattern === 'string'\n ? new RegExp(v.pattern)\n : { value: new RegExp(v.pattern.value), message: v.pattern.message }\n }\n\n return rules\n}\n\nexport function buildInitialValues<T extends Record<string, unknown>>(\n fields: readonly FormFieldSchema[],\n initialData?: Partial<T>,\n): Record<string, unknown> {\n const initialValues = {} as Record<string, unknown>\n\n for (const field of fields) {\n const key = field.name\n if (initialData && hasOwnKey(initialData, key)) {\n initialValues[key] = (initialData as Record<string, unknown>)[key]\n } else if (field.defaultValue !== undefined) {\n initialValues[key] = field.defaultValue\n } else {\n initialValues[key] = getTypeDefault(field.type)\n }\n }\n\n return initialValues\n}\n\nexport function buildRules<T extends Record<string, unknown>>(\n fields: readonly FormFieldSchema[],\n enhancements?: FormEnhancements<T>,\n): Partial<Record<string, FieldRules>> {\n const rules: Partial<Record<string, FieldRules>> = {}\n\n for (const field of fields) {\n const base: FieldRules = field.validation ? convertValidation(field.validation) : {}\n const enhancement = enhancements?.fields?.[field.name as keyof T]\n\n const customValidators: Array<(\n value: unknown,\n formData: Record<string, unknown>,\n ) => string | undefined | null> = []\n\n if (enhancement?.validate) {\n const fn = enhancement.validate\n customValidators.push((value, formData) => fn(value, formData as T))\n }\n\n if (customValidators.length > 0) {\n base.custom = customValidators\n }\n\n if (Object.keys(base).length > 0) {\n rules[field.name] = base\n }\n }\n\n return rules\n}\n\nexport function replaceArray<T>(target: T[], values: readonly T[]): void {\n target.splice(0, target.length, ...values)\n}\n\nexport function replaceRecord<TValue>(\n target: Partial<Record<string, TValue>>,\n source: Partial<Record<string, TValue>>,\n): void {\n for (const key of Object.keys(target)) {\n delete target[key]\n }\n Object.assign(target, source)\n}\n\nfunction hasOwnKey(source: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(source, key)\n}\n","import { reactive, ref, computed, shallowRef, watch } from 'vue'\nimport { useForm, type FieldRules, type UseFormReturn } from './useForm'\nimport { getFieldRegistryEntry } from './formBuilderRegistry'\nimport {\n buildInitialValues,\n buildRules,\n collectSections,\n evaluateCondition,\n flattenFields,\n replaceArray,\n replaceRecord,\n} from './formBuilderSchema'\nimport type {\n FormSchema,\n FormFieldSchema,\n FormSectionSchema,\n FormOptionInput,\n FormEnhancements,\n UseFormBuilderReturn,\n} from '../types/form-builder'\n\nexport { evaluateCondition } from './formBuilderSchema'\n\n// ---------------------------------------------------------------------------\n// useFormBuilder\n// ---------------------------------------------------------------------------\n\n/**\n * Drive a `FormSchema` as reactive form state.\n *\n * Builds initial values from schema defaults and `initialData`, derives\n * validation rules from `FieldValidation` descriptors and enhancement\n * validators, evaluates `FieldCondition` expressions for field/section\n * visibility, and wires wizard step navigation when `schema.steps` is set.\n *\n * @param schema - Declarative form or wizard schema.\n * @param initialData - Values that override schema defaults.\n * @param enhancements - TypeScript-only callbacks (dynamic options, validators,\n * submit handler, transform, field-change watcher).\n */\n/** Drives a declarative FormSchema as reactive state with conditional fields, wizard steps, and validation. */\nexport function useFormBuilder<T extends Record<string, unknown> = Record<string, unknown>>(\n schema: FormSchema,\n initialData?: Partial<T>,\n enhancements?: FormEnhancements<T>,\n): UseFormBuilderReturn<T> {\n const currentSchema = shallowRef(schema)\n const fields = reactive<FormFieldSchema[]>(flattenFields(schema))\n const sections = reactive<FormSectionSchema[]>(collectSections(schema))\n const rules = reactive<Partial<Record<string, FieldRules>>>(buildRules(fields, enhancements))\n\n // -- Create form -----------------------------------------------------------\n const form = useForm(\n buildInitialValues<T>(fields, initialData) as T,\n rules as Partial<Record<keyof T, FieldRules>>,\n )\n\n // -- Visibility ------------------------------------------------------------\n function isFieldVisible(name: string): boolean {\n const enhancement = enhancements?.fields?.[name as keyof T]\n if (enhancement?.visible) {\n return enhancement.visible(form.data as T)\n }\n\n const field = fields.find((f) => f.name === name)\n if (field?.condition) {\n return evaluateCondition(field.condition, form.data as Record<string, unknown>)\n }\n\n return true\n }\n\n function isSectionVisible(id: string): boolean {\n const section = sections.find((s) => s.id === id)\n if (section?.condition) {\n return evaluateCondition(section.condition, form.data as Record<string, unknown>)\n }\n return true\n }\n\n // -- Resolved field props --------------------------------------------------\n function getResolvedFieldProps(field: FormFieldSchema): Record<string, unknown> {\n const entry = getFieldRegistryEntry(field.type)\n const formProps = form.getFieldProps(field.name as keyof T)\n\n const merged: Record<string, unknown> = {\n ...entry.defaults,\n ...(field.props ?? {}),\n }\n\n // Dynamic enhancement props\n const enhancement = enhancements?.fields?.[field.name as keyof T]\n if (enhancement?.props) {\n Object.assign(merged, enhancement.props(form.data as T))\n }\n\n // Dynamic options\n const options = getFieldOptions(field.name)\n if (options) {\n merged.options = options\n }\n\n // Form field bindings\n if (entry.vModel) {\n merged.modelValue = formProps.modelValue\n merged['onUpdate:modelValue'] = formProps['onUpdate:modelValue']\n }\n merged.onBlur = formProps.onBlur\n\n // Error as boolean for components that use boolean error prop\n const errorMsg = formProps.error\n if (errorMsg) {\n merged.error = true\n }\n\n // Schema-level props\n if (field.placeholder) merged.placeholder = field.placeholder\n if (field.size) merged.size = field.size\n if (field.disabled) merged.disabled = true\n if (field.readonly) merged.readonly = true\n\n // Radio group needs a name prop\n if (field.type === 'radio' && !merged.name) {\n merged.name = field.name\n }\n\n return merged\n }\n\n function getFieldOptions(name: string): FormOptionInput[] | undefined {\n const enhancement = enhancements?.fields?.[name as keyof T]\n if (enhancement?.options) {\n return enhancement.options(form.data as T)\n }\n\n const field = fields.find((f) => f.name === name)\n if (field?.props?.options) {\n return field.props.options as FormOptionInput[]\n }\n\n return undefined\n }\n\n // -- Wizard state ----------------------------------------------------------\n const currentStep = ref(0)\n\n const isCurrentStepValid = computed(() => {\n if (!currentSchema.value.steps) return form.isValid.value\n\n const step = currentSchema.value.steps[currentStep.value]\n if (!step) return true\n\n for (const section of step.sections) {\n if (!isSectionVisible(section.id)) continue\n for (const field of section.fields) {\n if (!isFieldVisible(field.name)) continue\n if (!form.validateField(field.name)) return false\n }\n }\n return true\n })\n\n function goNext(): boolean {\n if (!currentSchema.value.steps) return false\n\n // Touch and validate all visible fields in current step\n const step = currentSchema.value.steps[currentStep.value]\n if (!step) return false\n\n let valid = true\n for (const section of step.sections) {\n if (!isSectionVisible(section.id)) continue\n for (const field of section.fields) {\n if (!isFieldVisible(field.name)) continue\n form.setFieldTouched(field.name, true)\n if (!form.validateField(field.name)) valid = false\n }\n }\n\n if (!valid) return false\n\n if (currentStep.value < currentSchema.value.steps.length - 1) {\n currentStep.value++\n }\n return true\n }\n\n function goBack(): void {\n if (currentStep.value > 0) {\n currentStep.value--\n }\n }\n\n function goToStep(index: number): void {\n if (!currentSchema.value.steps) return\n if (index >= 0 && index < currentSchema.value.steps.length) {\n currentStep.value = index\n }\n }\n\n // -- Validate (skip hidden fields) -----------------------------------------\n function validate(): boolean {\n let allValid = true\n for (const field of fields) {\n if (!isFieldVisible(field.name)) continue\n form.setFieldTouched(field.name, true)\n if (!form.validateField(field.name)) {\n allValid = false\n }\n }\n return allValid\n }\n\n // -- Submit ----------------------------------------------------------------\n async function submit(): Promise<void> {\n if (!validate()) return\n\n // Build submission data excluding hidden fields\n let submitData = {} as Record<string, unknown>\n for (const field of fields) {\n if (isFieldVisible(field.name)) {\n submitData[field.name] = (form.data as Record<string, unknown>)[field.name]\n }\n }\n\n if (enhancements?.transform) {\n submitData = enhancements.transform(submitData as T) as Record<string, unknown>\n }\n\n if (enhancements?.onSubmit) {\n form.isSubmitting.value = true\n try {\n await enhancements.onSubmit(submitData as T)\n } finally {\n form.isSubmitting.value = false\n }\n }\n }\n\n // -- Reset -----------------------------------------------------------------\n function reset(values?: Partial<T>): void {\n form.reset(values)\n currentStep.value = 0\n }\n\n function updateSchema(nextSchema: FormSchema, nextData?: Partial<T>): void {\n currentSchema.value = nextSchema\n\n const nextFields = flattenFields(nextSchema)\n replaceArray(fields, nextFields)\n replaceArray(sections, collectSections(nextSchema))\n replaceRecord(rules, buildRules(nextFields, enhancements))\n form.replaceState(buildInitialValues<T>(nextFields, nextData) as T)\n currentStep.value = 0\n }\n\n // -- onFieldChange wiring --------------------------------------------------\n if (enhancements?.onFieldChange) {\n const callback = enhancements.onFieldChange\n watch(\n () => ({ ...(form.data as Record<string, unknown>) }),\n (newData, oldData) => {\n if (!oldData) return\n for (const key of Object.keys(newData)) {\n if (newData[key] !== oldData[key]) {\n callback(key as keyof T, newData[key], newData as T)\n }\n }\n },\n { deep: true },\n )\n }\n\n return {\n form: form as UseFormReturn<T>,\n rules: rules as Partial<Record<keyof T, FieldRules>>,\n isFieldVisible,\n isSectionVisible,\n fields,\n getResolvedFieldProps,\n getFieldOptions,\n currentStep,\n isCurrentStepValid,\n goNext,\n goBack,\n goToStep,\n validate,\n reset,\n submit,\n updateSchema,\n }\n}\n","import { ref, computed, onMounted, onUnmounted } from 'vue'\nimport type { PlatformContext, PlatformContextOptions, PlatformEvent } from '../types'\n\nconst DEFAULT_CONTEXT: PlatformContext = {\n isIntegrated: false,\n theme: 'system',\n}\n\nconst platformContext = ref<PlatformContext>({ ...DEFAULT_CONTEXT })\n\n// Track allowed origins for postMessage security.\n// Inferred origins come from platform injection and URL params.\n// Consumer origins and allowAnyOrigin are tracked per composable instance.\nlet inferredOrigins: Set<string> = new Set()\nlet allowedOrigins: Set<string> = new Set()\nlet allowAnyOrigin = false\nlet initialized = false\nlet listenerCount = 0\nlet nextConsumerId = 0\nlet currentHandler: ((event: MessageEvent) => void) | null = null\nconst consumerOrigins: Map<number, Set<string>> = new Map()\nconst allowAnyOriginConsumers: Set<number> = new Set()\n\n/**\n * Derive origin from URL (protocol + host)\n */\nfunction getOriginFromUrl(url: string): string | null {\n try {\n const parsed = new URL(url)\n return parsed.origin\n } catch {\n return null\n }\n}\n\nfunction normalizeAllowedOrigins(origins: string[] | undefined): Set<string> {\n const normalized = new Set<string>()\n if (!origins) {\n return normalized\n }\n\n for (const origin of origins) {\n normalized.add(getOriginFromUrl(origin) || origin)\n }\n return normalized\n}\n\nfunction recomputeOriginPolicy(): void {\n allowedOrigins = new Set(inferredOrigins)\n for (const origins of consumerOrigins.values()) {\n for (const origin of origins) {\n allowedOrigins.add(origin)\n }\n }\n allowAnyOrigin = allowAnyOriginConsumers.size > 0\n}\n\nfunction resetPlatformContextState(): void {\n inferredOrigins = new Set()\n allowedOrigins = new Set()\n allowAnyOrigin = false\n initialized = false\n listenerCount = 0\n currentHandler = null\n consumerOrigins.clear()\n allowAnyOriginConsumers.clear()\n platformContext.value = { ...DEFAULT_CONTEXT }\n}\n\n/**\n * Check if an origin is allowed for postMessage communication\n */\nfunction isOriginAllowed(origin: string): boolean {\n // Development mode: allow any origin (must be explicitly enabled)\n if (allowAnyOrigin) {\n console.warn('[MINT SDK] postMessage origin validation disabled - only use in development')\n return true\n }\n\n // Same origin is always allowed\n if (origin === window.location.origin) {\n return true\n }\n\n // Check against allowed origins list\n return allowedOrigins.has(origin)\n}\n\n/**\n * Platform context composable for plugin integration with MINT Platform.\n *\n * Provides secure communication with the parent platform via postMessage.\n *\n * @param options - Configuration options\n * @param options.allowedOrigins - List of allowed origins for postMessage\n * @param options.allowAnyOrigin - Allow any origin (UNSAFE, development only)\n *\n * @example\n * ```typescript\n * // Basic usage - derives origin from platform injection\n * const { isIntegrated, user, theme } = usePlatformContext()\n *\n * // With explicit allowed origins\n * const { isIntegrated } = usePlatformContext({\n * allowedOrigins: ['https://mint.example.com']\n * })\n *\n * // Development mode (UNSAFE)\n * const { isIntegrated } = usePlatformContext({\n * allowAnyOrigin: import.meta.env.DEV\n * })\n * ```\n */\n/** Connects a plugin to the MINT platform via postMessage, exposing user, theme, and experiment context. */\nexport function usePlatformContext(options: PlatformContextOptions = {}) {\n const consumerId = ++nextConsumerId\n const instanceOrigins = normalizeAllowedOrigins(options.allowedOrigins)\n const instanceAllowAnyOrigin = options.allowAnyOrigin === true\n\n function detectPlatform(): void {\n const detectedOrigins = new Set<string>()\n\n // Check if running under MINT Platform by looking for platform-injected global\n const platformData = (window as unknown as { __MINT_PLATFORM__?: PlatformContext }).__MINT_PLATFORM__\n\n if (platformData) {\n platformContext.value = {\n ...platformData,\n isIntegrated: true,\n }\n\n // Derive platform origin from injected data\n if (platformData.platformOrigin) {\n detectedOrigins.add(platformData.platformOrigin)\n } else if (platformData.platformApiUrl) {\n const origin = getOriginFromUrl(platformData.platformApiUrl)\n if (origin) {\n detectedOrigins.add(origin)\n }\n }\n } else {\n // Check for platform indicator in URL or localStorage\n const urlParams = new URLSearchParams(window.location.search)\n const hasPluginParam = urlParams.has('mint-plugin')\n\n // Try to get platform origin from URL parameter\n const platformOrigin = urlParams.get('mint-origin')\n if (platformOrigin) {\n const origin = getOriginFromUrl(platformOrigin)\n if (origin) {\n detectedOrigins.add(origin)\n }\n }\n\n platformContext.value = {\n isIntegrated: hasPluginParam,\n theme: (() => {\n try {\n const s = localStorage.getItem('mint-settings')\n if (s) return (JSON.parse(s).theme as 'light' | 'dark' | 'system') || 'system'\n } catch { /* ignore */ }\n return 'system'\n })(),\n platformOrigin: platformOrigin || undefined,\n }\n }\n\n inferredOrigins = detectedOrigins\n }\n\n function handlePlatformMessage(event: MessageEvent): void {\n // Only accept messages from parent window (platform)\n if (event.source !== window.parent) return\n\n // Validate origin for security\n if (!isOriginAllowed(event.origin)) {\n console.warn(`[MINT SDK] Rejected postMessage from untrusted origin: ${event.origin}`)\n return\n }\n\n try {\n const platformEvent = event.data as PlatformEvent\n if (!platformEvent.type?.startsWith('mint:')) return\n\n switch (platformEvent.type) {\n case 'mint:theme-changed':\n platformContext.value.theme = platformEvent.payload as 'light' | 'dark' | 'system'\n break\n case 'mint:user-changed':\n platformContext.value.user = platformEvent.payload as PlatformContext['user']\n break\n }\n } catch {\n // Ignore invalid messages\n }\n }\n\n /**\n * Send a message to the parent platform.\n * Uses validated target origin for security.\n */\n function sendToPlatform(event: PlatformEvent): void {\n if (!platformContext.value.isIntegrated || window.parent === window) {\n return\n }\n\n // Determine target origin\n let targetOrigin: string\n\n if (platformContext.value.platformOrigin) {\n // Use explicitly configured platform origin\n targetOrigin = platformContext.value.platformOrigin\n } else if (allowedOrigins.size > 0) {\n // Use first allowed origin (typically the platform)\n targetOrigin = allowedOrigins.values().next().value as string\n } else if (allowAnyOrigin) {\n // Development mode fallback\n targetOrigin = '*'\n console.warn('[MINT SDK] Using wildcard origin for postMessage - only use in development')\n } else {\n // Safety: if no origin is configured, log warning and don't send\n console.warn('[MINT SDK] Cannot send postMessage: no platform origin configured')\n return\n }\n\n window.parent.postMessage(event, targetOrigin)\n }\n\n /**\n * Request navigation to a path in the platform.\n */\n function navigate(path: string): void {\n sendToPlatform({\n type: 'mint:navigate',\n payload: path,\n })\n }\n\n /**\n * Show a notification in the platform.\n */\n function notify(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info'): void {\n sendToPlatform({\n type: 'mint:notification',\n payload: { message, type },\n })\n }\n\n onMounted(() => {\n consumerOrigins.set(consumerId, instanceOrigins)\n if (instanceAllowAnyOrigin) {\n allowAnyOriginConsumers.add(consumerId)\n }\n\n if (!initialized) {\n detectPlatform()\n currentHandler = handlePlatformMessage\n window.addEventListener('message', handlePlatformMessage)\n initialized = true\n }\n\n listenerCount++\n recomputeOriginPolicy()\n })\n\n onUnmounted(() => {\n consumerOrigins.delete(consumerId)\n allowAnyOriginConsumers.delete(consumerId)\n\n listenerCount = Math.max(0, listenerCount - 1)\n if (listenerCount === 0) {\n if (currentHandler) {\n window.removeEventListener('message', currentHandler)\n }\n resetPlatformContextState()\n return\n }\n\n recomputeOriginPolicy()\n })\n\n const isIntegrated = computed(() => platformContext.value.isIntegrated)\n const plugin = computed(() => platformContext.value.plugin)\n const user = computed(() => platformContext.value.user)\n const theme = computed(() => platformContext.value.theme)\n const features = computed(() => platformContext.value.features)\n\n return {\n context: platformContext,\n isIntegrated,\n plugin,\n user,\n theme,\n features,\n navigate,\n notify,\n sendToPlatform,\n }\n}\n","import type { DatePreset, ExperimentStatus, SelectOption, PillVariant } from '../types'\n\ninterface ExperimentCodeSource {\n id?: number | null\n experiment_code?: string | null\n}\n\nexport function formatExperimentDate(dateStr: string): string {\n try {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n } catch {\n return dateStr\n }\n}\n\nexport function datePresetToISO(preset: DatePreset): string {\n const now = new Date()\n const days = preset === 'last_7_days' ? 7 : preset === 'last_30_days' ? 30 : 90\n const d = new Date(now.getTime() - days * 86_400_000)\n return d.toISOString()\n}\n\nexport const EXPERIMENT_STATUS_OPTIONS: SelectOption<string>[] = [\n { value: '', label: 'All statuses' },\n { value: 'planned', label: 'Planned' },\n { value: 'ongoing', label: 'Ongoing' },\n { value: 'completed', label: 'Completed' },\n { value: 'cancelled', label: 'Cancelled' },\n]\n\nexport const EXPERIMENT_STATUS_VARIANT_MAP: Record<ExperimentStatus, PillVariant> = {\n planned: 'default',\n ongoing: 'primary',\n completed: 'success',\n cancelled: 'error',\n}\n\nexport const EXPERIMENT_STATUS_LABELS: Record<ExperimentStatus, string> = {\n planned: 'Planned',\n ongoing: 'Ongoing',\n completed: 'Completed',\n cancelled: 'Cancelled',\n}\n\nexport function formatExperimentStatus(status?: ExperimentStatus | string | null): string {\n if (!status) return ''\n if (status in EXPERIMENT_STATUS_LABELS) {\n return EXPERIMENT_STATUS_LABELS[status as ExperimentStatus]\n }\n const label = String(status).replace(/[-_]+/g, ' ').trim()\n return label ? label.replace(/^\\w/, c => c.toUpperCase()) : ''\n}\n\nexport function getExperimentStatusVariant(status?: ExperimentStatus | string | null): PillVariant {\n if (status && status in EXPERIMENT_STATUS_VARIANT_MAP) {\n return EXPERIMENT_STATUS_VARIANT_MAP[status as ExperimentStatus]\n }\n return 'default'\n}\n\nexport function resolveExperimentCode(experiment: ExperimentCodeSource): string | undefined {\n if (experiment.experiment_code) return experiment.experiment_code\n return experiment.id != null ? `EXP-${experiment.id}` : undefined\n}\n\nexport const DATE_PRESET_OPTIONS: SelectOption<string>[] = [\n { value: '', label: 'Any time' },\n { value: 'last_7_days', label: 'Last 7 days' },\n { value: 'last_30_days', label: 'Last 30 days' },\n { value: 'last_90_days', label: 'Last 90 days' },\n]\n\nexport const SORT_OPTIONS: SelectOption<string>[] = [\n { value: 'created_at:desc', label: 'Newest first' },\n { value: 'created_at:asc', label: 'Oldest first' },\n { value: 'updated_at:desc', label: 'Recently updated' },\n { value: 'name:asc', label: 'Name A\\u2013Z' },\n { value: 'name:desc', label: 'Name Z\\u2013A' },\n]\n","import axios, { type AxiosInstance, type AxiosRequestConfig, type InternalAxiosRequestConfig } from 'axios'\nimport { useSettingsStore } from '../stores/settings'\nimport { useAuthStore } from '../stores/auth'\n\nlet apiClientInstance: AxiosInstance | null = null\nlet interceptorAttached = false\n\ninterface MintAxiosRequestConfig extends AxiosRequestConfig {\n _mintSkipAuth?: boolean\n}\n\ninterface MintInternalAxiosRequestConfig extends InternalAxiosRequestConfig {\n _mintSkipAuth?: boolean\n}\n\ntype MutableHeaders = Record<string, unknown> & {\n has?: (header: string) => boolean\n set?: (header: string, value: string) => void\n delete?: (header: string) => void\n}\n\nfunction joinUrlPath(baseUrl: string, path: string): string {\n if (!path) return baseUrl\n if (path.startsWith('?') || path.startsWith('#')) return `${baseUrl.replace(/\\/+$/, '')}${path}`\n const normalizedBase = baseUrl.replace(/\\/+$/, '')\n const normalizedPath = path.replace(/^\\/+/, '/')\n return `${normalizedBase}${normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`}`\n}\n\nfunction getBasePath(baseUrl: string): string {\n if (!baseUrl) return '/'\n\n try {\n const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n return new URL(baseUrl, origin).pathname.replace(/\\/+$/, '') || '/'\n } catch {\n const path = baseUrl.replace(/^https?:\\/\\/[^/]+/i, '')\n return path.replace(/\\/+$/, '') || '/'\n }\n}\n\nfunction normalizeRequestUrl(baseUrl: string, url: string): string {\n if (!url || /^https?:\\/\\//.test(url)) return url\n\n const basePath = getBasePath(baseUrl)\n if (basePath === '/') return url\n\n const normalizedUrl = url.startsWith('/') ? url : `/${url}`\n if (\n normalizedUrl === basePath\n || normalizedUrl.startsWith(`${basePath}?`)\n || normalizedUrl.startsWith(`${basePath}#`)\n ) {\n return normalizedUrl.slice(basePath.length)\n }\n if (normalizedUrl.startsWith(`${basePath}/`)) {\n return normalizedUrl.slice(basePath.length) || ''\n }\n return url\n}\n\nfunction asMutableHeaders(headers: AxiosRequestConfig['headers']): MutableHeaders | null {\n return headers ? (headers as MutableHeaders) : null\n}\n\nfunction hasAuthorizationHeader(headers: AxiosRequestConfig['headers']): boolean {\n const bag = asMutableHeaders(headers)\n if (!bag) return false\n if (typeof bag.has === 'function') return bag.has('Authorization')\n return Object.keys(bag).some((key) => key.toLowerCase() === 'authorization')\n}\n\nfunction setAuthorizationHeader(headers: AxiosRequestConfig['headers'], value: string): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.set === 'function') {\n bag.set('Authorization', value)\n return\n }\n bag.Authorization = value\n}\n\nfunction deleteAuthorizationHeader(headers: AxiosRequestConfig['headers']): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.delete === 'function') {\n bag.delete('Authorization')\n return\n }\n for (const key of Object.keys(bag)) {\n if (key.toLowerCase() === 'authorization') delete bag[key]\n }\n}\n\nfunction getApiClient(): AxiosInstance {\n if (!apiClientInstance) {\n apiClientInstance = axios.create({\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n }\n return apiClientInstance\n}\n\nexport interface ApiClientOptions {\n baseUrl?: string\n timeout?: number\n withAuth?: boolean\n}\n\nexport interface UseApiReturn {\n client: AxiosInstance\n get: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n post: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n put: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n patch: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n delete: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n upload: <T>(url: string, file: File, fieldName?: string, additionalData?: Record<string, unknown>) => Promise<T>\n download: (url: string, filename?: string) => Promise<string>\n buildUrl: (path: string) => string\n buildWsUrl: (path: string) => string\n}\n\n/** Axios-based API client that injects the plugin base URL and JWT auth header on every request. */\nexport function useApi(options: ApiClientOptions = {}): UseApiReturn {\n const settingsStore = useSettingsStore()\n const authStore = useAuthStore()\n const apiClient = getApiClient()\n\n // Ensure auth store is initialized (reads token from localStorage)\n if (!authStore.isInitialized) {\n authStore.initialize()\n }\n\n // Attach auth interceptor only once (reads token dynamically, not from closure)\n if (!interceptorAttached) {\n apiClient.interceptors.request.use((config) => {\n const request = config as MintInternalAxiosRequestConfig\n if (request._mintSkipAuth) {\n delete request._mintSkipAuth\n deleteAuthorizationHeader(request.headers)\n return request\n }\n\n const currentAuthStore = useAuthStore()\n if (currentAuthStore.token && config.headers && !hasAuthorizationHeader(config.headers)) {\n setAuthorizationHeader(config.headers, `Bearer ${currentAuthStore.token}`)\n }\n return config\n })\n interceptorAttached = true\n }\n\n function getBaseUrl(): string {\n return options.baseUrl ?? settingsStore.getApiBaseUrl()\n }\n\n function normalizeUrl(url: string): string {\n return normalizeRequestUrl(getBaseUrl(), url)\n }\n\n // Build per-request config that applies this caller's options\n function requestConfig(config?: AxiosRequestConfig): MintAxiosRequestConfig {\n const base: MintAxiosRequestConfig = {\n baseURL: getBaseUrl(),\n timeout: options.timeout ?? settingsStore.requestTimeout,\n ...config,\n }\n // Strip auth header if explicitly disabled\n if (options.withAuth === false) {\n base._mintSkipAuth = true\n deleteAuthorizationHeader(base.headers)\n }\n return base\n }\n\n // Generic request methods\n async function get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.get<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n async function post<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.post<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function put<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.put<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.patch<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function del<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.delete<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n // File upload helper\n async function upload<T>(url: string, file: File, fieldName = 'file', additionalData?: Record<string, unknown>): Promise<T> {\n const formData = new FormData()\n formData.append(fieldName, file)\n\n if (additionalData) {\n Object.entries(additionalData).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value))\n }\n })\n }\n\n const response = await apiClient.post<T>(normalizeUrl(url), formData, requestConfig({\n // Let Axios set Content-Type with the correct multipart boundary\n headers: { 'Content-Type': undefined },\n }))\n return response.data\n }\n\n // Download helper - returns blob URL\n async function download(url: string, filename?: string): Promise<string> {\n const response = await apiClient.get(normalizeUrl(url), requestConfig({ responseType: 'blob' }))\n const blob = new Blob([response.data])\n const blobUrl = URL.createObjectURL(blob)\n\n // Optionally trigger download\n if (filename) {\n const link = document.createElement('a')\n link.href = blobUrl\n link.download = filename\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n // Revoke after a short delay to allow download to start;\n // return empty string since the URL will be invalidated\n setTimeout(() => URL.revokeObjectURL(blobUrl), 100)\n return ''\n }\n\n // Caller is responsible for revoking when no filename is provided\n return blobUrl\n }\n\n // Build full URL for external use (e.g., <a href=\"...\">)\n function buildUrl(path: string): string {\n const baseUrl = getBaseUrl()\n return joinUrlPath(baseUrl, normalizeRequestUrl(baseUrl, path))\n }\n\n // WebSocket URL builder\n function buildWsUrl(path: string): string {\n return joinUrlPath(settingsStore.getWsBaseUrl(), path)\n }\n\n return {\n client: apiClient,\n get,\n post,\n put,\n patch,\n delete: del,\n upload,\n download,\n buildUrl,\n buildWsUrl,\n }\n}\n","import {\n onScopeDispose,\n ref,\n watch,\n type Ref,\n type WatchOptions,\n type WatchSource,\n type WatchStopHandle,\n} from 'vue'\n\nexport type DebouncedWatchCleanup = () => void\nexport type DebouncedWatchSource<T> = WatchSource<T> | WatchSource<unknown>[] | (() => T)\nexport type DebouncedWatchCallback<T> = (\n value: T,\n oldValue: T | undefined,\n onCleanup: (cleanup: DebouncedWatchCleanup) => void,\n) => void | Promise<void>\n\nexport interface UseDebouncedWatchOptions extends WatchOptions {\n /** Debounce interval in milliseconds. */\n delay?: number\n}\n\nexport interface UseDebouncedWatchReturn {\n /** Whether a callback is currently scheduled. */\n isPending: Ref<boolean>\n /** Cancel the pending callback without stopping the watcher. */\n cancel: () => void\n /** Run the pending callback immediately, if one is scheduled. */\n flush: () => void\n /** Stop the watcher and clear pending callbacks. */\n stop: WatchStopHandle\n}\n\n/** Watch a Vue source with debounced callback execution and explicit cancel/flush controls. */\nexport function useDebouncedWatch<T>(\n source: DebouncedWatchSource<T>,\n callback: DebouncedWatchCallback<T>,\n options: UseDebouncedWatchOptions = {},\n): UseDebouncedWatchReturn {\n const { delay = 300, ...watchOptions } = options\n const isPending = ref(false)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n let hasLatestValue = false\n let latestValue: T\n let latestOldValue: T | undefined\n let callbackCleanup: DebouncedWatchCleanup | null = null\n let stopped = false\n\n function clearTimer(): void {\n if (timer) {\n clearTimeout(timer)\n timer = null\n }\n isPending.value = false\n }\n\n function runCallbackCleanup(): void {\n if (!callbackCleanup) return\n callbackCleanup()\n callbackCleanup = null\n }\n\n function cancel(): void {\n clearTimer()\n hasLatestValue = false\n latestOldValue = undefined\n }\n\n function flush(): void {\n if (!hasLatestValue) return\n const value = latestValue\n const oldValue = latestOldValue\n cancel()\n runCallbackCleanup()\n void callback(value, oldValue, (cleanup) => {\n callbackCleanup = cleanup\n })\n }\n\n function schedule(value: T, oldValue: T | undefined): void {\n cancel()\n hasLatestValue = true\n latestValue = value\n latestOldValue = oldValue\n isPending.value = true\n timer = setTimeout(flush, delay)\n }\n\n const stopWatch = watch(\n source as WatchSource<unknown> | WatchSource<unknown>[],\n (value, oldValue, onCleanup) => {\n schedule(value as T, oldValue as T | undefined)\n onCleanup(() => {\n cancel()\n runCallbackCleanup()\n })\n },\n watchOptions,\n )\n\n function stop(): void {\n if (stopped) return\n stopped = true\n stopWatch()\n cancel()\n runCallbackCleanup()\n }\n\n onScopeDispose(stop)\n\n return {\n isPending,\n cancel,\n flush,\n stop,\n }\n}\n","import { ref, type Ref } from 'vue'\n\nexport type RequestSyncSuccessKind = 'load' | 'save' | 'run'\n\nexport interface RequestSyncRunOptions {\n success?: RequestSyncSuccessKind\n errorMessage?: string\n}\n\nexport interface UseRequestSyncStateReturn {\n /** Whether a request is in progress. */\n loading: Ref<boolean>\n /** Message from the last failed request, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful load operation, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Timestamp of the last successful save operation, or null. */\n lastSavedAt: Ref<Date | null>\n /** Timestamp of the last successful run operation, or null. */\n lastRunAt: Ref<Date | null>\n /** Clear the current error without changing loading or timestamps. */\n clearError: () => void\n /** Convert thrown values into a developer-facing error message. */\n readErrorMessage: (value: unknown, fallback?: string) => string\n /** Store and return a normalized error message. */\n setError: (value: unknown, fallback?: string) => string\n /** Mark the resource as successfully loaded. */\n markLoaded: (date?: Date) => void\n /** Mark the resource as successfully saved. */\n markSaved: (date?: Date) => void\n /** Mark the operation as successfully run. */\n markRun: (date?: Date) => void\n /** Run a request with shared loading/error handling and optional sync marking. */\n run: <T>(operation: () => Promise<T>, options?: RequestSyncRunOptions) => Promise<T>\n}\n\n/** Shared loading/error/timestamp state for generated plugin request helpers. */\nexport function useRequestSyncState(\n defaultErrorMessage = 'Request failed.',\n): UseRequestSyncStateReturn {\n const loading = ref(false)\n const error = ref<string | null>(null)\n const lastLoadedAt = ref<Date | null>(null)\n const lastSavedAt = ref<Date | null>(null)\n const lastRunAt = ref<Date | null>(null)\n\n function clearError(): void {\n error.value = null\n }\n\n function readErrorMessage(value: unknown, fallback = defaultErrorMessage): string {\n if (value instanceof Error && value.message) {\n return value.message\n }\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n if (\n typeof value === 'object'\n && value !== null\n && 'message' in value\n && typeof (value as { message?: unknown }).message === 'string'\n && (value as { message: string }).message\n ) {\n return (value as { message: string }).message\n }\n return fallback\n }\n\n function setError(value: unknown, fallback?: string): string {\n const message = readErrorMessage(value, fallback)\n error.value = message\n return message\n }\n\n function markLoaded(date = new Date()): void {\n lastLoadedAt.value = date\n }\n\n function markSaved(date = new Date()): void {\n lastSavedAt.value = date\n }\n\n function markRun(date = new Date()): void {\n lastRunAt.value = date\n }\n\n async function run<T>(\n operation: () => Promise<T>,\n options: RequestSyncRunOptions = {},\n ): Promise<T> {\n loading.value = true\n clearError()\n try {\n const response = await operation()\n if (options.success === 'load') {\n markLoaded()\n } else if (options.success === 'save') {\n markSaved()\n } else if (options.success === 'run') {\n markRun()\n }\n return response\n } catch (err) {\n setError(err, options.errorMessage)\n throw err\n } finally {\n loading.value = false\n }\n }\n\n return {\n loading,\n error,\n lastLoadedAt,\n lastSavedAt,\n lastRunAt,\n clearError,\n readErrorMessage,\n setError,\n markLoaded,\n markSaved,\n markRun,\n run,\n }\n}\n","import { ref, reactive, computed, watch, type Ref, type ComputedRef } from 'vue'\nimport { useApi } from './useApi'\nimport { useDebouncedWatch } from './useDebouncedWatch'\nimport { useRequestSyncState } from './useRequestSyncState'\nimport { datePresetToISO } from './experiment-utils'\nimport type {\n ExperimentSummary,\n ExperimentListResponse,\n ExperimentFilters,\n ExperimentTypeOption,\n ExperimentSortField,\n PlatformContext,\n SelectOption,\n} from '../types'\n\nfunction getPlatformContext(): PlatformContext | undefined {\n if (typeof window === 'undefined') return undefined\n return (window as unknown as { __MINT_PLATFORM__?: PlatformContext }).__MINT_PLATFORM__\n}\n\nfunction getPlatformApiUrl(): string | undefined {\n return getPlatformContext()?.platformApiUrl\n}\n\nexport interface UseExperimentSelectorOptions {\n experimentType?: string\n apiBaseUrl?: string\n limit?: number\n immediate?: boolean\n}\n\nexport interface UseExperimentSelectorReturn {\n /** Current page of fetched experiment rows. */\n experiments: Ref<ExperimentSummary[]>\n /** Total matching experiment count reported by the platform. */\n total: Ref<number>\n /** Currently selected experiment, or null. */\n selectedExperiment: Ref<ExperimentSummary | null>\n /** Mutable filters used for the next fetch. */\n filters: ExperimentFilters\n /** Whether experiment rows are currently loading. */\n isLoading: Ref<boolean>\n /** Error message from the last failed fetch, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful experiment fetch, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Current zero-based page index. */\n page: Ref<number>\n /** Whether another page can be loaded. */\n hasMore: ComputedRef<boolean>\n // Sort\n /** Combined sort key in the form `<field>:<asc|desc>`. */\n sortKey: Ref<string>\n // Filter options\n /** Available experiment type options for filter UI. */\n experimentTypes: Ref<ExperimentTypeOption[]>\n /** Available project options for filter UI. */\n projects: Ref<SelectOption<string>[]>\n // Grouped view\n /** Fetched experiments grouped by project name. */\n groupedByProject: ComputedRef<[string, ExperimentSummary[]][]>\n // Methods\n /** Fetch the current page with the current filters. */\n fetch: () => Promise<void>\n /** Fetch and append the next page when available. */\n loadMore: () => Promise<void>\n /** Clear fetched rows and refetch page zero. */\n reset: () => void\n /** Select an experiment. */\n select: (experiment: ExperimentSummary) => void\n /** Clear selection and filters. */\n clear: () => void\n /** Fetch experiment type and project filter options. */\n fetchFilterOptions: () => Promise<void>\n}\n\n/** Fetches a paginated, filtered experiment list from the platform API for picker and selector UIs. */\nexport function useExperimentSelector(\n options: UseExperimentSelectorOptions = {},\n): UseExperimentSelectorReturn {\n const { limit = 100, immediate = false, experimentType, apiBaseUrl } = options\n const platformBase = apiBaseUrl ?? getPlatformApiUrl()\n const api = useApi()\n\n const experiments = ref<ExperimentSummary[]>([])\n const total = ref(0)\n const selectedExperiment = ref<ExperimentSummary | null>(null)\n const request = useRequestSyncState('Failed to fetch experiments')\n const isLoading = request.loading\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n const page = ref(0)\n\n // Sort: combined key like \"created_at:desc\"\n const sortKey = ref<string>('created_at:desc')\n\n // Filter option data (fetched once, cached)\n const experimentTypes = ref<ExperimentTypeOption[]>([])\n const projects = ref<SelectOption<string>[]>([])\n let filterOptionsFetched = false\n\n const hasMore = computed(() => experiments.value.length < total.value)\n\n const filters: ExperimentFilters = reactive({\n search: undefined,\n status: undefined,\n project: undefined,\n experimentType: undefined,\n datePreset: undefined,\n })\n\n function parseSortKey(): { sortBy: ExperimentSortField; sortOrder: 'asc' | 'desc' } {\n const [field, order] = sortKey.value.split(':')\n return {\n sortBy: (field || 'created_at') as ExperimentSortField,\n sortOrder: (order || 'desc') as 'asc' | 'desc',\n }\n }\n\n async function fetchExperiments(): Promise<void> {\n try {\n await request.run(async () => {\n const params = new URLSearchParams()\n // Priority: explicit option > platform context (single type) > filter dropdown > no filter\n const allowedTypes = getPlatformContext()?.allowedExperimentTypes\n const effectiveType = experimentType\n ?? (allowedTypes?.length === 1 ? allowedTypes[0] : undefined)\n ?? filters.experimentType\n ?? undefined\n if (effectiveType) params.set('experiment_type', effectiveType)\n if (filters.status) params.set('status', filters.status)\n if (filters.search) params.set('search', filters.search)\n if (filters.project) params.set('project', filters.project)\n\n // Sort params\n const { sortBy, sortOrder } = parseSortKey()\n params.set('sort_by', sortBy)\n params.set('sort_order', sortOrder)\n\n // Date preset → created_after\n if (filters.datePreset) {\n params.set('created_after', datePresetToISO(filters.datePreset))\n }\n\n params.set('limit', String(limit))\n params.set('skip', String(page.value * limit))\n\n const query = params.toString()\n const base = platformBase ?? ''\n const url = `${base}/experiments${query ? `?${query}` : ''}`\n const data = await api.get<ExperimentListResponse>(url)\n\n // Client-side filter for multiple allowed types (backend only supports single type)\n let filtered = data.experiments\n if (!effectiveType && allowedTypes && allowedTypes.length > 1) {\n const typeSet = new Set(allowedTypes)\n filtered = filtered.filter(e => typeSet.has(e.experiment_type))\n }\n\n if (page.value === 0) {\n experiments.value = filtered\n } else {\n experiments.value = [...experiments.value, ...filtered]\n }\n // When client-side filtering is active (multiple allowedTypes), we can't\n // use data.total since it counts all types. Check if server has more pages.\n if (!effectiveType && allowedTypes && allowedTypes.length > 1) {\n if (data.experiments.length < limit) {\n // Server returned less than a full page — no more data\n total.value = experiments.value.length\n } else {\n // Might be more pages on the server\n total.value = experiments.value.length + 1\n }\n } else {\n total.value = data.total\n }\n }, { success: 'load', errorMessage: 'Failed to fetch experiments' })\n } catch {\n if (page.value === 0) {\n experiments.value = []\n total.value = 0\n }\n }\n }\n\n async function fetchFilterOptions(): Promise<void> {\n if (filterOptionsFetched) return\n filterOptionsFetched = true\n\n const base = platformBase ?? ''\n const [typesRes, projectsRes] = await Promise.allSettled([\n api.get<Array<{ value: string; label: string; color?: string }>>(`${base}/experiments/experiment-types`),\n api.get<{ projects: Array<{ id: number; name: string }>; total: number }>(`${base}/projects`),\n ])\n\n if (typesRes.status === 'fulfilled' && Array.isArray(typesRes.value)) {\n experimentTypes.value = typesRes.value.map(t => ({\n value: t.value,\n label: t.label,\n color: t.color,\n }))\n }\n\n if (projectsRes.status === 'fulfilled' && projectsRes.value?.projects && Array.isArray(projectsRes.value.projects)) {\n projects.value = projectsRes.value.projects.map(p => ({\n value: p.name,\n label: p.name,\n }))\n }\n }\n\n async function loadMore(): Promise<void> {\n if (!hasMore.value || isLoading.value) return\n page.value++\n await fetchExperiments()\n }\n\n function reset(): void {\n page.value = 0\n experiments.value = []\n total.value = 0\n fetchExperiments()\n }\n\n function select(experiment: ExperimentSummary): void {\n selectedExperiment.value = experiment\n }\n\n function clear(): void {\n selectedExperiment.value = null\n filters.search = undefined\n filters.status = undefined\n filters.project = undefined\n filters.experimentType = undefined\n filters.datePreset = undefined\n sortKey.value = 'created_at:desc'\n page.value = 0\n request.clearError()\n }\n\n // Group experiments by project (client-side)\n const groupedByProject = computed<[string, ExperimentSummary[]][]>(() => {\n const groups = new Map<string, ExperimentSummary[]>()\n for (const exp of experiments.value) {\n const key = exp.project_name ?? exp.project ?? 'No project'\n const list = groups.get(key)\n if (list) {\n list.push(exp)\n } else {\n groups.set(key, [exp])\n }\n }\n // Sort alphabetically, \"No project\" last\n return [...groups.entries()].sort(([a], [b]) => {\n if (a === 'No project') return 1\n if (b === 'No project') return -1\n return a.localeCompare(b)\n })\n })\n\n const searchWatch = useDebouncedWatch(\n () => filters.search,\n () => {\n page.value = 0\n fetchExperiments()\n },\n { delay: 300 },\n )\n\n // Immediate watch on discrete filters and sort (cancel any pending search debounce)\n watch(\n () => [filters.status, filters.project, filters.experimentType, filters.datePreset, sortKey.value],\n () => {\n searchWatch.cancel()\n page.value = 0\n fetchExperiments()\n },\n )\n\n if (immediate) {\n fetchExperiments()\n }\n\n return {\n experiments,\n total,\n selectedExperiment,\n filters,\n isLoading,\n error,\n lastLoadedAt,\n page,\n hasMore,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n loadMore,\n reset,\n select,\n clear,\n fetchFilterOptions,\n }\n}\n","import {\n ref,\n computed,\n readonly,\n provide,\n toValue,\n watch,\n onScopeDispose,\n type Ref,\n type ComputedRef,\n type InjectionKey,\n} from 'vue'\nimport type { ExperimentSummary, ExperimentStatus } from '../types'\nimport { resolveExperimentCode } from './experiment-utils'\n\nexport type AppExperimentRecord = Pick<ExperimentSummary, 'id' | 'name' | 'status' | 'experiment_code'>\n\nexport type AppExperimentSource =\n | AppExperimentRecord\n | null\n | undefined\n | Ref<AppExperimentRecord | null | undefined>\n | ComputedRef<AppExperimentRecord | null | undefined>\n | (() => AppExperimentRecord | null | undefined)\n\nexport interface UseAppExperimentOptions {\n /** Current experiment source to mirror into the app shell. Accepts a value, ref, computed, or getter. */\n experiment?: AppExperimentSource\n onSelect?: (experiment: ExperimentSummary) => void | Promise<void>\n onSave?: () => string | null | Promise<string | null>\n onDetach?: () => void\n saveDisabled?: Ref<boolean> | ComputedRef<boolean>\n saveDisabledMessage?: string | Ref<string | undefined> | ComputedRef<string | undefined>\n}\n\nexport interface AppExperimentPopoverBinding {\n experimentName?: string\n experimentCode?: string\n experimentStatus?: ExperimentStatus\n showSave: boolean\n showDetach: boolean\n saveDisabled: boolean\n saveDisabledMessage?: string\n saveLoading: boolean\n saveSuccessMessage?: string\n}\n\nexport interface AppExperimentSelectorModalBinding {\n modelValue: boolean\n currentExperimentId: number | null\n}\n\nexport interface UseAppExperimentReturn {\n set: (experiment: AppExperimentRecord) => void\n clear: () => void\n experimentName: Readonly<Ref<string | undefined>>\n experimentCode: Readonly<Ref<string | undefined>>\n experimentStatus: Readonly<Ref<ExperimentStatus | undefined>>\n experimentId: Readonly<Ref<number | null>>\n popover: ComputedRef<AppExperimentPopoverBinding>\n selectorModal: ComputedRef<AppExperimentSelectorModalBinding>\n openModal: () => void\n closeModal: () => void\n handleSelect: (experiment: ExperimentSummary) => void\n handleSave: () => Promise<void>\n handleDetach: () => void\n}\n\nexport interface AppExperimentState {\n experimentName: Ref<string | undefined>\n experimentCode: Ref<string | undefined>\n experimentStatus: Ref<ExperimentStatus | undefined>\n experimentId: Ref<number | null>\n showModal: Ref<boolean>\n saveLoading: Ref<boolean>\n saveSuccessMessage: Ref<string | undefined>\n showSave: Ref<boolean>\n showDetach: ComputedRef<boolean>\n closeModal: () => void\n saveDisabled: ComputedRef<boolean>\n saveDisabledMessage: ComputedRef<string | undefined>\n popover: ComputedRef<AppExperimentPopoverBinding>\n selectorModal: ComputedRef<AppExperimentSelectorModalBinding>\n openModal: () => void\n handleSelect: (experiment: ExperimentSummary) => void\n handleSave: () => Promise<void>\n handleDetach: () => void\n}\n\nexport const APP_EXPERIMENT_KEY: InjectionKey<AppExperimentState> = Symbol('app-experiment')\n\n/** Manages the current experiment selection, save flow, and detach action for a plugin's AppTopBar app shell. */\nexport function useAppExperiment(options: UseAppExperimentOptions = {}): UseAppExperimentReturn {\n const experimentName = ref<string | undefined>()\n const experimentCode = ref<string | undefined>()\n const experimentStatus = ref<ExperimentStatus | undefined>()\n const experimentId = ref<number | null>(null)\n const showModal = ref(false)\n const saveLoading = ref(false)\n const saveSuccessMessage = ref<string | undefined>()\n\n let successTimer: ReturnType<typeof setTimeout> | null = null\n\n const showSave = ref(!!options.onSave)\n const showDetach = computed(() => experimentId.value !== null)\n const saveDisabled = computed(() => toValue(options.saveDisabled) ?? false)\n const saveDisabledMessage = computed(() => toValue(options.saveDisabledMessage))\n const popover = computed<AppExperimentPopoverBinding>(() => ({\n experimentName: experimentName.value,\n experimentCode: experimentCode.value,\n experimentStatus: experimentStatus.value,\n showSave: showSave.value,\n showDetach: showDetach.value,\n saveDisabled: saveDisabled.value,\n saveDisabledMessage: saveDisabledMessage.value,\n saveLoading: saveLoading.value,\n saveSuccessMessage: saveSuccessMessage.value,\n }))\n const selectorModal = computed<AppExperimentSelectorModalBinding>(() => ({\n modelValue: showModal.value,\n currentExperimentId: experimentId.value,\n }))\n\n function set(experiment: AppExperimentRecord) {\n experimentId.value = experiment.id\n experimentName.value = experiment.name\n experimentCode.value = resolveExperimentCode(experiment)\n experimentStatus.value = experiment.status\n }\n\n function clear() {\n experimentId.value = null\n experimentName.value = undefined\n experimentCode.value = undefined\n experimentStatus.value = undefined\n }\n\n if (options.experiment !== undefined) {\n watch(\n () => toValue(options.experiment),\n (experiment) => {\n if (experiment) {\n set(experiment)\n } else {\n clear()\n }\n },\n { immediate: true },\n )\n }\n\n function openModal() {\n showModal.value = true\n }\n\n function closeModal() {\n showModal.value = false\n }\n\n function handleSelect(experiment: ExperimentSummary) {\n set(experiment)\n showModal.value = false\n options.onSelect?.(experiment)\n }\n\n async function handleSave() {\n if (!options.onSave || saveLoading.value) return\n saveLoading.value = true\n try {\n const message = await options.onSave()\n if (message) {\n saveSuccessMessage.value = message\n if (successTimer) clearTimeout(successTimer)\n successTimer = setTimeout(() => {\n saveSuccessMessage.value = undefined\n successTimer = null\n }, 3000)\n }\n } finally {\n saveLoading.value = false\n }\n }\n\n function handleDetach() {\n clear()\n options.onDetach?.()\n }\n\n onScopeDispose(() => {\n if (successTimer) clearTimeout(successTimer)\n })\n\n const state: AppExperimentState = {\n experimentName,\n experimentCode,\n experimentStatus,\n experimentId,\n showModal,\n saveLoading,\n saveSuccessMessage,\n showSave,\n showDetach,\n saveDisabled,\n saveDisabledMessage,\n popover,\n selectorModal,\n openModal,\n closeModal,\n handleSelect,\n handleSave,\n handleDetach,\n }\n\n provide(APP_EXPERIMENT_KEY, state)\n\n return {\n set,\n clear,\n experimentName: readonly(experimentName),\n experimentCode: readonly(experimentCode),\n experimentStatus: readonly(experimentStatus),\n experimentId: readonly(experimentId),\n popover,\n selectorModal,\n openModal,\n closeModal,\n handleSelect,\n handleSave,\n handleDetach,\n }\n}\n","import {\n useConcentrationUnits,\n type ConcentrationValue,\n type MolarityUnit,\n type MassVolumeUnit,\n} from './useConcentrationUnits'\n\n// Volume units\nexport type VolumeUnit = 'µL' | 'mL' | 'L'\n\nexport interface VolumeValue {\n value: number\n unit: VolumeUnit\n}\n\n// Dilution calculation types\nexport interface DilutionParams {\n stockConcentration: ConcentrationValue\n finalConcentration: ConcentrationValue\n finalVolume: VolumeValue\n}\n\nexport interface DilutionResult {\n stockVolume: VolumeValue\n diluentVolume: VolumeValue\n dilutionFactor: number\n valid: boolean\n error?: string\n}\n\n// Serial dilution types\nexport interface SerialDilutionParams {\n startingConcentration: ConcentrationValue\n dilutionFactor: number\n numberOfDilutions: number\n volumePerWell: VolumeValue\n}\n\nexport interface SerialDilutionStep {\n stepNumber: number\n concentration: ConcentrationValue\n transferVolume: VolumeValue\n diluentVolume: VolumeValue\n}\n\nexport interface SerialDilutionResult {\n steps: SerialDilutionStep[]\n totalStockVolume: VolumeValue\n valid: boolean\n error?: string\n}\n\n// Conversion types\nexport interface ConversionResult {\n result: ConcentrationValue\n valid: boolean\n error?: string\n}\n\n// Well concentration for plate integration\nexport interface WellConcentration {\n wellId: string\n concentration: ConcentrationValue\n volume?: VolumeValue\n}\n\nexport interface UseDoseCalculatorReturn {\n volumeUnits: VolumeUnit[]\n calculateDilution: (params: DilutionParams) => DilutionResult\n calculateSerialDilution: (params: SerialDilutionParams) => SerialDilutionResult\n convertMassToMolar: (\n mass: number,\n massUnit: MassVolumeUnit,\n mw: number\n ) => ConcentrationValue\n convertMolarToMass: (\n molar: number,\n molarUnit: MolarityUnit,\n mw: number\n ) => ConcentrationValue\n convertVolume: (value: number, from: VolumeUnit, to: VolumeUnit) => number\n formatVolume: (volume: VolumeValue, precision?: number) => string\n generateWellConcentrations: (\n result: SerialDilutionResult,\n wellIds: string[]\n ) => WellConcentration[]\n}\n\nconst VOLUME_UNITS: VolumeUnit[] = ['µL', 'mL', 'L']\n\nconst VOLUME_FACTORS: Record<VolumeUnit, number> = {\n 'µL': 1e-6,\n 'mL': 1e-3,\n 'L': 1,\n}\n\n/** Calculates single dilutions, serial dilution series, and plate-filling volumes for dose-response experiments. */\nexport function useDoseCalculator(): UseDoseCalculatorReturn {\n const { convert } = useConcentrationUnits()\n\n function convertVolume(value: number, from: VolumeUnit, to: VolumeUnit): number {\n if (from === to) return value\n const baseValue = value * VOLUME_FACTORS[from]\n return baseValue / VOLUME_FACTORS[to]\n }\n\n function formatVolume(volume: VolumeValue, precision: number = 3): string {\n const { value, unit } = volume\n if (value === 0) return `0 ${unit}`\n\n let formattedValue: string\n if (Math.abs(value) >= 1000) {\n formattedValue = value.toExponential(precision - 1)\n } else if (Math.abs(value) < 0.001) {\n formattedValue = value.toExponential(precision - 1)\n } else {\n formattedValue = value.toPrecision(precision)\n }\n\n // Remove trailing zeros\n formattedValue = formattedValue.replace(/\\.?0+$/, '')\n formattedValue = formattedValue.replace(/\\.?0+e/, 'e')\n\n return `${formattedValue} ${unit}`\n }\n\n function calculateDilution(params: DilutionParams): DilutionResult {\n const { stockConcentration, finalConcentration, finalVolume } = params\n\n // Validate inputs\n if (stockConcentration.value <= 0) {\n return {\n stockVolume: { value: 0, unit: 'µL' },\n diluentVolume: { value: 0, unit: 'µL' },\n dilutionFactor: 0,\n valid: false,\n error: 'Stock concentration must be positive',\n }\n }\n\n if (finalConcentration.value <= 0) {\n return {\n stockVolume: { value: 0, unit: 'µL' },\n diluentVolume: { value: 0, unit: 'µL' },\n dilutionFactor: 0,\n valid: false,\n error: 'Final concentration must be positive',\n }\n }\n\n if (finalVolume.value <= 0) {\n return {\n stockVolume: { value: 0, unit: 'µL' },\n diluentVolume: { value: 0, unit: 'µL' },\n dilutionFactor: 0,\n valid: false,\n error: 'Final volume must be positive',\n }\n }\n\n // Convert concentrations to same unit\n const convertedFinal = convert(\n finalConcentration.value,\n finalConcentration.unit,\n stockConcentration.unit\n )\n\n if (convertedFinal === null) {\n return {\n stockVolume: { value: 0, unit: 'µL' },\n diluentVolume: { value: 0, unit: 'µL' },\n dilutionFactor: 0,\n valid: false,\n error: 'Cannot convert between concentration units. Provide molecular weight for mass↔molarity conversion.',\n }\n }\n\n // Check that stock is more concentrated than final\n if (convertedFinal >= stockConcentration.value) {\n return {\n stockVolume: { value: 0, unit: 'µL' },\n diluentVolume: { value: 0, unit: 'µL' },\n dilutionFactor: 0,\n valid: false,\n error: 'Stock concentration must be higher than final concentration',\n }\n }\n\n // Calculate using C1V1 = C2V2\n const dilutionFactor = stockConcentration.value / convertedFinal\n const finalVolumeInL = convertVolume(finalVolume.value, finalVolume.unit, 'L')\n const stockVolumeInL = finalVolumeInL / dilutionFactor\n const diluentVolumeInL = finalVolumeInL - stockVolumeInL\n\n // Convert to appropriate unit (µL for small volumes, mL for larger)\n let outputUnit: VolumeUnit = 'µL'\n if (stockVolumeInL >= 0.001) outputUnit = 'mL'\n if (stockVolumeInL >= 1) outputUnit = 'L'\n\n return {\n stockVolume: {\n value: convertVolume(stockVolumeInL, 'L', outputUnit),\n unit: outputUnit,\n },\n diluentVolume: {\n value: convertVolume(diluentVolumeInL, 'L', outputUnit),\n unit: outputUnit,\n },\n dilutionFactor,\n valid: true,\n }\n }\n\n function calculateSerialDilution(params: SerialDilutionParams): SerialDilutionResult {\n const { startingConcentration, dilutionFactor, numberOfDilutions, volumePerWell } = params\n\n // Validate inputs\n if (startingConcentration.value <= 0) {\n return {\n steps: [],\n totalStockVolume: { value: 0, unit: 'µL' },\n valid: false,\n error: 'Starting concentration must be positive',\n }\n }\n\n if (dilutionFactor <= 1) {\n return {\n steps: [],\n totalStockVolume: { value: 0, unit: 'µL' },\n valid: false,\n error: 'Dilution factor must be greater than 1',\n }\n }\n\n if (numberOfDilutions < 1) {\n return {\n steps: [],\n totalStockVolume: { value: 0, unit: 'µL' },\n valid: false,\n error: 'Number of dilutions must be at least 1',\n }\n }\n\n if (volumePerWell.value <= 0) {\n return {\n steps: [],\n totalStockVolume: { value: 0, unit: 'µL' },\n valid: false,\n error: 'Volume per well must be positive',\n }\n }\n\n const steps: SerialDilutionStep[] = []\n let currentConcentration = startingConcentration.value\n const transferVolume = volumePerWell.value / (dilutionFactor - 1)\n const diluentVolume = volumePerWell.value - transferVolume\n\n for (let i = 0; i < numberOfDilutions; i++) {\n steps.push({\n stepNumber: i + 1,\n concentration: {\n value: currentConcentration,\n unit: startingConcentration.unit,\n },\n transferVolume: {\n value: i === 0 ? volumePerWell.value : transferVolume,\n unit: volumePerWell.unit,\n },\n diluentVolume: {\n value: i === 0 ? 0 : diluentVolume,\n unit: volumePerWell.unit,\n },\n })\n currentConcentration /= dilutionFactor\n }\n\n // Total stock needed for first well\n const totalStockVolume: VolumeValue = {\n value: volumePerWell.value + (transferVolume * (numberOfDilutions - 1)),\n unit: volumePerWell.unit,\n }\n\n return {\n steps,\n totalStockVolume,\n valid: true,\n }\n }\n\n function convertMassToMolar(\n mass: number,\n massUnit: MassVolumeUnit,\n mw: number\n ): ConcentrationValue {\n // Convert mass to g/mL, then to M using MW\n const converted = convert(mass, massUnit, 'µM', mw)\n if (converted !== null) {\n return { value: converted, unit: 'µM' }\n }\n // Fallback to manual calculation\n // mass (g/mL) / MW (g/mol) = mol/L = M\n const massInGML = mass * getMassVolumeFactor(massUnit)\n const molarConc = massInGML / mw\n return { value: molarConc * 1e6, unit: 'µM' }\n }\n\n function convertMolarToMass(\n molar: number,\n molarUnit: MolarityUnit,\n mw: number\n ): ConcentrationValue {\n // Convert M to mass using MW\n const converted = convert(molar, molarUnit, 'µg/mL', mw)\n if (converted !== null) {\n return { value: converted, unit: 'µg/mL' }\n }\n // Fallback to manual calculation\n // mol/L * MW (g/mol) = g/L = mg/mL\n const molarInM = molar * getMolarityFactor(molarUnit)\n const massConc = molarInM * mw * 1e6 // µg/mL\n return { value: massConc, unit: 'µg/mL' }\n }\n\n function getMassVolumeFactor(unit: MassVolumeUnit): number {\n const factors: Record<MassVolumeUnit, number> = {\n 'pg/mL': 1e-12,\n 'ng/mL': 1e-9,\n 'µg/mL': 1e-6,\n 'mg/mL': 1e-3,\n 'g/mL': 1,\n }\n return factors[unit]\n }\n\n function getMolarityFactor(unit: MolarityUnit): number {\n const factors: Record<MolarityUnit, number> = {\n 'pM': 1e-12,\n 'nM': 1e-9,\n 'µM': 1e-6,\n 'mM': 1e-3,\n 'M': 1,\n }\n return factors[unit]\n }\n\n function generateWellConcentrations(\n result: SerialDilutionResult,\n wellIds: string[]\n ): WellConcentration[] {\n if (!result.valid || result.steps.length === 0) return []\n\n return result.steps.slice(0, wellIds.length).map((step, index) => ({\n wellId: wellIds[index],\n concentration: step.concentration,\n volume: step.transferVolume,\n }))\n }\n\n return {\n volumeUnits: VOLUME_UNITS,\n calculateDilution,\n calculateSerialDilution,\n convertMassToMolar,\n convertMolarToMass,\n convertVolume,\n formatVolume,\n generateWellConcentrations,\n }\n}\n","import { ref, computed, toRaw, type ComputedRef } from 'vue'\nimport type {\n PlateMapEditorState,\n PlateMap,\n SampleType,\n WellPlateFormat,\n} from '../types'\n\nconst DEFAULT_PALETTE = [\n '#3B82F6', // blue\n '#10B981', // green\n '#EF4444', // red\n '#F59E0B', // amber\n '#8B5CF6', // purple\n '#F97316', // orange\n '#06B6D4', // cyan\n '#14B8A6', // teal\n '#6B7280', // gray\n]\n\nconst MAX_HISTORY = 50\n\ninterface HistoryEntry {\n plates: PlateMap[]\n samples: SampleType[]\n}\n\nexport interface UseWellPlateEditorOptions {\n maxHistory?: number\n defaultFormat?: WellPlateFormat\n}\n\nexport interface UseWellPlateEditorReturn {\n state: ComputedRef<PlateMapEditorState>\n plates: ComputedRef<PlateMap[]>\n activePlate: ComputedRef<PlateMap | undefined>\n samples: ComputedRef<SampleType[]>\n selectedWells: ComputedRef<string[]>\n activeSampleId: ComputedRef<string | undefined>\n canUndo: ComputedRef<boolean>\n canRedo: ComputedRef<boolean>\n setActivePlate: (plateId: string) => void\n setActiveSample: (sampleId: string | undefined) => void\n setSelectedWells: (wellIds: string[]) => void\n addPlate: (name?: string, format?: WellPlateFormat) => PlateMap\n removePlate: (plateId: string) => void\n addSample: (name: string, color?: string) => SampleType\n removeSample: (sampleId: string) => void\n assignSample: (wellIds: string[], sampleId: string | undefined) => void\n clearWells: (wellIds: string[]) => void\n undo: () => void\n redo: () => void\n exportData: (format: 'json' | 'csv') => string\n importData: (data: string, format: 'json' | 'csv') => boolean\n loadState: (state: Partial<PlateMapEditorState>) => void\n reset: () => void\n}\n\nfunction createEmptyPlate(\n name: string,\n format: WellPlateFormat,\n id?: string\n): PlateMap {\n return {\n id: id || `plate-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n name,\n format,\n wells: {},\n }\n}\n\nfunction generateSampleId(): string {\n return `sample-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\nfunction cloneValue<T>(value: T): T {\n return structuredClone(toRaw(value))\n}\n\n/** Manages multi-plate well-plate state with sample assignment, selection, and undo/redo history. */\nexport function useWellPlateEditor(\n initialState?: Partial<PlateMapEditorState>,\n options: UseWellPlateEditorOptions = {}\n): UseWellPlateEditorReturn {\n const { maxHistory = MAX_HISTORY, defaultFormat = 96 } = options\n\n const defaultPlate = createEmptyPlate('Plate 1', defaultFormat)\n\n const internalState = ref<PlateMapEditorState>({\n plates: initialState?.plates || [defaultPlate],\n activePlateId: initialState?.activePlateId || defaultPlate.id,\n samples: initialState?.samples || [],\n selectedWells: initialState?.selectedWells || [],\n activeSampleId: initialState?.activeSampleId,\n })\n\n const history = ref<HistoryEntry[]>([])\n const historyIndex = ref(-1)\n\n const state = computed(() => internalState.value)\n const plates = computed(() => internalState.value.plates)\n const activePlate = computed(() =>\n internalState.value.plates.find(p => p.id === internalState.value.activePlateId)\n )\n const samples = computed(() => internalState.value.samples)\n const selectedWells = computed(() => internalState.value.selectedWells)\n const activeSampleId = computed(() => internalState.value.activeSampleId)\n\n const canUndo = computed(() => historyIndex.value >= 0)\n const canRedo = computed(() => historyIndex.value < history.value.length - 1)\n\n function saveToHistory() {\n const entry: HistoryEntry = {\n plates: cloneValue(internalState.value.plates),\n samples: cloneValue(internalState.value.samples),\n }\n\n if (historyIndex.value < history.value.length - 1) {\n history.value = history.value.slice(0, historyIndex.value + 1)\n }\n\n history.value.push(entry)\n\n if (history.value.length > maxHistory) {\n history.value.shift()\n } else {\n historyIndex.value++\n }\n }\n\n function setActivePlate(plateId: string) {\n if (internalState.value.plates.some(p => p.id === plateId)) {\n internalState.value.activePlateId = plateId\n internalState.value.selectedWells = []\n }\n }\n\n function setActiveSample(sampleId: string | undefined) {\n internalState.value.activeSampleId = sampleId\n }\n\n function setSelectedWells(wellIds: string[]) {\n internalState.value.selectedWells = wellIds\n }\n\n function addPlate(name?: string, format?: WellPlateFormat): PlateMap {\n saveToHistory()\n const plateNumber = internalState.value.plates.length + 1\n const plate = createEmptyPlate(\n name || `Plate ${plateNumber}`,\n format || defaultFormat\n )\n internalState.value.plates.push(plate)\n internalState.value.activePlateId = plate.id\n internalState.value.selectedWells = []\n return plate\n }\n\n function removePlate(plateId: string) {\n if (internalState.value.plates.length <= 1) return\n\n saveToHistory()\n const index = internalState.value.plates.findIndex(p => p.id === plateId)\n if (index === -1) return\n\n internalState.value.plates.splice(index, 1)\n if (internalState.value.activePlateId === plateId) {\n internalState.value.activePlateId = internalState.value.plates[0].id\n internalState.value.selectedWells = []\n }\n }\n\n function addSample(name: string, color?: string): SampleType {\n saveToHistory()\n const sample: SampleType = {\n id: generateSampleId(),\n name,\n color: color || DEFAULT_PALETTE[internalState.value.samples.length % DEFAULT_PALETTE.length],\n count: 0,\n }\n internalState.value.samples.push(sample)\n return sample\n }\n\n function removeSample(sampleId: string) {\n saveToHistory()\n const index = internalState.value.samples.findIndex(s => s.id === sampleId)\n if (index === -1) return\n\n internalState.value.samples.splice(index, 1)\n\n for (const plate of internalState.value.plates) {\n for (const well of Object.values(plate.wells)) {\n if (well.sampleType === sampleId) {\n delete well.sampleType\n well.state = 'empty'\n }\n }\n }\n\n if (internalState.value.activeSampleId === sampleId) {\n internalState.value.activeSampleId = undefined\n }\n\n updateSampleCounts()\n }\n\n function assignSample(wellIds: string[], sampleId: string | undefined) {\n if (wellIds.length === 0) return\n\n saveToHistory()\n const plate = activePlate.value\n if (!plate) return\n\n for (const wellId of wellIds) {\n const well = plate.wells[wellId] || {\n id: wellId,\n row: wellId.charCodeAt(0) - 65,\n col: parseInt(wellId.slice(1)) - 1,\n state: 'empty',\n }\n\n if (sampleId) {\n well.sampleType = sampleId\n well.state = 'filled'\n } else {\n delete well.sampleType\n well.state = 'empty'\n }\n\n plate.wells[wellId] = well\n }\n updateSampleCounts()\n }\n\n function clearWells(wellIds: string[]) {\n assignSample(wellIds, undefined)\n }\n\n function updateSampleCounts() {\n const counts: Record<string, number> = {}\n\n for (const plate of internalState.value.plates) {\n for (const well of Object.values(plate.wells)) {\n if (well.sampleType) {\n counts[well.sampleType] = (counts[well.sampleType] || 0) + 1\n }\n }\n }\n\n for (const sample of internalState.value.samples) {\n sample.count = counts[sample.id] || 0\n }\n }\n\n function undo() {\n if (!canUndo.value) return\n\n const entry = history.value[historyIndex.value]\n historyIndex.value--\n\n internalState.value.plates = cloneValue(entry.plates)\n internalState.value.samples = cloneValue(entry.samples)\n\n const activePlateExists = internalState.value.plates.some(p => p.id === internalState.value.activePlateId)\n if (!activePlateExists) {\n internalState.value.activePlateId = internalState.value.plates[0]?.id || ''\n }\n internalState.value.selectedWells = []\n }\n\n function redo() {\n if (!canRedo.value) return\n\n historyIndex.value++\n const entry = history.value[historyIndex.value]\n\n internalState.value.plates = cloneValue(entry.plates)\n internalState.value.samples = cloneValue(entry.samples)\n internalState.value.selectedWells = []\n }\n\n function exportData(format: 'json' | 'csv'): string {\n if (format === 'json') {\n return JSON.stringify({\n plates: internalState.value.plates,\n samples: internalState.value.samples,\n }, null, 2)\n }\n\n const sampleMap = new Map(internalState.value.samples.map(s => [s.id, s.name]))\n const rows = ['Plate,Well,Sample Type,Sample Name']\n\n for (const plate of internalState.value.plates) {\n for (const [wellId, well] of Object.entries(plate.wells)) {\n if (well.sampleType) {\n const sampleName = sampleMap.get(well.sampleType) || ''\n rows.push(`\"${plate.name}\",\"${wellId}\",\"${well.sampleType}\",\"${sampleName}\"`)\n }\n }\n }\n\n return rows.join('\\n')\n }\n\n function importData(data: string, format: 'json' | 'csv'): boolean {\n try {\n saveToHistory()\n\n if (format === 'json') {\n const parsed = JSON.parse(data)\n if (parsed.plates && Array.isArray(parsed.plates)) {\n internalState.value.plates = parsed.plates\n internalState.value.activePlateId = parsed.plates[0]?.id || ''\n }\n if (parsed.samples && Array.isArray(parsed.samples)) {\n internalState.value.samples = parsed.samples\n }\n internalState.value.selectedWells = []\n updateSampleCounts()\n return true\n }\n\n const lines = data.trim().split('\\n')\n if (lines.length < 2) return false\n\n const plateMap = new Map<string, PlateMap>()\n const sampleMap = new Map<string, SampleType>()\n\n for (let i = 1; i < lines.length; i++) {\n const parts = lines[i].match(/(?:[^\",]+|\"[^\"]*\")+/g)\n if (!parts || parts.length < 4) continue\n\n const plateName = parts[0].replace(/^\"|\"$/g, '')\n const wellId = parts[1].replace(/^\"|\"$/g, '')\n const sampleId = parts[2].replace(/^\"|\"$/g, '')\n const sampleName = parts[3].replace(/^\"|\"$/g, '')\n\n if (!plateMap.has(plateName)) {\n plateMap.set(plateName, createEmptyPlate(plateName, defaultFormat))\n }\n\n if (sampleId && !sampleMap.has(sampleId)) {\n sampleMap.set(sampleId, {\n id: sampleId,\n name: sampleName || sampleId,\n color: DEFAULT_PALETTE[sampleMap.size % DEFAULT_PALETTE.length],\n count: 0,\n })\n }\n\n const plate = plateMap.get(plateName)!\n plate.wells[wellId] = {\n id: wellId,\n row: wellId.charCodeAt(0) - 65,\n col: parseInt(wellId.slice(1)) - 1,\n state: sampleId ? 'filled' : 'empty',\n sampleType: sampleId || undefined,\n }\n }\n\n internalState.value.plates = Array.from(plateMap.values())\n internalState.value.samples = Array.from(sampleMap.values())\n internalState.value.activePlateId = internalState.value.plates[0]?.id || ''\n internalState.value.selectedWells = []\n updateSampleCounts()\n return true\n } catch {\n return false\n }\n }\n\n function loadState(state: Partial<PlateMapEditorState>) {\n saveToHistory()\n if (state.plates && state.plates.length > 0) {\n internalState.value.plates = cloneValue(state.plates)\n internalState.value.activePlateId = state.activePlateId ?? state.plates[0].id\n }\n if (state.samples) {\n internalState.value.samples = cloneValue(state.samples)\n }\n internalState.value.selectedWells = state.selectedWells ?? []\n internalState.value.activeSampleId = state.activeSampleId\n updateSampleCounts()\n }\n\n function reset() {\n const plate = createEmptyPlate('Plate 1', defaultFormat)\n internalState.value = {\n plates: [plate],\n activePlateId: plate.id,\n samples: [],\n selectedWells: [],\n activeSampleId: undefined,\n }\n history.value = []\n historyIndex.value = -1\n }\n\n return {\n state,\n plates,\n activePlate,\n samples,\n selectedWells,\n activeSampleId,\n canUndo,\n canRedo,\n setActivePlate,\n setActiveSample,\n setSelectedWells,\n addPlate,\n removePlate,\n addSample,\n removeSample,\n assignSample,\n clearWells,\n undo,\n redo,\n exportData,\n importData,\n loadState,\n reset,\n }\n}\n","const DELIMITER_CANDIDATES = ['_', '-', '.'] as const\nexport type Delimiter = (typeof DELIMITER_CANDIDATES)[number]\n\n/** Pick the delimiter whose split produces the most consistent field count across `lines`. */\nexport function pickPrimaryDelimiter(lines: string[]): Delimiter {\n if (lines.length === 0) return '_'\n\n let best: Delimiter = '_'\n let bestScore = -1\n\n for (const candidate of DELIMITER_CANDIDATES) {\n const counts = lines.map(l => l.split(candidate).length)\n const frequency = new Map<number, number>()\n for (const c of counts) frequency.set(c, (frequency.get(c) ?? 0) + 1)\n\n // Mode and its frequency\n let mode = 1\n let modeFreq = 0\n for (const [c, f] of frequency) {\n if (f > modeFreq || (f === modeFreq && c > mode)) {\n mode = c\n modeFreq = f\n }\n }\n\n // A delimiter that produces field count 1 didn't split anything — score 0.\n const score = mode > 1 ? modeFreq / lines.length : 0\n if (score > bestScore) {\n best = candidate\n bestScore = score\n }\n }\n\n return best\n}\n\n/** Split a single sample name on `delimiter`. Empty input returns `['']` so callers\n * always receive at least one token. */\nexport function splitMulti(name: string, delimiter: Delimiter): string[] {\n return name.split(delimiter)\n}\n","import type { SampleClass } from '../../types/auto-group'\n\nexport function classKey(c: Pick<SampleClass, 'kind'> & { subKind?: string }): string {\n return c.subKind ? `${c.kind}:${c.subKind}` : c.kind\n}\n","{\n \"$schema\": \"./vocab.schema.json\",\n \"_doc\": {\n \"purpose\": \"Sample-name classification vocabulary for the auto-group pipeline. Edit this file to teach the classifier new sample types or matrices without changing TypeScript. All matching is case-insensitive unless a pattern's regex flags say otherwise.\",\n \"patterns\": \"Regex source strings (no surrounding slashes). Compiled with the `i` flag at load time.\",\n \"standardAllow\": \"Tokens that should be classified as Standard even when they don't match `patterns.standard`. Case-sensitive — use the exact spelling that appears in your filenames.\",\n \"matrixVocab\": \"Lowercase token → canonical display name. Tokens are normalised to lowercase before lookup, so adding 'tissues' handles 'Tissues' / 'TISSUES' / 'tissues' uniformly. Multiple aliases map to one canonical (e.g. cell + cells → Cells).\",\n \"ionizationModes\": \"Lowercase polarity tokens (pos / positive / neg / negative). Suffixed to the subKind so POS and NEG acquisitions get independent schemas.\",\n \"tissueParentTokens\": \"Tokens that flag the immediately-following token as the organ subKind (so `tissues / kidney` becomes Biological/Tissues, tagging both positions).\",\n \"replicateStripPatterns\": \"Regex sources stripped from each sample name during the replicate pre-grouping pass. Samples whose stripped (base) names match are treated as replicates of one another and grouped together before tokenisation. Default set covers: trailing run-order numbers, _T<n>, _B<n>, and _Rep<n> markers.\"\n },\n\n \"patterns\": {\n \"blank\": \"^(blank|blk|solv?blank|matrix.?blank)$\",\n \"iqc\": \"^iqc$\",\n \"eqc\": \"^eqc$\",\n \"standard\": \"^(standard|std\\\\d*|cal(?:ibrator)?\\\\d*|.+standard)$\",\n \"spikeIn\": \"spike.?in\",\n \"stock\": \"^stock$\"\n },\n\n \"standardAllow\": [\n \"ExtractionS\",\n \"IS\",\n \"internal.standard\",\n \"PQC\",\n \"pooledQC\"\n ],\n\n \"matrixVocab\": {\n \"plasma\": \"Plasma\",\n \"serum\": \"Serum\",\n \"tissue\": \"Tissue\",\n \"tissues\": \"Tissues\",\n \"kidney\": \"Kidney\",\n \"liver\": \"Liver\",\n \"tumor\": \"Tumor\",\n \"urine\": \"Urine\",\n \"brain\": \"Brain\",\n \"muscle\": \"Muscle\",\n \"adipose\": \"Adipose\",\n \"csf\": \"CSF\",\n \"feces\": \"Feces\",\n \"faeces\": \"Faeces\",\n \"cells\": \"Cells\",\n \"cell\": \"Cells\",\n \"media\": \"Media\",\n \"medium\": \"Medium\",\n \"extract\": \"Extract\"\n },\n\n \"ionizationModes\": {\n \"pos\": \"POS\",\n \"positive\": \"POS\",\n \"neg\": \"NEG\",\n \"negative\": \"NEG\"\n },\n\n \"tissueParentTokens\": [\"tissue\", \"tissues\"],\n\n \"replicateStripPatterns\": [\n \"[_-]\\\\d{2,4}[A-Za-z]?$\",\n \"[_-]T\\\\d+(?=[_-]|$)\",\n \"[_-]B\\\\d+(?=[_-]|$)\",\n \"[_-](?:rep(?:licate)?)\\\\d+(?=[_-]|$)\"\n ]\n}\n","import type { SampleClass, SampleClassKind, ClassDisposition } from '../../types/auto-group'\nimport { classKey } from './classKey'\nimport vocabData from './vocab.json'\n\nexport interface ClassVocab {\n blankPattern: RegExp\n iqcPattern: RegExp\n eqcPattern: RegExp\n standardPattern: RegExp\n standardAllow: Set<string>\n spikeInPattern: RegExp\n stockPattern: RegExp\n /**\n * Matrix lookup: lowercase token → canonical display name. Matching is\n * case-insensitive, and several aliases can collapse to one display name\n * (cell → Cells, medium → Medium).\n */\n matrixVocab: Map<string, string>\n /** Lowercase polarity tokens → POS / NEG. */\n ionizationModes: Map<string, 'POS' | 'NEG'>\n /** Lowercase tokens that flag the next token as the organ subKind. */\n tissueParentTokens: Set<string>\n}\n\ntype VocabFile = typeof vocabData\ntype IonMode = 'POS' | 'NEG'\n\nfunction compileVocab(data: VocabFile): ClassVocab {\n return {\n blankPattern: new RegExp(data.patterns.blank, 'i'),\n iqcPattern: new RegExp(data.patterns.iqc, 'i'),\n eqcPattern: new RegExp(data.patterns.eqc, 'i'),\n standardPattern: new RegExp(data.patterns.standard, 'i'),\n spikeInPattern: new RegExp(data.patterns.spikeIn, 'i'),\n stockPattern: new RegExp(data.patterns.stock, 'i'),\n standardAllow: new Set(data.standardAllow),\n matrixVocab: new Map(Object.entries(data.matrixVocab)),\n ionizationModes: new Map(\n Object.entries(data.ionizationModes).map(([k, v]) => [k, v as IonMode]),\n ),\n tissueParentTokens: new Set(data.tissueParentTokens),\n }\n}\n\nexport const classVocab: ClassVocab = compileVocab(vocabData)\n\nfunction matrixCanonical(token: string, vocab: ClassVocab): string | undefined {\n return vocab.matrixVocab.get(token.toLowerCase())\n}\n\nfunction isTissueParentToken(token: string, vocab: ClassVocab): boolean {\n return vocab.tissueParentTokens.has(token.toLowerCase())\n}\n\nexport interface DetectClassOptions {\n vocab?: Partial<ClassVocab>\n /** Per-sample explicit `sample_type` hint (e.g. from experiment design data). */\n sampleTypeHints?: (string | undefined)[]\n}\n\nconst DEFAULT_DISPOSITION: Record<SampleClassKind, ClassDisposition> = {\n biological: 'group',\n iqc: 'overlay',\n eqc: 'overlay',\n standard: 'overlay',\n 'spike-in': 'overlay',\n stock: 'overlay',\n blank: 'overlay',\n unknown: 'group',\n}\n\nfunction classifyTokens(\n tokens: string[],\n vocab: ClassVocab,\n): { kind: SampleClassKind; subKind?: string; tagPositions: number[] } {\n // First-match-wins ordering: Blank → IQC → EQC → Standard → SpikeIn → Stock → Biological → Unknown.\n for (let i = 0; i < tokens.length; i++) {\n if (vocab.blankPattern.test(tokens[i])) return { kind: 'blank', tagPositions: [i] }\n }\n for (let i = 0; i < tokens.length; i++) {\n if (vocab.iqcPattern.test(tokens[i])) return { kind: 'iqc', tagPositions: [i] }\n }\n for (let i = 0; i < tokens.length; i++) {\n if (vocab.eqcPattern.test(tokens[i])) return { kind: 'eqc', tagPositions: [i] }\n }\n for (let i = 0; i < tokens.length; i++) {\n if (vocab.standardPattern.test(tokens[i]) || vocab.standardAllow.has(tokens[i])) {\n return { kind: 'standard', tagPositions: [i] }\n }\n }\n for (let i = 0; i < tokens.length; i++) {\n if (vocab.spikeInPattern.test(tokens[i])) return { kind: 'spike-in', tagPositions: [i] }\n }\n for (let i = 0; i < tokens.length; i++) {\n if (vocab.stockPattern.test(tokens[i])) return { kind: 'stock', tagPositions: [i] }\n }\n for (let i = 0; i < tokens.length; i++) {\n const canonical = matrixCanonical(tokens[i], vocab)\n if (canonical) {\n const isTissueParent = isTissueParentToken(tokens[i], vocab)\n const tagPositions = [i]\n if (isTissueParent) {\n // \"Tissues\"/\"Tissue\" is the parent group; tag the next token (organ name) as well.\n if (i + 1 < tokens.length) tagPositions.push(i + 1)\n return { kind: 'biological', subKind: 'Tissues', tagPositions }\n }\n // Non-parent matrix token: tag any immediately preceding \"Tissues\"/\"Tissue\" parent.\n if (i > 0 && isTissueParentToken(tokens[i - 1], vocab)) {\n tagPositions.unshift(i - 1)\n return { kind: 'biological', subKind: 'Tissues', tagPositions }\n }\n return { kind: 'biological', subKind: canonical, tagPositions }\n }\n }\n return { kind: 'unknown', tagPositions: [] }\n}\n\n/**\n * LC-MS batches run positive- and negative-ion-mode acquisitions separately,\n * and the two produce different feature tables that should never be grouped\n * together. When a POS / NEG / positive / negative token is present we suffix\n * it to the class label so the schema builder treats each mode as its own class.\n */\nfunction ionizationModeFor(\n tokens: string[],\n vocab: ClassVocab,\n): { mode: IonMode; position: number } | undefined {\n for (let i = 0; i < tokens.length; i++) {\n const mode = vocab.ionizationModes.get(tokens[i].toLowerCase())\n if (mode) return { mode, position: i }\n }\n return undefined\n}\n\nfunction labelFor(kind: SampleClassKind, subKind?: string): string {\n const base: Record<SampleClassKind, string> = {\n biological: 'Biological',\n iqc: 'IQC',\n eqc: 'EQC',\n standard: 'Standard',\n 'spike-in': 'SpikeIn',\n stock: 'Stock',\n blank: 'Blank',\n unknown: 'Unknown',\n }\n return subKind ? `${base[kind]} / ${subKind}` : base[kind]\n}\n\n/** Classify each tokenized sample into a SampleClass. */\nexport function detectClass(\n tokenizedSamples: string[][],\n options: DetectClassOptions,\n): SampleClass[] {\n const vocab = { ...classVocab, ...(options.vocab ?? {}) }\n const hints = options.sampleTypeHints ?? []\n const buckets = new Map<string, SampleClass>()\n\n for (let i = 0; i < tokenizedSamples.length; i++) {\n const hint = hints[i]?.toLowerCase()\n let kind: SampleClassKind\n let subKind: string | undefined\n let tagPositions: number[]\n\n if (hint === 'qc') {\n kind = 'iqc'\n tagPositions = []\n } else if (hint === 'blank') {\n kind = 'blank'\n tagPositions = []\n } else {\n const r = classifyTokens(tokenizedSamples[i], vocab)\n kind = r.kind\n subKind = r.subKind\n tagPositions = r.tagPositions\n }\n\n // Split classes by ionization mode (POS / NEG) so the two acquisitions get\n // independent schemas even when they share a matrix and sample-type vocab.\n const mode = ionizationModeFor(tokenizedSamples[i], vocab)\n if (mode) {\n subKind = subKind ? `${subKind} / ${mode.mode}` : mode.mode\n if (!tagPositions.includes(mode.position)) tagPositions.push(mode.position)\n }\n\n const key = classKey({ kind, subKind })\n const bucket = buckets.get(key)\n if (bucket) {\n bucket.members.push(i)\n for (const p of tagPositions) {\n if (!bucket.classTagPositions.includes(p)) bucket.classTagPositions.push(p)\n }\n } else {\n buckets.set(key, {\n kind,\n subKind,\n label: labelFor(kind, subKind),\n members: [i],\n classTagPositions: [...tagPositions],\n disposition: DEFAULT_DISPOSITION[kind],\n })\n }\n }\n\n return [...buckets.values()]\n}\n","import type {\n ColumnInfo,\n ColumnRole,\n ClassSchema,\n NumericParse,\n SampleClassKind,\n} from '../../types/auto-group'\n\nconst NUMERIC_VALUE_UNIT = /^(\\d+(?:dot\\d+)?)([A-Za-z%/μ]+)$/\nconst RATIO = /^(\\d+)to(\\d+)$/\nconst RUN_ORDER_PATTERN = /^\\d{2,4}[A-Z]?$/\n// Explicit replicate-token vocabulary: rep1 / Rep2 / REP3 / replicate1 / R1.\n// These tokens almost always identify a within-group replicate dimension that\n// users do NOT want in the default group key — e.g. ctrl/rep1, ctrl/rep2\n// should land in one Ctrl group with two replicates, not two singleton groups.\nconst REPLICATE_TOKEN_PATTERN = /^(?:rep(?:licate)?|r)\\d+$/i\n\n// Real-world LC-MS batches often have 30+ distinct conditions (subject IDs,\n// calibration levels, treatment combinations) in a single class. The earlier\n// cutoff of 20 routed everything above it to `ignore`, leaving such columns\n// out of the default groupBy — meaning a single giant group with no useful\n// split. 50 covers the common cases without making singleton-heavy columns\n// (sample IDs, timestamps) into factors.\nconst DEFAULT_FACTOR_CARDINALITY_MAX = 50\n// Numerics with only a handful of distinct levels (e.g. `5min` / `10min`,\n// `1nM` / `5nM` / `25nM`) read as factor levels by default. Users opt into\n// numeric binning explicitly via the column popover when they want it.\nconst LOW_CARDINALITY_NUMERIC_AS_FACTOR_MAX = 5\n\nexport function parseNumeric(token: string): NumericParse | null {\n const ratio = RATIO.exec(token)\n if (ratio) {\n const num = Number(ratio[1])\n const den = Number(ratio[2])\n if (den === 0) return null\n return { value: num / den, unit: 'ratio', raw: token }\n }\n const m = NUMERIC_VALUE_UNIT.exec(token)\n if (!m) return null\n const valueStr = m[1].replace(/dot/g, '.')\n const value = Number(valueStr)\n if (Number.isNaN(value)) return null\n return { value, unit: m[2], raw: token }\n}\n\nexport interface InferRoleInput {\n values: string[]\n position: number\n totalPositions: number\n classSize: number\n}\n\nexport function inferRole(input: InferRoleInput): ColumnRole {\n const { values, position, totalPositions, classSize } = input\n const unique = new Set(values)\n const cardinality = unique.size\n\n if (cardinality === 1) return 'constant'\n\n // run-order: trailing position, all values match \\d{2,4}[A-Z]? and cardinality is high.\n if (position === totalPositions - 1) {\n const allRunLike = values.every(v => RUN_ORDER_PATTERN.test(v))\n if (allRunLike && cardinality >= classSize * 0.5) return 'run-order'\n }\n\n // numeric: every value parses as value+unit (or ratio).\n const allNumeric = values.every(v => parseNumeric(v) !== null)\n if (allNumeric) {\n // A column like 5min / 10min reads as a factor in practice — users want to\n // group on the timepoint, not bin it. Numeric binning UI is opt-in via the\n // column popover for columns the user explicitly wants binned.\n if (cardinality <= LOW_CARDINALITY_NUMERIC_AS_FACTOR_MAX) return 'factor'\n return 'numeric'\n }\n\n // replicate by explicit name (rep1, Rep2, replicate3, R1) — regardless of\n // cardinality. The cardinality-based ID_PATTERN check that used to live\n // here (allId && cardinality >= classSize * 0.5) was removed: with the\n // replicate pre-grouping pass running upstream, real replicates have\n // already been collapsed by name, so a column like C1/C2/M1/M2 reaching\n // role inference is almost certainly a distinct-condition factor, not a\n // replicate. Users who want a residual replicate column can still mark\n // it via the Workspace popover.\n const allReplicateNamed = values.every(v => REPLICATE_TOKEN_PATTERN.test(v))\n if (allReplicateNamed) return 'replicate'\n\n if (cardinality <= DEFAULT_FACTOR_CARDINALITY_MAX) return 'factor'\n\n return 'ignore'\n}\n\n/**\n * Auto-detect a friendlier column name from the column's values + role.\n * Returns null when nothing matches, so callers can fall back to \"Token N\".\n *\n * Heuristics are content-derived and deterministic. They cover the common\n * LC-MS / proteomics naming conventions; users can override anything via\n * the column popover (rename input).\n */\nfunction inferColumnName(\n values: string[],\n uniqueValues: string[],\n role: ColumnRole,\n): string | null {\n if (uniqueValues.length === 0) return null\n\n // Numeric+unit columns name themselves by the dominant unit category.\n // We tally all values so a mixed-unit column (`1uL`, `1nM`) picks the\n // majority rather than the first sample's unit.\n const allNumeric = (role === 'numeric' || role === 'factor') && values.every(v => parseNumeric(v) !== null)\n if (allNumeric) {\n const categoryFor = (u: string): string | null => {\n const ul = u.toLowerCase()\n if (['min', 'sec', 's', 'hr', 'h', 'd', 'day', 'days', 'wk', 'week', 'hour', 'hours'].includes(ul)) return 'Timepoint'\n if (['nm', 'um', 'mm', 'm', 'molar', 'mol'].includes(ul)) return 'Concentration'\n if (['mg', 'g', 'kg', 'ng', 'ug', 'pg'].includes(ul)) return 'Mass'\n if (['ml', 'l', 'ul', 'nl', 'pl'].includes(ul)) return 'Volume'\n if (ul === '%') return 'Percentage'\n if (['c', 'k', 'celsius', 'kelvin'].includes(ul)) return 'Temperature'\n return null\n }\n const tally = new Map<string, number>()\n for (const v of values) {\n const p = parseNumeric(v)\n const cat = p ? categoryFor(p.unit) : null\n if (cat) tally.set(cat, (tally.get(cat) ?? 0) + 1)\n }\n let best: { cat: string; count: number } | null = null\n for (const [cat, count] of tally) {\n if (!best || count > best.count) best = { cat, count }\n }\n if (best && best.count >= values.length / 2) return best.cat\n }\n\n // Date-like all-digits tokens (YYMMDD or YYYYMMDD).\n if (values.every(v => /^\\d{6}$/.test(v) || /^\\d{8}$/.test(v))) return 'Date'\n\n // Run-order column already has the right semantic role.\n if (role === 'run-order') return 'Run Order'\n\n // Polarity tokens (POS/NEG/positive/negative).\n if (values.every(v => /^(pos|neg|positive|negative)$/i.test(v))) return 'Polarity'\n\n // Common biological matrices — kept in sync with classify.ts matrixVocab.\n if (values.every(v => /^(plasma|serum|tissue|tissues|kidney|liver|tumor|urine|brain|muscle|adipose|csf|feces|faeces|cells?|media|medium|extract)$/i.test(v))) {\n return 'Matrix'\n }\n\n // Experiment / study identifiers (exp268, study42, expt7).\n if (values.every(v => /^(exp|expt|study)\\d+$/i.test(v))) return 'Experiment'\n\n // Operator / Username (2-3 uppercase letters per value, low cardinality).\n // Constant or small-set 2-3-letter all-caps columns are almost always\n // operator initials (LCK, JD, AB). Larger 2-4-letter columns could be\n // project codes — disambiguation happens after the loop via the\n // `Username → Project` post-pass.\n if (uniqueValues.length <= 4 && values.every(v => /^[A-Z]{2,3}$/.test(v))) return 'Username'\n\n return null\n}\n\nexport function buildClassSchema(\n tokenizedMembers: string[][],\n classKind: SampleClassKind,\n subKind: string | undefined,\n classTagPositions: number[],\n): ClassSchema {\n const totalPositions = tokenizedMembers[0]?.length ?? 0\n const classSize = tokenizedMembers.length\n\n const columns: ColumnInfo[] = []\n const tagSet = new Set(classTagPositions)\n\n for (let pos = 0; pos < totalPositions; pos++) {\n const values = tokenizedMembers.map(t => t[pos] ?? '')\n const uniqueValues = [...new Set(values)]\n\n let role: ColumnRole\n if (tagSet.has(pos) && uniqueValues.length === 1) {\n // Only constant tag positions become class-tag; multi-valued positions (e.g. organ\n // type within a Tissues class) fall through to normal role inference so they can be\n // used as factor columns for grouping.\n role = 'class-tag'\n } else {\n role = inferRole({ values, position: pos, totalPositions, classSize })\n }\n\n const numeric =\n role === 'numeric'\n ? {\n byValue: Object.fromEntries(\n uniqueValues\n .map(v => [v, parseNumeric(v)] as const)\n .filter((e): e is readonly [string, NumericParse] => e[1] !== null),\n ),\n }\n : undefined\n\n const inferredName = inferColumnName(values, uniqueValues, role)\n columns.push({\n index: pos,\n name: inferredName ?? `Token ${pos + 1}`,\n sourceIndices: [pos],\n uniqueValues,\n cardinality: uniqueValues.length,\n role,\n numeric,\n // Legacy field\n type: pos === 0 ? 'prefix' : 'suffix',\n })\n }\n\n // Disambiguate Username when there are multiple constant 2-4 letter columns:\n // first hit stays \"Username\", subsequent ones become \"Project\".\n let usernameAssigned = false\n for (const c of columns) {\n if (c.name === 'Username') {\n if (usernameAssigned) c.name = 'Project'\n else usernameAssigned = true\n }\n }\n\n const groupBy = columns.filter(c => c.role === 'factor').map(c => c.index)\n\n return { classKind, subKind, columns, groupBy }\n}\n","import type { ClassSchema, MergeSuggestion } from '../../types/auto-group'\n\nconst THRESHOLD = 0.95\nconst MIN_CLASS_SIZE = 4\n\nfunction functionalDependency(rows: string[][], srcIdx: number, dstIdx: number): number {\n const map = new Map<string, Set<string>>()\n for (const row of rows) {\n const a = row[srcIdx] ?? ''\n const b = row[dstIdx] ?? ''\n const set = map.get(a)\n if (set) set.add(b)\n else map.set(a, new Set([b]))\n }\n if (map.size === 0) return 0\n let functional = 0\n for (const set of map.values()) if (set.size === 1) functional++\n return functional / map.size\n}\n\nexport function findMerges(schema: ClassSchema, rows: string[][]): MergeSuggestion[] {\n if (rows.length < MIN_CLASS_SIZE) return []\n\n const out: MergeSuggestion[] = []\n for (let i = 0; i < schema.columns.length - 1; i++) {\n const a = schema.columns[i]\n const b = schema.columns[i + 1]\n if (a.role !== 'factor' && b.role !== 'factor') continue\n\n const forward = functionalDependency(rows, a.index, b.index)\n const backward = functionalDependency(rows, b.index, a.index)\n if (forward < THRESHOLD || backward < THRESHOLD) continue\n\n const merged = new Set(rows.map(r => `${r[a.index] ?? ''}_${r[b.index] ?? ''}`))\n if (merged.size < 2) continue\n\n out.push({\n classKind: schema.classKind,\n subKind: schema.subKind,\n columnIndices: [a.index, b.index],\n proposedName: `Column ${a.index + 1}–${b.index + 1}`,\n confidence: Math.min(forward, backward),\n })\n }\n return out\n}\n","export const DEFAULT_COLORS = [\n '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6',\n '#EC4899', '#06B6D4', '#84CC16', '#F97316', '#6366F1',\n]\n\nexport const QC_OVERLAY_COLOR = '#6B7280'\n","import type {\n AutoGroupResult,\n ClassSchema,\n ColumnInfo,\n MetadataRow,\n NumericBinning,\n SampleClass,\n} from '../../types/auto-group'\nimport type { SampleGroup } from '../../types/components'\nimport { DEFAULT_COLORS, QC_OVERLAY_COLOR } from './colors'\nimport { classKey } from './classKey'\n\nexport interface ComposeInput {\n tokenizedSamples: string[][]\n sampleNames: string[]\n schemas: Record<string, ClassSchema>\n classes: SampleClass[]\n}\n\n/** Apply the operation pipeline to a single raw token value:\n * numeric-parse → alias → exclude → bin\n * Returns null if the row is excluded.\n */\nfunction applyOps(col: ColumnInfo, raw: string): string | null {\n let value = raw\n\n // alias (operates on raw source value)\n if (col.ops?.alias && raw in col.ops.alias) {\n value = col.ops.alias[raw]\n }\n\n // exclude (post-alias)\n if (col.ops?.exclude?.includes(raw) || col.ops?.exclude?.includes(value)) {\n return null\n }\n\n // bin (only when role === 'numeric' and binning.mode === 'binned')\n if (col.role === 'numeric' && col.binning?.mode === 'binned') {\n const parsed = col.numeric?.byValue[raw]\n if (parsed && col.binning.edges) {\n const bin = pickBin(parsed.value, col.binning)\n if (bin !== null) value = bin\n }\n }\n\n return value\n}\n\nfunction pickBin(value: number, b: NumericBinning): string | null {\n if (!b.edges || b.edges.length === 0) return null\n for (let i = 0; i < b.edges.length; i++) {\n if (value < b.edges[i]) {\n return b.labels?.[i] ?? `< ${b.edges[i]}`\n }\n }\n const last = b.edges[b.edges.length - 1]\n return b.labels?.[b.edges.length] ?? `> ${last}`\n}\n\nexport function composeGroups(input: ComposeInput): AutoGroupResult {\n const experimentalGroups: SampleGroup[] = []\n const qcGroups: SampleGroup[] = []\n const excludedSamples: string[] = []\n const metadata: MetadataRow[] = []\n let colorIdx = 0\n\n for (const cls of input.classes) {\n const schema = input.schemas[classKey(cls)]\n if (!schema) continue\n\n if (cls.disposition === 'exclude') {\n for (const m of cls.members) excludedSamples.push(input.sampleNames[m])\n continue\n }\n\n // Per-class group map. group key built from enabled (groupBy) columns,\n // joined with ' / ' for hierarchical display.\n const groupMap = new Map<string, { name: string; samples: string[] }>()\n const colByIdx = new Map(schema.columns.map(c => [c.index, c]))\n\n for (const m of cls.members) {\n const tokens = input.tokenizedSamples[m]\n const rowName = input.sampleNames[m]\n let excluded = false\n const fieldValues: Record<string, string> = {}\n\n for (const col of schema.columns) {\n const sourceValues = col.sourceIndices.map(idx => tokens[idx] ?? '')\n const raw = sourceValues.join('_') // merged columns join their source tokens\n const display = applyOps(col, raw)\n if (display === null) {\n excluded = true\n break\n }\n fieldValues[col.displayName ?? col.name] = display\n }\n\n if (excluded) {\n excludedSamples.push(rowName)\n continue\n }\n\n const keyParts: string[] =\n cls.disposition === 'group'\n ? schema.groupBy.map(idx => {\n const col = colByIdx.get(idx)!\n return fieldValues[col.displayName ?? col.name]\n })\n : [cls.label]\n\n // Prepend the class label to the factor-key parts so group names carry\n // class context (e.g. \"Biological / Tissues / POS / 5min\" instead of a\n // bare \"5min\" that collides with another class's \"5min\").\n //\n // Only fires for disposition === 'group' classes with actual factor\n // keyParts: overlay/exclude classes already use cls.label AS their\n // single key part, so prefixing again would produce \"IQC / IQC\". The\n // Unknown class also opts out — its label (\"Unknown\") adds no signal.\n const useClassPrefix =\n cls.disposition === 'group' && cls.kind !== 'unknown' && keyParts.length > 0\n const groupKey = useClassPrefix\n ? `${cls.label} / ${keyParts.join(' / ')}`\n : keyParts.length > 0\n ? keyParts.join(' / ')\n : cls.label\n\n const bucket = groupMap.get(groupKey)\n if (bucket) {\n bucket.samples.push(rowName)\n } else {\n groupMap.set(groupKey, { name: groupKey, samples: [rowName] })\n }\n\n metadata.push({ sampleName: rowName, fields: fieldValues, group: groupKey })\n }\n\n for (const { name, samples } of groupMap.values()) {\n const color =\n cls.disposition === 'overlay'\n ? QC_OVERLAY_COLOR\n : DEFAULT_COLORS[colorIdx++ % DEFAULT_COLORS.length]\n const g: SampleGroup = { name, color, samples }\n if (cls.disposition === 'overlay') qcGroups.push(g)\n else experimentalGroups.push(g)\n }\n }\n\n return {\n groups: [...experimentalGroups, ...qcGroups],\n experimentalGroups,\n qcGroups,\n metadata,\n excludedSamples,\n schemas: Object.values(input.schemas),\n }\n}\n","import type { ClassSchema, SchemaFingerprint } from '../../types/auto-group'\n\nexport function serializeFingerprint(schemas: ClassSchema[]): SchemaFingerprint {\n return {\n version: 1,\n classes: schemas.map(s => ({\n kind: s.classKind,\n subKind: s.subKind,\n columns: s.columns.map(c => ({\n name: c.displayName ?? c.name,\n role: c.role ?? 'factor',\n sourceIndices: c.sourceIndices,\n binning: c.binning,\n ops: c.ops,\n })),\n groupBy: s.groupBy,\n })),\n }\n}\n\nexport function restoreFingerprint(\n fp: SchemaFingerprint,\n current: ClassSchema[],\n): ClassSchema[] {\n return fp.classes.map(snap => {\n const target = current.find(c => c.classKind === snap.kind && c.subKind === snap.subKind)\n if (!target) {\n throw new Error(\n `Fingerprint class not present in current input: ${snap.kind}${snap.subKind ? '/' + snap.subKind : ''}`,\n )\n }\n if (target.columns.length !== snap.columns.length) {\n throw new Error(\n `Saved schema expects ${snap.columns.length} columns, current data has ${target.columns.length} for class ${snap.kind}${snap.subKind ? '/' + snap.subKind : ''}. column count mismatch`,\n )\n }\n return {\n ...target,\n groupBy: snap.groupBy,\n columns: target.columns.map((col, i) => ({\n ...col,\n displayName: snap.columns[i].name,\n role: snap.columns[i].role,\n binning: snap.columns[i].binning,\n ops: snap.columns[i].ops,\n })),\n }\n })\n}\n","import type { ClassSchema, ColumnInfo, SampleClass } from '../../types/auto-group'\nimport { classKey } from './classKey'\n\nexport interface TemplateOptions {\n mode: 'blank' | 'prefilled'\n format: 'csv' | 'tsv'\n /** For prefilled mode. Indexed parallel to `samples`. */\n tokenizedSamples?: string[][]\n /** Used to compute the filename; omitted → falls back to `sample_metadata_<date>`. */\n experimentCode?: string\n /** Override the blank-mode suggested headers. Defaults to ['group','class','replicate','notes']. */\n suggestedColumns?: string[]\n}\n\nconst DEFAULT_BLANK_HEADERS = ['group', 'class', 'replicate', 'notes']\n\nfunction rfc4180Quote(cell: string): string {\n if (/[\",\\n]/.test(cell)) {\n return `\"${cell.replace(/\"/g, '\"\"')}\"`\n }\n return cell\n}\n\nfunction joinRow(cells: string[], sep: string): string {\n return sep === ','\n ? cells.map(rfc4180Quote).join(',')\n : cells.join('\\t')\n}\n\nfunction today(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nexport function composeTemplate(\n samples: string[],\n schemas: ClassSchema[] | null,\n classes: SampleClass[] | null,\n options: TemplateOptions,\n): { content: string; filename: string } {\n const sep = options.format === 'tsv' ? '\\t' : ','\n const ext = options.format === 'tsv' ? 'tsv' : 'csv'\n\n const stem = options.experimentCode ? `${options.experimentCode}_metadata` : 'sample_metadata'\n const filename = `${stem}_${today()}.${ext}`\n\n if (options.mode === 'blank' || !schemas || !classes) {\n const headers = ['sample_name', ...(options.suggestedColumns ?? DEFAULT_BLANK_HEADERS)]\n const rows = samples.map(s => {\n const r = [s, ...Array(headers.length - 1).fill('')]\n return joinRow(r, sep)\n })\n return { content: [joinRow(headers, sep), ...rows].join('\\n'), filename }\n }\n\n // Prefilled mode: union of all class columns, wide layout.\n const tokenized = options.tokenizedSamples ?? []\n const allColumns: { schemaKey: string; colName: string }[] = []\n\n // Hoist: build lookup maps before the per-sample loop.\n const sampleToClass = new Map<number, SampleClass>()\n for (const cls of classes) for (const m of cls.members) sampleToClass.set(m, cls)\n\n const schemaByKey = new Map<string, ClassSchema>()\n for (const s of schemas) {\n const k = s.subKind ? `${s.classKind}:${s.subKind}` : s.classKind\n schemaByKey.set(k, s)\n }\n\n const colByNameByKey = new Map<string, Map<string, ColumnInfo>>()\n const groupBySetByKey = new Map<string, Set<number>>()\n for (const s of schemas) {\n const k = s.subKind ? `${s.classKind}:${s.subKind}` : s.classKind\n const m = new Map<string, ColumnInfo>()\n for (const c of s.columns) m.set(c.displayName ?? c.name, c)\n colByNameByKey.set(k, m)\n groupBySetByKey.set(k, new Set(s.groupBy))\n // Collect allColumns with dedup via Set\n const seenColNames = new Set<string>()\n for (const c of s.columns) {\n const name = c.displayName ?? c.name\n if (!seenColNames.has(name)) {\n seenColNames.add(name)\n if (!allColumns.some(x => x.colName === name)) {\n allColumns.push({ schemaKey: k, colName: name })\n }\n }\n }\n }\n\n const headers = ['sample_name', 'class', ...allColumns.map(c => c.colName), 'group', 'notes']\n\n const rows: string[] = []\n for (let i = 0; i < samples.length; i++) {\n const cls = sampleToClass.get(i)\n const classLabel = cls?.label ?? ''\n const tokens = tokenized[i] ?? []\n const cells: string[] = [samples[i], classLabel]\n\n if (cls) {\n const k = classKey(cls)\n const groupBySet = groupBySetByKey.get(k)\n const colByName: Map<string, ColumnInfo> | undefined = colByNameByKey.get(k)\n const keyParts: string[] = []\n for (const col of allColumns) {\n if (col.schemaKey === k && colByName) {\n const schemaCol = colByName.get(col.colName)\n const raw =\n schemaCol?.sourceIndices.map(idx => tokens[idx] ?? '').join('_') ?? ''\n cells.push(raw)\n if (schemaCol && groupBySet?.has(schemaCol.index)) keyParts.push(raw)\n } else {\n cells.push('')\n }\n }\n cells.push(keyParts.join(' / '))\n } else {\n for (const _ of allColumns) cells.push('')\n cells.push('')\n }\n\n cells.push('') // notes\n rows.push(joinRow(cells, sep))\n }\n\n return { content: [joinRow(headers, sep), ...rows].join('\\n'), filename }\n}\n","/**\n * Replicate pre-grouping.\n *\n * Strip the replicate / injection-number markers from each sample's name, then\n * collapse samples that share a base name. Those samples ARE replicates of one\n * another by definition; the remaining unique base names are what the rest of\n * the pipeline (tokenize → classify → schema → compose) should operate on.\n *\n * This pre-pass replaces the older \"treat every sample as independent, then\n * infer replicate columns\" approach. Identifying replicate-equivalence by name\n * before tokenisation removes a class of fragile cardinality heuristics.\n */\n\nimport vocabData from './vocab.json'\n\nconst STRIP_PATTERNS: RegExp[] = (vocabData.replicateStripPatterns as string[]).map(\n src => new RegExp(src, 'gi'),\n)\n\n/**\n * Iteratively strip every replicate / injection-number marker from `name`.\n * Each pattern is applied repeatedly until it stops matching, so a sample like\n * `study_T1_Rep2_004` collapses to `study` in one call.\n */\nexport function stripReplicateTokens(name: string): string {\n let base = name\n for (const re of STRIP_PATTERNS) {\n let prev\n do {\n prev = base\n base = base.replace(re, '')\n re.lastIndex = 0\n } while (base !== prev)\n }\n return base\n}\n\nexport interface ReplicatePreGrouping {\n /** Unique base names in first-seen order. Length === `originalSamples.length` only if every sample has a distinct base. */\n baseNames: string[]\n /**\n * `membersByBase[i]` = original-sample indices that collapsed onto `baseNames[i]`.\n * Length of each entry is the replicate count for that base.\n */\n membersByBase: number[][]\n /** Original sample names indexed by their position in `samples`. Retained so the compose layer can expand groups back to originals. */\n originalSamples: string[]\n}\n\nexport function preGroupReplicates(samples: string[]): ReplicatePreGrouping {\n const map = new Map<string, number[]>()\n const baseOrder: string[] = []\n for (let i = 0; i < samples.length; i++) {\n const base = stripReplicateTokens(samples[i])\n const bucket = map.get(base)\n if (bucket) {\n bucket.push(i)\n } else {\n map.set(base, [i])\n baseOrder.push(base)\n }\n }\n return {\n baseNames: baseOrder,\n membersByBase: baseOrder.map(b => map.get(b)!),\n originalSamples: samples,\n }\n}\n\n/**\n * Expand groups produced over base names back to groups of original sample\n * names. Each entry in `group.samples` is treated as a base name and replaced\n * by the list of original samples that collapsed onto it.\n */\nexport function expandGroupsWithReplicates<G extends { samples: string[] }>(\n groups: G[],\n preGrouping: ReplicatePreGrouping,\n): G[] {\n const baseToMembers = new Map<string, number[]>()\n for (let i = 0; i < preGrouping.baseNames.length; i++) {\n baseToMembers.set(preGrouping.baseNames[i], preGrouping.membersByBase[i])\n }\n return groups.map(g => ({\n ...g,\n samples: g.samples.flatMap(baseName => {\n const indices = baseToMembers.get(baseName)\n return indices ? indices.map(i => preGrouping.originalSamples[i]) : [baseName]\n }),\n }))\n}\n","import type { SelectOption } from '../types'\n\nexport interface ExperimentDesignDataResponse {\n experiment_id?: number\n plugin_id?: string\n data?: Record<string, unknown>\n schema_version?: string\n updated_at?: string | null\n}\n\nexport interface ExtractExperimentSamplesOptions {\n includeControls?: boolean\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction isPlatformDesignDataResponse(value: Record<string, unknown>): boolean {\n return (\n 'data' in value\n && (\n 'experiment_id' in value\n || 'plugin_id' in value\n || 'schema_version' in value\n || 'updated_at' in value\n )\n )\n}\n\n/** Return the plugin-defined design_data payload from common platform and plugin response shapes. */\nexport function unwrapExperimentDesignData(\n rawData: Record<string, unknown> | null | undefined,\n): Record<string, unknown> | null {\n if (!rawData) return null\n\n if (isRecord(rawData.data) && isPlatformDesignDataResponse(rawData)) {\n return rawData.data\n }\n\n if (isRecord(rawData.design_data)) {\n return rawData.design_data\n }\n\n if (isRecord(rawData.designData)) {\n return rawData.designData\n }\n\n return rawData\n}\n\nfunction shouldIncludeSample(record: Record<string, unknown>, options: ExtractExperimentSamplesOptions): boolean {\n if (options.includeControls) return true\n const rawType = record.sample_type ?? record.sampleType ?? record.type ?? record.well_type ?? record.wellType\n const type = typeof rawType === 'string' ? rawType.toLowerCase() : ''\n return !['blank', 'qc'].includes(type)\n}\n\nfunction readSampleValue(record: Record<string, unknown>): string | null {\n const candidates = [\n record.sample_name,\n record.sampleName,\n record.sample_id,\n record.sampleId,\n record.name,\n record.id,\n record.label,\n ]\n\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.trim()) {\n return candidate\n }\n if (typeof candidate === 'number' && Number.isFinite(candidate)) {\n return String(candidate)\n }\n }\n\n return null\n}\n\nfunction readSampleLabel(record: Record<string, unknown>, fallback: string): string {\n const label = record.name ?? record.label ?? record.sample_name ?? record.sampleName\n return typeof label === 'string' && label.trim() ? label : fallback\n}\n\nfunction readSampleDescription(record: Record<string, unknown>): string | undefined {\n const parts = [\n record.group,\n record.batch,\n record.batch_id,\n record.batchId,\n record.plate_id,\n record.plateId,\n record.well_id,\n record.wellId,\n ].filter((value): value is string | number =>\n (typeof value === 'string' && value.trim().length > 0)\n || typeof value === 'number',\n )\n\n return parts.length > 0 ? parts.map(String).join(' / ') : undefined\n}\n\nfunction addSampleRecord(\n options: SelectOption<string>[],\n seen: Set<string>,\n value: unknown,\n extractOptions: ExtractExperimentSamplesOptions,\n): void {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed && !seen.has(trimmed)) {\n seen.add(trimmed)\n options.push({ value: trimmed, label: trimmed })\n }\n return\n }\n\n if (!isRecord(value) || !shouldIncludeSample(value, extractOptions)) return\n\n const sampleValue = readSampleValue(value)\n if (!sampleValue || seen.has(sampleValue)) return\n\n seen.add(sampleValue)\n options.push({\n value: sampleValue,\n label: readSampleLabel(value, sampleValue),\n description: readSampleDescription(value),\n })\n}\n\nfunction addSampleArray(\n options: SelectOption<string>[],\n seen: Set<string>,\n value: unknown,\n extractOptions: ExtractExperimentSamplesOptions,\n): void {\n if (!Array.isArray(value)) return\n for (const sample of value) {\n addSampleRecord(options, seen, sample, extractOptions)\n }\n}\n\n/** Extract selectable sample options from raw or wrapped experiment design_data. */\nexport function extractSampleOptionsFromDesignData(\n rawData: Record<string, unknown> | null | undefined,\n options: ExtractExperimentSamplesOptions = {},\n): SelectOption<string>[] {\n const designData = unwrapExperimentDesignData(rawData)\n if (!designData) return []\n\n const sampleOptions: SelectOption<string>[] = []\n const seen = new Set<string>()\n\n addSampleArray(sampleOptions, seen, designData.samples, options)\n\n if (isRecord(designData.templates)) {\n for (const template of Object.values(designData.templates)) {\n if (!isRecord(template)) continue\n const templateData = isRecord(template.data) ? template.data : template\n addSampleArray(sampleOptions, seen, templateData.samples, options)\n }\n }\n\n if (Array.isArray(designData.plates)) {\n for (const plate of designData.plates) {\n if (!isRecord(plate)) continue\n addSampleArray(sampleOptions, seen, plate.samples, options)\n }\n }\n\n return sampleOptions\n}\n\n/** Extract SampleSelector-compatible string values from raw or wrapped experiment design_data. */\nexport function extractSampleNamesFromDesignData(\n rawData: Record<string, unknown> | null | undefined,\n options: ExtractExperimentSamplesOptions = {},\n): string[] {\n return extractSampleOptionsFromDesignData(rawData, options).map(sample => sample.value)\n}\n","import type { ParsedCsvData } from '../../types/auto-group'\n\nfunction parseCSVLine(line: string, delimiter: string = ','): string[] {\n const result: string[] = []\n let current = ''\n let inQuotes = false\n for (let i = 0; i < line.length; i++) {\n const char = line[i]\n if (char === '\"') {\n if (inQuotes && line[i + 1] === '\"') {\n current += '\"'\n i++\n } else {\n inQuotes = !inQuotes\n }\n } else if (char === delimiter && !inQuotes) {\n result.push(current.trim())\n current = ''\n } else {\n current += char\n }\n }\n result.push(current.trim())\n return result\n}\n\nexport function parseCSV(text: string): ParsedCsvData {\n const lines = text.trim().split('\\n')\n if (lines.length < 2) throw new Error('CSV must have at least a header and one data row')\n const firstLine = lines[0]\n const csvDelimiter = firstLine.includes('\\t') ? '\\t' : ','\n const headers = parseCSVLine(firstLine, csvDelimiter)\n const rows: Record<string, string>[] = []\n for (let i = 1; i < lines.length; i++) {\n const values = parseCSVLine(lines[i], csvDelimiter)\n if (values.length !== headers.length) continue\n const row: Record<string, string> = {}\n headers.forEach((h, idx) => { row[h] = values[idx] })\n rows.push(row)\n }\n const sampleKeywords = ['sample', 'name', 'id', 'sample_name', 'samplename', 'file name', 'filename', 'file_name']\n const sampleColumn = headers.find(h => sampleKeywords.includes(h.toLowerCase())) ?? headers[0]\n return { columns: headers, rows, sampleColumn, delimiter: csvDelimiter }\n}\n","import { computed, ref } from 'vue'\nimport type {\n AutoGroupResult,\n ClassDisposition,\n ClassSchema,\n ColumnInfo,\n ColumnRole,\n InputMode,\n MergeSuggestion,\n NumericBinning,\n OutlierAction,\n OutlierInfo,\n ParsedCsvData,\n SampleClass,\n SchemaFingerprint,\n ValueOps,\n} from '../types/auto-group'\nimport {\n buildClassSchema,\n classKey,\n composeGroups,\n composeTemplate,\n detectClass,\n expandGroupsWithReplicates,\n findMerges,\n pickPrimaryDelimiter,\n preGroupReplicates,\n restoreFingerprint,\n serializeFingerprint,\n splitMulti,\n type ReplicatePreGrouping,\n type TemplateOptions,\n} from './autoGroup'\nimport { unwrapExperimentDesignData } from './experimentDesignData'\n\nexport { DEFAULT_COLORS } from './autoGroup'\n\nlet deprecationWarned = false\nlet outlierDeprecationWarned = false\nfunction warnDeprecated(api: string) {\n if (deprecationWarned) return\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') return\n console.warn(\n `[useAutoGroup] ${api} is deprecated; use the new class-first API (setClassDisposition / toggleGroupBy / setValueOps).`,\n )\n deprecationWarned = true\n}\n\n// Re-export `parseCSV` for callers that imported it from this file.\nexport { parseCSV } from './autoGroup/csv-shim'\n\nexport function useAutoGroup() {\n const inputMode = ref<InputMode>('paste')\n const rawText = ref('')\n const csvData = ref<ParsedCsvData | null>(null)\n const sampleTypeHints = ref<(string | undefined)[]>([])\n\n const classes = ref<SampleClass[]>([])\n const schemas = ref<Record<string, ClassSchema>>({})\n const activeClassKey = ref<string>('')\n\n const samples = computed(() => {\n if (csvData.value) {\n return csvData.value.rows.map(r => r[csvData.value!.sampleColumn] ?? '')\n }\n return rawText.value.split('\\n').map(l => l.trim()).filter(l => l.length > 0)\n })\n\n const tokenized = ref<string[][]>([])\n const delimiter = ref<'_' | '-' | '.'>('_')\n // Replicate pre-grouping: strip injection-number / T<n> / B<n> / Rep<n> markers,\n // collapse samples with the same base name into one \"row\" for tokenisation,\n // then expand the resulting groups back to the original samples in `result`.\n // null when the paste path hasn't run (CSV path keeps the old behaviour for now).\n const preGrouping = ref<ReplicatePreGrouping | null>(null)\n\n function parseInput() {\n if ((inputMode.value === 'csv' || inputMode.value === 'experiment') && csvData.value) {\n parseInputFromCsv()\n return\n }\n parseInputFromPaste()\n }\n\n function parseInputFromCsv() {\n // CSV input already exposes structured columns, so replicate pre-grouping\n // isn't needed (and would interfere with header-aware role inference).\n preGrouping.value = null\n const csv = csvData.value!\n const nonSampleCols = csv.columns.filter(c => c !== csv.sampleColumn && c !== 'sample_type')\n if (nonSampleCols.length === 0) {\n // No metadata columns — degenerate, single Unknown class\n tokenized.value = csv.rows.map(() => [])\n classes.value = [{\n kind: 'unknown',\n label: 'Unknown',\n members: csv.rows.map((_, i) => i),\n classTagPositions: [],\n disposition: 'group',\n }]\n schemas.value = { unknown: { classKind: 'unknown', columns: [], groupBy: [] } }\n activeClassKey.value = 'unknown'\n return\n }\n const tokens = csv.rows.map(r => nonSampleCols.map(col => r[col] ?? ''))\n tokenized.value = tokens\n // Class detection: use explicit sample_type hint from csv.rows[i].sample_type\n const hints = csv.rows.map(r => {\n const t = String(r['sample_type'] ?? '').toLowerCase()\n if (t === 'qc') return 'qc'\n if (t === 'blank') return 'blank'\n return undefined\n })\n const detected = detectClass(tokens, { sampleTypeHints: hints })\n classes.value = detected\n // Build schema per class with CSV header names\n const newSchemas: Record<string, ClassSchema> = {}\n for (const cls of detected) {\n const memberTokens = cls.members.map(i => tokens[i])\n const schema = buildClassSchema(memberTokens, cls.kind, cls.subKind, cls.classTagPositions)\n // Override default Token N names with actual CSV header names\n schema.columns = schema.columns.map((c, i) => ({\n ...c,\n name: nonSampleCols[i] ?? c.name,\n originalName: nonSampleCols[i],\n }))\n newSchemas[classKey(cls)] = schema\n }\n schemas.value = newSchemas\n if (detected.length > 0) activeClassKey.value = classKey(detected[0])\n }\n\n function parseInputFromPaste() {\n const lines = samples.value\n if (lines.length === 0) {\n classes.value = []\n schemas.value = {}\n preGrouping.value = null\n return\n }\n // Replicate pre-pass: strip run-order / T<n> / B<n> / Rep<n> markers, then\n // collapse samples whose stripped names match into one base entry. The\n // tokenize → classify → schema pipeline runs on the *base* names; the\n // `result` computed expands each resulting group back to the original\n // samples via `expandGroupsWithReplicates`.\n const pre = preGroupReplicates(lines)\n preGrouping.value = pre\n\n const d = pickPrimaryDelimiter(pre.baseNames)\n delimiter.value = d\n tokenized.value = pre.baseNames.map(l => splitMulti(l, d))\n // sampleTypeHints align to original samples, so pick the first sample's\n // hint per base group as the representative hint.\n const hints = pre.membersByBase.map(members => sampleTypeHints.value[members[0]])\n const detected = detectClass(tokenized.value, { sampleTypeHints: hints })\n classes.value = detected\n const newSchemas: Record<string, ClassSchema> = {}\n for (const cls of detected) {\n const memberTokens = cls.members.map(i => tokenized.value[i])\n newSchemas[classKey(cls)] = buildClassSchema(\n memberTokens,\n cls.kind,\n cls.subKind,\n cls.classTagPositions,\n )\n }\n schemas.value = newSchemas\n if (detected.length > 0) {\n activeClassKey.value = classKey(detected[0])\n }\n }\n\n const activeSchema = computed<ClassSchema | null>(() => schemas.value[activeClassKey.value] ?? null)\n\n const suggestions = computed<MergeSuggestion[]>(() => {\n const schema = activeSchema.value\n if (!schema) return []\n const activeClass = classes.value.find(\n c => classKey(c) === activeClassKey.value,\n )\n if (!activeClass) return []\n const memberTokens = activeClass.members.map(i => tokenized.value[i])\n return findMerges(schema, memberTokens)\n })\n\n const result = computed<AutoGroupResult>(() => {\n const pre = preGrouping.value\n const composed = composeGroups({\n tokenizedSamples: tokenized.value,\n // After the pre-pass, sampleNames passed to compose are the *base* names\n // (one per replicate group); fall back to the original samples when\n // pre-grouping hasn't run (e.g. the CSV path or empty input).\n sampleNames: pre ? pre.baseNames : samples.value,\n schemas: schemas.value,\n classes: classes.value,\n })\n if (!pre) return composed\n // Expand each group's `samples` from base names back to the original\n // sample names so the Preview panel shows real filenames, not stripped\n // bases, and so `metadata` reflects each input row exactly once.\n return {\n ...composed,\n groups: expandGroupsWithReplicates(composed.groups, pre),\n experimentalGroups: composed.experimentalGroups\n ? expandGroupsWithReplicates(composed.experimentalGroups, pre)\n : undefined,\n qcGroups: composed.qcGroups\n ? expandGroupsWithReplicates(composed.qcGroups, pre)\n : undefined,\n }\n })\n\n const fingerprint = computed<SchemaFingerprint>(() =>\n serializeFingerprint(result.value.schemas ?? []),\n )\n\n const groups = computed(() => result.value.groups)\n const qcGroups = computed(() => result.value.qcGroups ?? [])\n const metadata = computed(() => result.value.metadata)\n const excludedSamples = computed(() => result.value.excludedSamples)\n\n // ----- Mutators -----\n\n function setClassDisposition(key: string, disposition: ClassDisposition) {\n const target = classes.value.find(c => classKey(c) === key)\n if (!target || target.disposition === disposition) return\n classes.value = classes.value.map(c =>\n classKey(c) === key ? { ...c, disposition } : c,\n )\n }\n\n function setClassDispositions(\n predicate: (c: SampleClass) => boolean,\n disposition: ClassDisposition,\n ) {\n let changed = false\n const next = classes.value.map(c => {\n if (predicate(c) && c.disposition !== disposition) {\n changed = true\n return { ...c, disposition }\n }\n return c\n })\n if (changed) classes.value = next\n }\n\n function toggleGroupBy(key: string, idx: number) {\n const schema = schemas.value[key]\n if (!schema) return\n const set = new Set(schema.groupBy)\n if (set.has(idx)) set.delete(idx)\n else set.add(idx)\n schemas.value = {\n ...schemas.value,\n [key]: { ...schema, groupBy: [...set].sort((a, b) => a - b) },\n }\n }\n\n function updateColumn(key: string, idx: number, patch: Partial<ColumnInfo>) {\n const schema = schemas.value[key]\n if (!schema) return\n const col = schema.columns.find(c => c.index === idx)\n if (!col) return\n let changed = false\n for (const k of Object.keys(patch) as (keyof ColumnInfo)[]) {\n if ((col as unknown as Record<string, unknown>)[k as string] !== (patch as unknown as Record<string, unknown>)[k as string]) {\n changed = true\n break\n }\n }\n if (!changed) return\n schemas.value = {\n ...schemas.value,\n [key]: {\n ...schema,\n columns: schema.columns.map(c => (c.index === idx ? { ...c, ...patch } : c)),\n },\n }\n }\n\n function setRole(key: string, idx: number, role: ColumnRole) {\n updateColumn(key, idx, { role })\n }\n\n function setColumnName(key: string, idx: number, name: string) {\n updateColumn(key, idx, { displayName: name })\n }\n\n function setValueOps(key: string, idx: number, ops: ValueOps) {\n updateColumn(key, idx, { ops })\n }\n\n function setBinning(key: string, idx: number, binning: NumericBinning) {\n updateColumn(key, idx, { binning })\n }\n\n function mergeColumns(key: string, indices: number[]) {\n const schema = schemas.value[key]\n if (!schema || indices.length < 2) return\n const sorted = [...indices].sort((a, b) => a - b)\n const start = sorted[0]\n const merged: ColumnInfo = {\n index: start,\n name: `Column ${sorted.map(i => i + 1).join('–')}`,\n sourceIndices: sorted.flatMap(i => schema.columns.find(c => c.index === i)?.sourceIndices ?? [i]),\n uniqueValues: [],\n cardinality: 0,\n role: 'factor',\n }\n const dropped = new Set(sorted)\n schemas.value = {\n ...schemas.value,\n [key]: {\n ...schema,\n columns: [merged, ...schema.columns.filter(c => !dropped.has(c.index))]\n .sort((a, b) => a.index - b.index),\n groupBy: schema.groupBy.filter(i => !dropped.has(i) || i === start),\n },\n }\n }\n\n function loadFingerprint(fp: SchemaFingerprint) {\n const restored = restoreFingerprint(fp, Object.values(schemas.value))\n const next: Record<string, ClassSchema> = {}\n for (const s of restored) next[classKey({ kind: s.classKind, subKind: s.subKind })] = s\n schemas.value = next\n }\n\n // ----- Template download -----\n\n const canDownloadTemplate = computed(() => samples.value.length > 0)\n\n function downloadTemplate(mode: 'blank' | 'prefilled' = 'prefilled', format: 'csv' | 'tsv' = 'csv') {\n const { content, filename } = composeTemplate(\n samples.value,\n mode === 'prefilled' ? Object.values(schemas.value) : null,\n mode === 'prefilled' ? classes.value : null,\n { mode, format, tokenizedSamples: tokenized.value },\n )\n const blob = new Blob([content], { type: format === 'tsv' ? 'text/tab-separated-values' : 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n }\n\n /** Test seam — exposes the template content/filename without triggering a browser download. */\n function composeTemplateForTest(opts: Pick<TemplateOptions, 'mode' | 'format'>) {\n return composeTemplate(\n samples.value,\n opts.mode === 'prefilled' ? Object.values(schemas.value) : null,\n opts.mode === 'prefilled' ? classes.value : null,\n { ...opts, tokenizedSamples: tokenized.value },\n )\n }\n\n // ----- Legacy public refs and shims -----\n\n const outliers = ref<OutlierInfo[]>([])\n const hasOutliers = computed(() => outliers.value.length > 0)\n const conformingSamples = computed(() => samples.value)\n const dominantFieldCount = computed(() => tokenized.value[0]?.length ?? 0)\n const minFieldCount = computed(() => {\n if (tokenized.value.length === 0) return 0\n return Math.min(...tokenized.value.map(t => t.length))\n })\n const fields = computed(() => activeSchema.value?.columns ?? [])\n const fieldNames = computed<Record<number, string>>(() => {\n const out: Record<number, string> = {}\n for (const c of fields.value) out[c.index] = c.displayName ?? c.name\n return out\n })\n const enabledFields = computed<Set<number>>(() =>\n activeSchema.value ? new Set(activeSchema.value.groupBy) : new Set(),\n )\n const effectiveColumns = fields\n const allSingletons = computed(\n () => groups.value.length > 1 && groups.value.every(g => g.samples.length === 1),\n )\n\n function setOutlierAction(_: number, __: OutlierAction) {\n if (!outlierDeprecationWarned) {\n if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') {\n console.warn(\n `[useAutoGroup] setOutlierAction is deprecated AND inactive in the class-first redesign. ` +\n `Use setClassDisposition('iqc' | 'eqc' | …, 'group' | 'overlay' | 'exclude') instead.`,\n )\n outlierDeprecationWarned = true\n }\n }\n }\n\n function setAllOutlierActions(__: OutlierAction) {\n if (!outlierDeprecationWarned) {\n if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') {\n console.warn(\n `[useAutoGroup] setAllOutlierActions is deprecated AND inactive in the class-first redesign. ` +\n `Use setClassDisposition('iqc' | 'eqc' | …, 'group' | 'overlay' | 'exclude') instead.`,\n )\n outlierDeprecationWarned = true\n }\n }\n }\n\n function toggleField(index: number) {\n warnDeprecated('toggleField')\n if (activeClassKey.value) toggleGroupBy(activeClassKey.value, index)\n }\n\n function renameField(index: number, name: string) {\n warnDeprecated('renameField')\n if (activeClassKey.value) setColumnName(activeClassKey.value, index, name)\n }\n\n function loadExperimentData(rawData: Record<string, unknown>): boolean {\n const parsed = extractSamplesFromDesignData(rawData)\n if (!parsed) return false\n inputMode.value = 'experiment'\n csvData.value = parsed\n sampleTypeHints.value = parsed.sampleTypeHints\n parseInput()\n return true\n }\n\n function reset() {\n rawText.value = ''\n csvData.value = null\n classes.value = []\n schemas.value = {}\n activeClassKey.value = ''\n tokenized.value = []\n outliers.value = []\n sampleTypeHints.value = []\n }\n\n return {\n // State\n inputMode, rawText, csvData, sampleTypeHints,\n classes, schemas, activeClassKey,\n delimiter, dominantFieldCount, minFieldCount,\n // Computed\n samples, tokenized, activeSchema, suggestions,\n groups, qcGroups, metadata, excludedSamples, result, fingerprint,\n fields, fieldNames, enabledFields, effectiveColumns,\n hasOutliers, outliers, conformingSamples, allSingletons,\n canDownloadTemplate,\n // Mutators\n setClassDisposition, setClassDispositions, toggleGroupBy, setRole, setColumnName, setValueOps, setBinning,\n mergeColumns, loadFingerprint,\n // Actions\n parseInput, downloadTemplate, composeTemplateForTest, loadExperimentData, reset,\n // Deprecated shims\n setOutlierAction, setAllOutlierActions, toggleField, renameField,\n }\n}\n\nexport function extractSamplesFromDesignData(\n rawData: Record<string, unknown>,\n): (ParsedCsvData & { sampleTypeHints: (string | undefined)[] }) | null {\n const designData = unwrapExperimentDesignData(rawData)\n if (!designData) return null\n const samples = designData.samples\n if (!Array.isArray(samples) || samples.length === 0) return null\n const allConditionKeys: string[] = []\n const keySet = new Set<string>()\n const sampleTypeHints: (string | undefined)[] = []\n for (const sample of samples) {\n const rawType = (sample as Record<string, unknown>).sample_type\n sampleTypeHints.push(typeof rawType === 'string' ? rawType.toLowerCase() : undefined)\n const conditions = (sample as Record<string, unknown>).conditions as Record<string, string> | undefined\n if (conditions && typeof conditions === 'object') {\n for (const key of Object.keys(conditions)) {\n if (!keySet.has(key)) {\n keySet.add(key)\n allConditionKeys.push(key)\n }\n }\n }\n }\n if (allConditionKeys.length === 0) return null\n const columns = ['sample_name', ...allConditionKeys, 'sample_type']\n const rows = (samples as Record<string, unknown>[]).map(s => {\n const conditions = (s.conditions as Record<string, string>) ?? {}\n const row: Record<string, string> = {\n sample_name: String(s.sample_name ?? ''),\n sample_type: String(s.sample_type ?? 'sample'),\n }\n for (const key of allConditionKeys) row[key] = conditions[key] ?? ''\n return row\n })\n return { columns, rows, sampleColumn: 'sample_name', delimiter: ',', sampleTypeHints }\n}\n","/**\n * Color utilities for deriving same-hue-family shades.\n *\n * Used by SampleSelector and other group/legend UIs where a parent color\n * needs to seed a gradient of related shades for its children.\n */\n\nexport interface Hsl {\n h: number // 0–360\n s: number // 0–100\n l: number // 0–100\n}\n\nconst HEX_RE = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n))\n}\n\nexport function hexToHsl(hex: string): Hsl {\n const match = HEX_RE.exec(hex.trim())\n if (!match) return { h: 0, s: 0, l: 50 }\n\n let raw = match[1]\n if (raw.length === 3) raw = raw.split('').map(c => c + c).join('')\n\n const r = parseInt(raw.slice(0, 2), 16) / 255\n const g = parseInt(raw.slice(2, 4), 16) / 255\n const b = parseInt(raw.slice(4, 6), 16) / 255\n\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const delta = max - min\n\n let h = 0\n if (delta !== 0) {\n if (max === r) h = ((g - b) / delta) % 6\n else if (max === g) h = (b - r) / delta + 2\n else h = (r - g) / delta + 4\n }\n h = (h * 60 + 360) % 360\n\n const l = (max + min) / 2\n const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1))\n\n return { h, s: s * 100, l: l * 100 }\n}\n\nexport function hslToHex(h: number, s: number, l: number): string {\n const sNorm = clamp(s, 0, 100) / 100\n const lNorm = clamp(l, 0, 100) / 100\n const hNorm = ((h % 360) + 360) % 360\n\n const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm\n const x = c * (1 - Math.abs(((hNorm / 60) % 2) - 1))\n const m = lNorm - c / 2\n\n let r = 0, g = 0, b = 0\n if (hNorm < 60) [r, g, b] = [c, x, 0]\n else if (hNorm < 120) [r, g, b] = [x, c, 0]\n else if (hNorm < 180) [r, g, b] = [0, c, x]\n else if (hNorm < 240) [r, g, b] = [0, x, c]\n else if (hNorm < 300) [r, g, b] = [x, 0, c]\n else [r, g, b] = [c, 0, x]\n\n const toHex = (n: number) => Math.round((n + m) * 255).toString(16).padStart(2, '0')\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`\n}\n\n/**\n * Derive a same-hue-family shade of `parentHex` for child at `index` of `total`.\n *\n * Lightness is distributed evenly within [35, 75] so all children remain\n * readable on light/dark backgrounds. Hue and saturation are inherited from\n * the parent. With `total <= 1`, returns the parent unchanged.\n *\n * Children are ordered light → dark by index, so index 0 is the brightest\n * sibling and index `total - 1` is the darkest.\n */\nexport function deriveShade(parentHex: string, index: number, total: number): string {\n if (total <= 1) return parentHex\n const { h, s, l } = hexToHsl(parentHex)\n\n const L_MIN = 35\n const L_MAX = 75\n const spread = Math.min(L_MAX - L_MIN, 40)\n const center = clamp(l, L_MIN + spread / 2, L_MAX - spread / 2)\n\n const top = center + spread / 2\n const bot = center - spread / 2\n const t = total === 1 ? 0.5 : index / (total - 1)\n const childL = top - t * (top - bot)\n\n const childS = clamp(s, 35, 100)\n return hslToHex(h, childS, childL)\n}\n","import { computed, toValue, type ComputedRef, type Ref } from 'vue'\nimport type { SampleGroup } from '../types'\nimport { deriveShade } from '../utils/color'\n\nexport type SampleGroupSource<T> =\n | T\n | Ref<T>\n | ComputedRef<T>\n | (() => T)\n\nexport interface DisplaySampleSubGroup extends SampleGroup {\n displayColor: string\n displayBg: string\n displayBorder: string\n}\n\nexport interface SampleMajorGroup {\n name: string\n color: string\n subGroups: DisplaySampleSubGroup[]\n allSamples: string[]\n}\n\nexport interface UseSampleGroupsOptions {\n groups: SampleGroupSource<readonly SampleGroup[]>\n samples?: SampleGroupSource<readonly string[]>\n separator?: SampleGroupSource<string | null | undefined>\n fallbackColor?: SampleGroupSource<string | null | undefined>\n}\n\nexport interface UseSampleGroupsReturn {\n groups: ComputedRef<SampleGroup[]>\n separator: ComputedRef<string>\n hierarchicalGroups: ComputedRef<SampleMajorGroup[]>\n showHierarchy: ComputedRef<boolean>\n groupedSamples: ComputedRef<Set<string>>\n ungroupedSamples: ComputedRef<string[]>\n findGroup: (groupName: string) => SampleGroup | undefined\n getGroupColor: (groupName: string) => string\n}\n\n/** Shared sample-group hierarchy, color, and ungrouped-sample derivation for lab selectors. */\nexport function useSampleGroups(options: UseSampleGroupsOptions): UseSampleGroupsReturn {\n const groups = computed(() => [...toValue(options.groups)])\n const samples = computed(() => [...(toValue(options.samples) ?? [])])\n const fallbackColor = computed(() => toValue(options.fallbackColor) || '#3B82F6')\n const separator = computed(() => {\n const explicit = toValue(options.separator)\n if (explicit) return explicit\n return groups.value.some(group => group.name.includes('/')) ? '/' : '_'\n })\n\n const hierarchicalGroups = computed<SampleMajorGroup[]>(() => {\n if (groups.value.length === 0) return []\n const groupedByMajor = new Map<string, SampleGroup[]>()\n\n for (const group of groups.value) {\n const majorName = getMajorGroupName(group.name, separator.value)\n const existing = groupedByMajor.get(majorName)\n if (existing) {\n existing.push(group)\n } else {\n groupedByMajor.set(majorName, [group])\n }\n }\n\n return [...groupedByMajor.entries()].map(([majorName, subGroups]) => {\n const color = subGroups[0]?.color || fallbackColor.value\n const displaySubs = subGroups.map((subGroup, index) => {\n const displayColor = deriveShade(color, index, subGroups.length)\n return {\n ...subGroup,\n displayColor,\n displayBg: displayColor + '20',\n displayBorder: displayColor + '40',\n }\n })\n\n return {\n name: majorName,\n color,\n subGroups: displaySubs,\n allSamples: subGroups.flatMap(group => group.samples),\n }\n })\n })\n\n const showHierarchy = computed(() =>\n hierarchicalGroups.value.some(major =>\n major.subGroups.length > 1 ||\n (major.subGroups.length === 1 && major.name !== major.subGroups[0].name)\n )\n )\n\n const groupedSamples = computed(() =>\n new Set(groups.value.flatMap(group => group.samples))\n )\n\n const ungroupedSamples = computed(() =>\n samples.value.filter(sample => !groupedSamples.value.has(sample))\n )\n\n function findGroup(groupName: string): SampleGroup | undefined {\n return groups.value.find(group => group.name === groupName)\n }\n\n function getGroupColor(groupName: string): string {\n return findGroup(groupName)?.color || fallbackColor.value\n }\n\n return {\n groups,\n separator,\n hierarchicalGroups,\n showHierarchy,\n groupedSamples,\n ungroupedSamples,\n findGroup,\n getGroupColor,\n }\n}\n\nfunction getMajorGroupName(groupName: string, separator: string): string {\n const parts = groupName.split(separator)\n return parts.length > 1 ? parts[0] : groupName\n}\n","import { computed, ref, toValue, watch, type ComputedRef, type Ref } from 'vue'\n\nexport type ExpansionSetSource<T> =\n | T\n | Ref<T>\n | ComputedRef<T>\n | (() => T)\n\nexport interface UseExpansionSetOptions {\n defaultIds?: ExpansionSetSource<readonly string[] | null | undefined>\n allIds?: ExpansionSetSource<readonly string[] | null | undefined>\n expandAll?: ExpansionSetSource<boolean | null | undefined>\n}\n\nexport interface UseExpansionSetReturn {\n expandedIds: Ref<Set<string>>\n expandedList: ComputedRef<string[]>\n isExpanded: (id: string) => boolean\n expand: (id: string) => void\n collapse: (id: string) => void\n toggle: (id: string) => boolean\n expandMany: (ids: readonly string[]) => void\n setExpanded: (ids: readonly string[]) => void\n collapseAll: () => void\n reset: () => void\n}\n\n/** Shared expansion state for trees, grouped selectors, and disclosure lists. */\nexport function useExpansionSet(options: UseExpansionSetOptions = {}): UseExpansionSetReturn {\n const expandedIds = ref(new Set(normalizeIds(toValue(options.defaultIds))))\n const expandedList = computed(() => [...expandedIds.value])\n\n if (options.expandAll !== undefined) {\n watch(\n () => toValue(options.expandAll),\n (shouldExpandAll) => {\n if (shouldExpandAll === undefined || shouldExpandAll === null) return\n if (shouldExpandAll) {\n setExpanded(normalizeIds(toValue(options.allIds)))\n } else {\n reset()\n }\n },\n { immediate: true },\n )\n }\n\n if (options.allIds !== undefined) {\n watch(\n () => normalizeIds(toValue(options.allIds)),\n (ids) => {\n if (toValue(options.expandAll)) {\n setExpanded(ids)\n }\n },\n )\n }\n\n function isExpanded(id: string): boolean {\n return expandedIds.value.has(id)\n }\n\n function expand(id: string) {\n if (expandedIds.value.has(id)) return\n expandedIds.value = new Set([...expandedIds.value, id])\n }\n\n function collapse(id: string) {\n if (!expandedIds.value.has(id)) return\n const next = new Set(expandedIds.value)\n next.delete(id)\n expandedIds.value = next\n }\n\n function toggle(id: string): boolean {\n if (expandedIds.value.has(id)) {\n collapse(id)\n return false\n }\n expand(id)\n return true\n }\n\n function expandMany(ids: readonly string[]) {\n expandedIds.value = new Set([...expandedIds.value, ...normalizeIds(ids)])\n }\n\n function setExpanded(ids: readonly string[]) {\n expandedIds.value = new Set(normalizeIds(ids))\n }\n\n function collapseAll() {\n expandedIds.value = new Set()\n }\n\n function reset() {\n setExpanded(normalizeIds(toValue(options.defaultIds)))\n }\n\n return {\n expandedIds,\n expandedList,\n isExpanded,\n expand,\n collapse,\n toggle,\n expandMany,\n setExpanded,\n collapseAll,\n reset,\n }\n}\n\nfunction normalizeIds(ids: readonly string[] | null | undefined): string[] {\n if (!ids) return []\n return [...new Set(ids.filter(Boolean))]\n}\n","import type { PlatformContext } from '../types'\n\nexport type ExperimentAwarePlatformContext = PlatformContext & {\n currentExperiment?: unknown\n currentExperimentId?: unknown\n experiment?: unknown\n experimentId?: unknown\n}\n\nexport function getInjectedPlatformContext(): PlatformContext | undefined {\n if (typeof window === 'undefined') return undefined\n return (window as unknown as { __MINT_PLATFORM__?: PlatformContext }).__MINT_PLATFORM__\n}\n\nexport function getInjectedExperimentContext(): ExperimentAwarePlatformContext | undefined {\n return getInjectedPlatformContext() as ExperimentAwarePlatformContext | undefined\n}\n\nexport function currentExperimentFromContext<TExperiment>(\n context: ExperimentAwarePlatformContext | undefined = getInjectedExperimentContext(),\n): TExperiment | undefined {\n const candidate = context?.currentExperiment ?? context?.experiment\n return candidate && typeof candidate === 'object'\n ? candidate as TExperiment\n : undefined\n}\n\nexport function currentExperimentIdFromContext(\n context: ExperimentAwarePlatformContext | undefined = getInjectedExperimentContext(),\n): number | undefined {\n return parseExperimentId(\n context?.currentExperimentId ??\n context?.experimentId ??\n context?.currentExperiment ??\n context?.experiment,\n )\n}\n\nexport function currentExperimentIdFromUrl(): number | undefined {\n if (typeof window === 'undefined') return undefined\n const params = new URLSearchParams(window.location.search)\n return parseExperimentId(params.get('experimentId') ?? params.get('experiment_id'))\n ?? experimentIdFromPath(window.location.pathname)\n ?? experimentIdFromPath(window.location.hash.replace(/^#\\/?/, '/'))\n}\n\nexport function resolveCurrentExperimentId(\n context: ExperimentAwarePlatformContext | undefined = getInjectedExperimentContext(),\n): number | undefined {\n return currentExperimentIdFromContext(context) ?? currentExperimentIdFromUrl()\n}\n\nexport function parseExperimentId(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n }\n if (value && typeof value === 'object' && 'id' in value) {\n return parseExperimentId((value as { id?: unknown }).id)\n }\n return undefined\n}\n\nfunction experimentIdFromPath(path: string): number | undefined {\n const segments = path.split('/').filter(Boolean)\n for (let index = 0; index < segments.length - 1; index += 1) {\n const segment = segments[index]?.toLowerCase()\n if (segment === 'experiment' || segment === 'experiments') {\n return parseExperimentId(decodeURIComponent(segments[index + 1] ?? ''))\n }\n }\n return undefined\n}\n","import { shallowRef, computed, type Ref, type ComputedRef } from 'vue'\nimport { useApi, type UseApiReturn } from './useApi'\nimport { useRequestSyncState } from './useRequestSyncState'\nimport {\n extractSampleNamesFromDesignData,\n extractSampleOptionsFromDesignData,\n unwrapExperimentDesignData,\n} from './experimentDesignData'\nimport type { TreeNode, SummaryData, SelectOption } from '../types'\n\nexport interface UseExperimentDataOptions {\n apiBaseUrl?: string\n immediate?: boolean\n}\n\nexport interface UseExperimentDataReturn {\n /** Raw experiment data response from `/experiments/{id}/data`, or null. */\n data: Ref<Record<string, unknown> | null>\n /** Plugin-defined design_data payload unwrapped from platform response shapes, or null. */\n designData: ComputedRef<Record<string, unknown> | null>\n /** Sample values extracted from design_data for SampleSelector-style components. */\n sampleNames: ComputedRef<string[]>\n /** Sample options extracted from design_data for select/dropdown components. */\n sampleOptions: ComputedRef<SelectOption<string>[]>\n /** Hierarchy tree rows normalised from `tree_data` or `treeData`. */\n treeData: ComputedRef<TreeNode[]>\n /** Table rows normalised from `table_data` or `tableData`. */\n tableData: ComputedRef<Record<string, unknown>[]>\n /** Summary payload normalised from `summary_data` or `summaryData`, or null. */\n summaryData: ComputedRef<SummaryData | null>\n /** Whether experiment data is currently loading. */\n isLoading: Ref<boolean>\n /** Error message from the last failed fetch, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful experiment data fetch, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Fetch experiment data for an experiment id. */\n fetch: (experimentId: number) => Promise<Record<string, unknown> | null>\n /** Refetch the most recently fetched experiment id. */\n refresh: () => Promise<void>\n /** Clear local experiment data and current fetch target. */\n clear: () => void\n}\n\n/** Fetches and normalises experiment output data (tree, table, summary) from the platform API. */\nexport function useExperimentData(\n options: UseExperimentDataOptions = {},\n): UseExperimentDataReturn {\n const data = shallowRef<Record<string, unknown> | null>(null)\n const request = useRequestSyncState('Failed to fetch experiment data')\n const isLoading = request.loading\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n let lastExperimentId: number | null = null\n let api: UseApiReturn | null = null\n\n function getApi(): UseApiReturn {\n api ??= useApi({ baseUrl: options.apiBaseUrl })\n return api\n }\n\n const designData = computed<Record<string, unknown> | null>(() =>\n unwrapExperimentDesignData(data.value),\n )\n\n const sampleNames = computed<string[]>(() =>\n extractSampleNamesFromDesignData(designData.value),\n )\n\n const sampleOptions = computed<SelectOption<string>[]>(() =>\n extractSampleOptionsFromDesignData(designData.value),\n )\n\n const treeData = computed<TreeNode[]>(() => {\n if (!designData.value) return []\n const tree = designData.value.tree_data ?? designData.value.treeData\n return Array.isArray(tree) ? tree as TreeNode[] : []\n })\n\n const tableData = computed<Record<string, unknown>[]>(() => {\n if (!designData.value) return []\n const table = designData.value.table_data ?? designData.value.tableData\n return Array.isArray(table) ? table as Record<string, unknown>[] : []\n })\n\n const summaryData = computed<SummaryData | null>(() => {\n if (!designData.value) return null\n const summary = designData.value.summary_data ?? designData.value.summaryData\n if (summary && typeof summary === 'object' && 'metadata' in (summary as Record<string, unknown>)) {\n return summary as SummaryData\n }\n return null\n })\n\n async function fetchData(experimentId: number): Promise<Record<string, unknown> | null> {\n lastExperimentId = experimentId\n try {\n data.value = await request.run(\n () => getApi().get<Record<string, unknown>>(`/experiments/${experimentId}/data`),\n { success: 'load', errorMessage: 'Failed to fetch experiment data' },\n )\n return data.value\n } catch {\n data.value = null\n return null\n }\n }\n\n async function refresh(): Promise<void> {\n if (lastExperimentId !== null) {\n await fetchData(lastExperimentId)\n }\n }\n\n function clear(): void {\n data.value = null\n lastExperimentId = null\n request.clearError()\n }\n\n return {\n data,\n designData,\n sampleNames,\n sampleOptions,\n treeData,\n tableData,\n summaryData,\n isLoading,\n error,\n lastLoadedAt,\n fetch: fetchData,\n refresh,\n clear,\n }\n}\n","import {\n computed,\n inject,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\nimport type { SelectOption } from '../types'\nimport { APP_EXPERIMENT_KEY } from './useAppExperiment'\nimport {\n extractSampleNamesFromDesignData,\n extractSampleOptionsFromDesignData,\n unwrapExperimentDesignData,\n type ExtractExperimentSamplesOptions,\n} from './experimentDesignData'\nimport { parseExperimentId } from './platformContextHelpers'\nimport { useExperimentData } from './useExperimentData'\n\nexport type ExperimentIdSource = MaybeRefOrGetter<number | string | null | undefined>\nexport type ExperimentDesignDataSource = MaybeRefOrGetter<Record<string, unknown> | null | undefined>\n\nexport interface UseExperimentSamplesOptions extends ExtractExperimentSamplesOptions {\n /** Experiment id to load from `/experiments/{id}/data`; defaults to provided AppExperiment context. */\n experimentId?: ExperimentIdSource\n /** Pre-fetched design_data or full platform design-data response. */\n designData?: ExperimentDesignDataSource\n /** API base URL override for platform calls. */\n apiBaseUrl?: string\n /** Auto-load when an experiment id becomes available. Defaults to true. */\n immediate?: boolean\n /** Enable or disable automatic loading while keeping derived samples reactive. Defaults to true. */\n enabled?: MaybeRefOrGetter<boolean>\n /** Use `useAppExperiment` provided state when `experimentId` is not set. Defaults to true. */\n syncAppExperiment?: boolean\n}\n\nexport interface UseExperimentSamplesReturn {\n /** Experiment id currently used for automatic sample loading. */\n experimentId: ComputedRef<number | undefined>\n /** Raw `/experiments/{id}/data` response when loaded through the platform API. */\n data: Ref<Record<string, unknown> | null>\n /** Plugin-defined design_data payload from props or platform response. */\n designData: ComputedRef<Record<string, unknown> | null>\n /** SampleSelector-compatible string values extracted from design_data. */\n samples: ComputedRef<string[]>\n /** Select/dropdown-friendly sample options extracted from design_data. */\n sampleOptions: ComputedRef<SelectOption<string>[]>\n isLoading: Ref<boolean>\n error: Ref<string | null>\n lastLoadedAt: Ref<Date | null>\n /** Fetch samples for a specific experiment id, defaulting to the resolved id. */\n fetch: (experimentId?: number | string | null) => Promise<Record<string, unknown> | null>\n /** Refetch the current experiment id. */\n refresh: () => Promise<void>\n /** Clear fetched platform data. */\n clear: () => void\n}\n\n/** Loads experiment design_data and derives samples for SampleSelector-style UIs. */\nexport function useExperimentSamples(\n options: UseExperimentSamplesOptions = {},\n): UseExperimentSamplesReturn {\n const appExperiment = options.syncAppExperiment === false\n ? undefined\n : inject(APP_EXPERIMENT_KEY, undefined)\n const experimentData = useExperimentData({ apiBaseUrl: options.apiBaseUrl })\n\n const experimentId = computed<number | undefined>(() => {\n const explicitId = parseExperimentId(toValue(options.experimentId))\n return explicitId ?? parseExperimentId(appExperiment?.experimentId.value)\n })\n\n const explicitDesignData = computed<Record<string, unknown> | null>(() =>\n unwrapExperimentDesignData(toValue(options.designData) ?? null),\n )\n\n const designData = computed<Record<string, unknown> | null>(() =>\n explicitDesignData.value ?? experimentData.designData.value,\n )\n\n const extractOptions = computed<ExtractExperimentSamplesOptions>(() => ({\n includeControls: options.includeControls,\n }))\n\n const samples = computed<string[]>(() =>\n extractSampleNamesFromDesignData(designData.value, extractOptions.value),\n )\n\n const sampleOptions = computed<SelectOption<string>[]>(() =>\n extractSampleOptionsFromDesignData(designData.value, extractOptions.value),\n )\n\n const enabled = computed(() => toValue(options.enabled) ?? true)\n\n async function fetchSamples(\n id: number | string | null | undefined = experimentId.value,\n ): Promise<Record<string, unknown> | null> {\n const parsedId = parseExperimentId(id)\n if (parsedId === undefined) {\n experimentData.clear()\n return null\n }\n return experimentData.fetch(parsedId)\n }\n\n watch(\n () => ({\n designData: explicitDesignData.value,\n enabled: enabled.value,\n experimentId: experimentId.value,\n }),\n ({ designData: currentDesignData, enabled: isEnabled, experimentId: currentExperimentId }) => {\n if (options.immediate === false || !isEnabled) return\n if (currentDesignData) {\n experimentData.clear()\n return\n }\n if (currentExperimentId === undefined) {\n experimentData.clear()\n return\n }\n void experimentData.fetch(currentExperimentId)\n },\n { immediate: options.immediate !== false },\n )\n\n return {\n experimentId,\n data: experimentData.data,\n designData,\n samples,\n sampleOptions,\n isLoading: experimentData.isLoading,\n error: experimentData.error,\n lastLoadedAt: experimentData.lastLoadedAt,\n fetch: fetchSamples,\n refresh: experimentData.refresh,\n clear: experimentData.clear,\n }\n}\n","import { ref, computed, onUnmounted, type Ref } from 'vue'\nimport type { ScheduleEvent } from '../types/components'\n\nexport type DragType = 'create' | 'resize-top' | 'resize-bottom' | 'move'\n\nexport interface DragState {\n type: DragType\n event?: ScheduleEvent\n startDate: Date\n startY: number\n currentY: number\n dayIndex: number\n currentDayIndex: number\n}\n\nexport interface GhostEvent {\n start: Date\n end: Date\n dayIndex: number\n style: Record<string, string>\n}\n\nexport interface UseScheduleDragOptions {\n slotDuration: Ref<number>\n dayStartHour: Ref<number>\n dayEndHour: Ref<number>\n readonly: Ref<boolean>\n slotHeight: Ref<number>\n blockedSlots?: Ref<{ start: string; end: string; label?: string }[]>\n onCreateComplete?: (start: Date, end: Date) => void\n onMoveComplete?: (event: ScheduleEvent, newStart: Date, newEnd: Date) => void\n onResizeComplete?: (event: ScheduleEvent, newStart: Date, newEnd: Date) => void\n}\n\n/** Handles pointer-driven create, move, and resize drag interactions for the ScheduleCalendar grid. */\nexport function useScheduleDrag(options: UseScheduleDragOptions) {\n const isDragging = ref(false)\n const dragState = ref<DragState | null>(null)\n\n const ghost = computed<GhostEvent | null>(() => {\n if (!dragState.value) return null\n const state = dragState.value\n const slotH = options.slotHeight.value\n const slotMin = options.slotDuration.value\n const dayStart = options.dayStartHour.value * 60\n\n if (state.type === 'create') {\n const deltaY = state.currentY - state.startY\n const startOffset = state.startY\n const endOffset = startOffset + deltaY\n\n const topPx = Math.min(startOffset, endOffset)\n const bottomPx = Math.max(startOffset, endOffset)\n\n const startMinutes = dayStart + Math.round((topPx / slotH) * slotMin)\n const endMinutes = dayStart + Math.round((bottomPx / slotH) * slotMin)\n\n const snappedStart = snapMinutes(startMinutes, slotMin)\n const snappedEnd = Math.max(snapMinutes(endMinutes, slotMin), snappedStart + slotMin)\n\n const snappedTopPx = ((snappedStart - dayStart) / slotMin) * slotH\n const snappedHeightPx = ((snappedEnd - snappedStart) / slotMin) * slotH\n\n return {\n start: minutesToDate(state.startDate, snappedStart),\n end: minutesToDate(state.startDate, snappedEnd),\n dayIndex: state.dayIndex,\n style: {\n top: `${snappedTopPx}px`,\n height: `${snappedHeightPx}px`,\n },\n }\n }\n\n if (state.type === 'move' && state.event) {\n const deltaY = state.currentY - state.startY\n const eventStart = dateToMinutes(new Date(state.event.start))\n const eventEnd = dateToMinutes(new Date(state.event.end))\n const duration = eventEnd - eventStart\n const pixelDelta = Math.round((deltaY / slotH) * slotMin)\n const newStart = snapMinutes(eventStart + pixelDelta, slotMin)\n const newEnd = newStart + duration\n\n const clamped = clampRange(newStart, newEnd, dayStart, options.dayEndHour.value * 60)\n const topPx = ((clamped.start - dayStart) / slotMin) * slotH\n const heightPx = ((clamped.end - clamped.start) / slotMin) * slotH\n\n return {\n start: minutesToDate(state.startDate, clamped.start),\n end: minutesToDate(state.startDate, clamped.end),\n dayIndex: state.currentDayIndex,\n style: {\n top: `${topPx}px`,\n height: `${heightPx}px`,\n },\n }\n }\n\n if ((state.type === 'resize-top' || state.type === 'resize-bottom') && state.event) {\n const eventStart = dateToMinutes(new Date(state.event.start))\n const eventEnd = dateToMinutes(new Date(state.event.end))\n const deltaY = state.currentY - state.startY\n const pixelDelta = Math.round((deltaY / slotH) * slotMin)\n\n let newStart = eventStart\n let newEnd = eventEnd\n\n if (state.type === 'resize-top') {\n newStart = snapMinutes(eventStart + pixelDelta, slotMin)\n newStart = Math.min(newStart, newEnd - slotMin)\n } else {\n newEnd = snapMinutes(eventEnd + pixelDelta, slotMin)\n newEnd = Math.max(newEnd, newStart + slotMin)\n }\n\n const clamped = clampRange(newStart, newEnd, dayStart, options.dayEndHour.value * 60)\n const topPx = ((clamped.start - dayStart) / slotMin) * slotH\n const heightPx = ((clamped.end - clamped.start) / slotMin) * slotH\n\n return {\n start: minutesToDate(state.startDate, clamped.start),\n end: minutesToDate(state.startDate, clamped.end),\n dayIndex: state.dayIndex,\n style: {\n top: `${topPx}px`,\n height: `${heightPx}px`,\n },\n }\n }\n\n return null\n })\n\n function startCreate(date: Date, y: number, dayIndex: number) {\n if (options.readonly.value) return\n isDragging.value = true\n dragState.value = {\n type: 'create',\n startDate: date,\n startY: y,\n currentY: y,\n dayIndex,\n currentDayIndex: dayIndex,\n }\n addListeners()\n }\n\n function startMove(event: ScheduleEvent, y: number, dayIndex: number) {\n if (options.readonly.value || event.draggable === false) return\n isDragging.value = true\n dragState.value = {\n type: 'move',\n event,\n startDate: new Date(event.start),\n startY: y,\n currentY: y,\n dayIndex,\n currentDayIndex: dayIndex,\n }\n addListeners()\n }\n\n function startResize(event: ScheduleEvent, edge: 'top' | 'bottom', y: number, dayIndex: number) {\n if (options.readonly.value || event.resizable === false) return\n isDragging.value = true\n dragState.value = {\n type: edge === 'top' ? 'resize-top' : 'resize-bottom',\n event,\n startDate: new Date(event.start),\n startY: y,\n currentY: y,\n dayIndex,\n currentDayIndex: dayIndex,\n }\n addListeners()\n }\n\n function onPointerMove(e: PointerEvent) {\n if (!dragState.value) return\n dragState.value = { ...dragState.value, currentY: e.clientY }\n }\n\n function onPointerUp() {\n if (!dragState.value || !ghost.value) {\n cleanup()\n return\n }\n\n const g = ghost.value\n const state = dragState.value\n\n if (state.type === 'create' && options.onCreateComplete) {\n options.onCreateComplete(g.start, g.end)\n } else if (state.type === 'move' && state.event && options.onMoveComplete) {\n options.onMoveComplete(state.event, g.start, g.end)\n } else if ((state.type === 'resize-top' || state.type === 'resize-bottom') && state.event && options.onResizeComplete) {\n options.onResizeComplete(state.event, g.start, g.end)\n }\n\n cleanup()\n }\n\n function addListeners() {\n document.addEventListener('pointermove', onPointerMove)\n document.addEventListener('pointerup', onPointerUp)\n }\n\n function cleanup() {\n isDragging.value = false\n dragState.value = null\n document.removeEventListener('pointermove', onPointerMove)\n document.removeEventListener('pointerup', onPointerUp)\n }\n\n onUnmounted(cleanup)\n\n return {\n isDragging,\n dragState,\n ghost,\n startCreate,\n startMove,\n startResize,\n }\n}\n\nfunction snapMinutes(minutes: number, step: number): number {\n return Math.round(minutes / step) * step\n}\n\nfunction dateToMinutes(date: Date): number {\n return date.getHours() * 60 + date.getMinutes()\n}\n\nfunction minutesToDate(baseDate: Date, minutes: number): Date {\n const d = new Date(baseDate)\n d.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0)\n return d\n}\n\nfunction clampRange(start: number, end: number, min: number, max: number) {\n const s = Math.max(start, min)\n const e = Math.min(end, max)\n return { start: s, end: Math.max(e, s) }\n}\n","import { computed, ref, type ComputedRef, type Ref } from 'vue'\nimport { useApi } from './useApi'\nimport { useRequestSyncState } from './useRequestSyncState'\nimport { createBioTemplatePresetCollection } from '../templates'\nimport type {\n CreateLcmsBatchCollectionOptions,\n CreateQpcrExpressionCollectionOptions,\n CreateWellPlateScreenCollectionOptions,\n TemplatePresetId,\n} from '../templates'\nimport {\n getInjectedPlatformContext,\n resolveCurrentExperimentId,\n} from './platformContextHelpers'\n\ntype TemplatePresetCollectionOptions =\n | CreateWellPlateScreenCollectionOptions\n | CreateQpcrExpressionCollectionOptions\n | CreateLcmsBatchCollectionOptions\n\nexport interface UseExperimentSaveOptions {\n /** Default plugin_id for all save/load calls. */\n pluginId?: string\n /** Default schema version for design saves (defaults to \"1.0\"). */\n schemaVersion?: string\n /** Override API base URL. */\n apiBaseUrl?: string\n}\n\nexport interface UseExperimentSaveReturn {\n /** Current experiment id from platform injection, URL query, or URL path. */\n currentExperimentId: ComputedRef<number | undefined>\n /** Whether a current experiment id is available. */\n hasCurrentExperiment: ComputedRef<boolean>\n /** Whether a save operation is in progress. */\n isSaving: Ref<boolean>\n /** Whether a load or delete operation is in progress. */\n isLoading: Ref<boolean>\n /** Error message from the last failed operation, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful load, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Timestamp of the last successful save, or null. */\n lastSavedAt: Ref<Date | null>\n /** Save design data for an experiment. */\n saveDesign: (experimentId: number, data: Record<string, unknown>) => Promise<boolean>\n /** Save design data for the current experiment. */\n saveCurrentDesign: (data: Record<string, unknown>) => Promise<boolean>\n /** Create and save a built-in biology template preset as experiment design data. */\n saveTemplatePreset: (\n experimentId: number,\n name: TemplatePresetId | string,\n options?: TemplatePresetCollectionOptions,\n ) => Promise<boolean>\n /** Create and save a built-in biology template preset for the current experiment. */\n saveCurrentTemplatePreset: (\n name: TemplatePresetId | string,\n options?: TemplatePresetCollectionOptions,\n ) => Promise<boolean>\n /** Save analysis result for an experiment. */\n saveAnalysis: (experimentId: number, result: Record<string, unknown>) => Promise<boolean>\n /** Save analysis result for the current experiment. */\n saveCurrentAnalysis: (result: Record<string, unknown>) => Promise<boolean>\n /** Save design and/or analysis in one call. */\n save: (experimentId: number, opts: {\n design?: Record<string, unknown>\n analysis?: Record<string, unknown>\n }) => Promise<boolean>\n /** Load design data for an experiment. */\n loadDesign: (experimentId: number) => Promise<Record<string, unknown> | null>\n /** Load design data for the current experiment. */\n loadCurrentDesign: () => Promise<Record<string, unknown> | null>\n /** Load analysis result for an experiment. */\n loadAnalysis: (experimentId: number) => Promise<Record<string, unknown> | null>\n /** Load analysis result for the current experiment. */\n loadCurrentAnalysis: () => Promise<Record<string, unknown> | null>\n /** Delete design data for an experiment. */\n deleteDesign: (experimentId: number) => Promise<boolean>\n /** Delete design data for the current experiment. */\n deleteCurrentDesign: () => Promise<boolean>\n /** Delete analysis result for an experiment. */\n deleteAnalysis: (experimentId: number) => Promise<boolean>\n /** Delete analysis result for the current experiment. */\n deleteCurrentAnalysis: () => Promise<boolean>\n /** Require and return the current experiment id, or throw when missing. */\n requireCurrentExperimentId: () => number\n}\n\n/** Persists experiment design, analysis, and built-in template preset data through the platform API. */\nexport function useExperimentSave(\n options: UseExperimentSaveOptions = {},\n): UseExperimentSaveReturn {\n const injectedContext = getInjectedPlatformContext()\n const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })\n const pluginId = options.pluginId ?? injectedContext?.plugin?.id\n const schemaVersion = options.schemaVersion ?? '1.0'\n const currentExperimentId = computed<number | undefined>(() => {\n return resolveCurrentExperimentId(injectedContext)\n })\n const hasCurrentExperiment = computed(() => currentExperimentId.value !== undefined)\n\n const request = useRequestSyncState('Experiment request failed.')\n const isSaving = ref(false)\n const isLoading = ref(false)\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n const lastSavedAt = request.lastSavedAt\n\n function requireCurrentExperimentId(): number {\n const id = currentExperimentId.value\n if (id === undefined) {\n throw new Error('[MINT SDK] No current experiment is selected.')\n }\n return id\n }\n\n function currentExperimentIdOrError(action: string): number | undefined {\n const id = currentExperimentId.value\n if (id === undefined) {\n request.setError(`No current experiment is selected for ${action}`)\n return undefined\n }\n return id\n }\n\n async function saveDesign(\n experimentId: number,\n data: Record<string, unknown>,\n ): Promise<boolean> {\n if (!pluginId) {\n request.setError('pluginId is required for saveDesign')\n return false\n }\n isSaving.value = true\n try {\n await request.run(async () => {\n await api.put(`/experiments/${experimentId}/data`, {\n plugin_id: pluginId,\n data,\n schema_version: schemaVersion,\n })\n }, { success: 'save' })\n return true\n } catch {\n return false\n } finally {\n isSaving.value = false\n }\n }\n\n async function saveCurrentDesign(data: Record<string, unknown>): Promise<boolean> {\n const experimentId = currentExperimentIdOrError('saveDesign')\n return experimentId === undefined ? false : saveDesign(experimentId, data)\n }\n\n async function saveTemplatePreset(\n experimentId: number,\n name: TemplatePresetId | string,\n presetOptions: TemplatePresetCollectionOptions = {},\n ): Promise<boolean> {\n const collection = createBioTemplatePresetCollection(name, presetOptions)\n return saveDesign(experimentId, collection as unknown as Record<string, unknown>)\n }\n\n async function saveCurrentTemplatePreset(\n name: TemplatePresetId | string,\n presetOptions: TemplatePresetCollectionOptions = {},\n ): Promise<boolean> {\n const experimentId = currentExperimentIdOrError('saveTemplatePreset')\n return experimentId === undefined ? false : saveTemplatePreset(experimentId, name, presetOptions)\n }\n\n async function saveAnalysis(\n experimentId: number,\n result: Record<string, unknown>,\n ): Promise<boolean> {\n if (!pluginId) {\n request.setError('pluginId is required for saveAnalysis')\n return false\n }\n isSaving.value = true\n try {\n await request.run(async () => {\n await api.put(`/experiments/${experimentId}/results/${pluginId}`, {\n result,\n })\n }, { success: 'save' })\n return true\n } catch {\n return false\n } finally {\n isSaving.value = false\n }\n }\n\n async function saveCurrentAnalysis(result: Record<string, unknown>): Promise<boolean> {\n const experimentId = currentExperimentIdOrError('saveAnalysis')\n return experimentId === undefined ? false : saveAnalysis(experimentId, result)\n }\n\n async function save(\n experimentId: number,\n opts: { design?: Record<string, unknown>; analysis?: Record<string, unknown> },\n ): Promise<boolean> {\n if ((opts.design || opts.analysis) && !pluginId) {\n request.setError('pluginId is required for save')\n return false\n }\n isSaving.value = true\n try {\n await request.run(async () => {\n if (opts.design) {\n await api.put(`/experiments/${experimentId}/data`, {\n plugin_id: pluginId,\n data: opts.design,\n schema_version: schemaVersion,\n })\n }\n if (opts.analysis) {\n await api.put(`/experiments/${experimentId}/results/${pluginId}`, {\n result: opts.analysis,\n })\n }\n }, { success: 'save' })\n return true\n } catch {\n return false\n } finally {\n isSaving.value = false\n }\n }\n\n async function loadDesign(\n experimentId: number,\n ): Promise<Record<string, unknown> | null> {\n isLoading.value = true\n try {\n return await request.run(\n () => api.get<Record<string, unknown>>(`/experiments/${experimentId}/data`),\n { success: 'load' },\n )\n } catch {\n return null\n } finally {\n isLoading.value = false\n }\n }\n\n async function loadCurrentDesign(): Promise<Record<string, unknown> | null> {\n const experimentId = currentExperimentIdOrError('loadDesign')\n return experimentId === undefined ? null : loadDesign(experimentId)\n }\n\n async function loadAnalysis(\n experimentId: number,\n ): Promise<Record<string, unknown> | null> {\n if (!pluginId) {\n request.setError('pluginId is required for loadAnalysis')\n return null\n }\n isLoading.value = true\n try {\n return await request.run(\n () => api.get<Record<string, unknown>>(\n `/experiments/${experimentId}/results/${pluginId}`,\n ),\n { success: 'load' },\n )\n } catch {\n return null\n } finally {\n isLoading.value = false\n }\n }\n\n async function loadCurrentAnalysis(): Promise<Record<string, unknown> | null> {\n const experimentId = currentExperimentIdOrError('loadAnalysis')\n return experimentId === undefined ? null : loadAnalysis(experimentId)\n }\n\n async function deleteDesign(experimentId: number): Promise<boolean> {\n isLoading.value = true\n try {\n await request.run(() => api.delete(`/experiments/${experimentId}/data`))\n return true\n } catch {\n return false\n } finally {\n isLoading.value = false\n }\n }\n\n async function deleteCurrentDesign(): Promise<boolean> {\n const experimentId = currentExperimentIdOrError('deleteDesign')\n return experimentId === undefined ? false : deleteDesign(experimentId)\n }\n\n async function deleteAnalysis(experimentId: number): Promise<boolean> {\n if (!pluginId) {\n request.setError('pluginId is required for deleteAnalysis')\n return false\n }\n isLoading.value = true\n try {\n await request.run(() => api.delete(`/experiments/${experimentId}/results/${pluginId}`))\n return true\n } catch {\n return false\n } finally {\n isLoading.value = false\n }\n }\n\n async function deleteCurrentAnalysis(): Promise<boolean> {\n const experimentId = currentExperimentIdOrError('deleteAnalysis')\n return experimentId === undefined ? false : deleteAnalysis(experimentId)\n }\n\n return {\n currentExperimentId,\n hasCurrentExperiment,\n isSaving,\n isLoading,\n error,\n lastLoadedAt,\n lastSavedAt,\n saveDesign,\n saveCurrentDesign,\n saveTemplatePreset,\n saveCurrentTemplatePreset,\n saveAnalysis,\n saveCurrentAnalysis,\n save,\n loadDesign,\n loadCurrentDesign,\n loadAnalysis,\n loadCurrentAnalysis,\n deleteDesign,\n deleteCurrentDesign,\n deleteAnalysis,\n deleteCurrentAnalysis,\n requireCurrentExperimentId,\n }\n}\n","import { computed, ref, type ComputedRef, type Ref } from 'vue'\nimport { useExperimentSave, type UseExperimentSaveOptions } from './useExperimentSave'\nimport {\n createTemplateCollection,\n ensureTemplateFromCollection,\n extractTemplateCollection,\n type BioTemplateEnvelope,\n type TemplateCollection,\n type TemplateCollectionEnvelope,\n} from '../templates'\n\nexport type TemplateCollectionInput =\n | TemplateCollectionEnvelope\n | BioTemplateEnvelope<unknown>\n | Array<BioTemplateEnvelope<unknown>>\n\nexport interface UseTemplateCollectionOptions extends UseExperimentSaveOptions {\n /** Initial template collection or factory; defaults to an empty collection. */\n initial?: TemplateCollectionInput | (() => TemplateCollectionInput)\n}\n\nexport interface UseTemplateCollectionReturn {\n /** Current experiment id from platform injection, URL query, or URL path. */\n currentExperimentId: ComputedRef<number | undefined>\n /** Whether a current experiment id is available. */\n hasCurrentExperiment: ComputedRef<boolean>\n /** Full template collection envelope ready to save into design_data. */\n templateCollection: Ref<TemplateCollectionEnvelope>\n /** Template map keyed by template_id. */\n templates: ComputedRef<TemplateCollection>\n /** Collection-level metadata, or an empty object. */\n metadata: ComputedRef<Record<string, unknown>>\n /** Template ids currently present in the collection. */\n templateIds: ComputedRef<string[]>\n /** Whether the collection contains at least one template. */\n hasTemplates: ComputedRef<boolean>\n /** Whether a load or delete operation is in progress. */\n isLoading: Ref<boolean>\n /** Whether a save operation is in progress. */\n isSaving: Ref<boolean>\n /** Error message from the last failed operation, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful load, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Timestamp of the last successful save, or null. */\n lastSavedAt: Ref<Date | null>\n /** Replace local state from a template envelope, collection envelope, or raw design_data. */\n apply: (value: unknown, metadata?: Record<string, unknown>) => TemplateCollectionEnvelope\n /** Reset local state to the configured initial collection. */\n reset: () => TemplateCollectionEnvelope\n /** Add or replace one template in the local collection. */\n setTemplate: (template: BioTemplateEnvelope<unknown>) => TemplateCollectionEnvelope\n /** Remove one template from the local collection. */\n removeTemplate: (templateId: string) => boolean\n /** Read one template from the local collection, returning null when missing. */\n getTemplate: <TTemplate extends BioTemplateEnvelope<unknown> = BioTemplateEnvelope<unknown>>(\n templateId: string,\n ) => TTemplate | null\n /** Read one template from the local collection, throwing when missing or mismatched. */\n requireTemplate: <TTemplate extends BioTemplateEnvelope<unknown> = BioTemplateEnvelope<unknown>>(\n templateId: string,\n ) => TTemplate\n /** Load template design_data for an experiment and apply it locally. */\n load: (experimentId: number) => Promise<TemplateCollectionEnvelope | null>\n /** Load template design_data for the current experiment and apply it locally. */\n loadCurrent: () => Promise<TemplateCollectionEnvelope | null>\n /** Save the local template collection as experiment design_data. */\n save: (experimentId: number) => Promise<boolean>\n /** Save the local template collection as current experiment design_data. */\n saveCurrent: () => Promise<boolean>\n /** Require and return the current experiment id, or throw when missing. */\n requireCurrentExperimentId: () => number\n}\n\n/** Manages a biology template collection in the current experiment's design_data. */\nexport function useTemplateCollection(\n options: UseTemplateCollectionOptions = {},\n): UseTemplateCollectionReturn {\n const experimentSave = useExperimentSave(options)\n const templateCollection = ref<TemplateCollectionEnvelope>(createInitialCollection(options))\n const templates = computed(() => extractTemplateCollection(templateCollection.value))\n const metadata = computed(() => templateCollection.value.metadata ?? {})\n const templateIds = computed(() => Object.keys(templates.value))\n const hasTemplates = computed(() => templateIds.value.length > 0)\n\n function apply(value: unknown, metadata?: Record<string, unknown>): TemplateCollectionEnvelope {\n templateCollection.value = normalizeTemplateCollection(value, metadata)\n return templateCollection.value\n }\n\n function reset(): TemplateCollectionEnvelope {\n templateCollection.value = createInitialCollection(options)\n return templateCollection.value\n }\n\n function setTemplate(template: BioTemplateEnvelope<unknown>): TemplateCollectionEnvelope {\n const nextTemplates = {\n ...templates.value,\n [template.template_id]: template,\n }\n templateCollection.value = createTemplateCollectionEnvelope(\n Object.values(nextTemplates),\n templateCollection.value.metadata,\n )\n return templateCollection.value\n }\n\n function removeTemplate(templateId: string): boolean {\n if (!templates.value[templateId]) return false\n const nextTemplates = { ...templates.value }\n delete nextTemplates[templateId]\n templateCollection.value = createTemplateCollectionEnvelope(\n Object.values(nextTemplates),\n templateCollection.value.metadata,\n )\n return true\n }\n\n function getTemplate<\n TTemplate extends BioTemplateEnvelope<unknown> = BioTemplateEnvelope<unknown>,\n >(templateId: string): TTemplate | null {\n return (templates.value[templateId] as TTemplate | undefined) ?? null\n }\n\n function requireTemplate<\n TTemplate extends BioTemplateEnvelope<unknown> = BioTemplateEnvelope<unknown>,\n >(templateId: string): TTemplate {\n return ensureTemplateFromCollection<TTemplate>(templateCollection.value, templateId)\n }\n\n async function load(experimentId: number): Promise<TemplateCollectionEnvelope | null> {\n const data = await experimentSave.loadDesign(experimentId)\n return data === null ? null : apply(data)\n }\n\n async function loadCurrent(): Promise<TemplateCollectionEnvelope | null> {\n const data = await experimentSave.loadCurrentDesign()\n return data === null ? null : apply(data)\n }\n\n async function save(experimentId: number): Promise<boolean> {\n return experimentSave.saveDesign(\n experimentId,\n templateCollection.value as unknown as Record<string, unknown>,\n )\n }\n\n async function saveCurrent(): Promise<boolean> {\n return experimentSave.saveCurrentDesign(\n templateCollection.value as unknown as Record<string, unknown>,\n )\n }\n\n return {\n currentExperimentId: experimentSave.currentExperimentId,\n hasCurrentExperiment: experimentSave.hasCurrentExperiment,\n templateCollection,\n templates,\n metadata,\n templateIds,\n hasTemplates,\n isLoading: experimentSave.isLoading,\n isSaving: experimentSave.isSaving,\n error: experimentSave.error,\n lastLoadedAt: experimentSave.lastLoadedAt,\n lastSavedAt: experimentSave.lastSavedAt,\n apply,\n reset,\n setTemplate,\n removeTemplate,\n getTemplate,\n requireTemplate,\n load,\n loadCurrent,\n save,\n saveCurrent,\n requireCurrentExperimentId: experimentSave.requireCurrentExperimentId,\n }\n}\n\nfunction createInitialCollection(options: UseTemplateCollectionOptions): TemplateCollectionEnvelope {\n const initial = typeof options.initial === 'function' ? options.initial() : options.initial\n if (initial === undefined) return { templates: {} }\n return normalizeTemplateCollection(initial)\n}\n\nfunction normalizeTemplateCollection(\n value: unknown,\n metadata?: Record<string, unknown>,\n): TemplateCollectionEnvelope {\n if (Array.isArray(value)) {\n return createTemplateCollectionEnvelope(value, metadata)\n }\n\n return createTemplateCollectionEnvelope(\n Object.values(extractTemplateCollection(value)),\n readCollectionMetadata(value) ?? metadata,\n )\n}\n\nfunction createTemplateCollectionEnvelope(\n templates: Array<BioTemplateEnvelope<unknown>>,\n metadata?: Record<string, unknown>,\n): TemplateCollectionEnvelope {\n return metadata === undefined\n ? createTemplateCollection(templates)\n : createTemplateCollection(templates, metadata)\n}\n\nfunction readCollectionMetadata(value: unknown): Record<string, unknown> | undefined {\n if (\n typeof value !== 'object'\n || value === null\n || Array.isArray(value)\n || !('metadata' in value)\n ) {\n return undefined\n }\n\n const metadata = (value as { metadata?: unknown }).metadata\n if (\n typeof metadata === 'object'\n && metadata !== null\n && !Array.isArray(metadata)\n ) {\n return metadata as Record<string, unknown>\n }\n return undefined\n}\n","import {\n createBioTemplateControlToolkit,\n getBioTemplateControlSchema,\n requireBioTemplateControlSchema,\n type BioTemplateControlSchema,\n type BioTemplateControlTarget,\n} from '../templates/controlSchemas'\nimport type {\n ControlSchemaOptions,\n ControlWorkspaceOptions,\n UseControlSchemaReturn,\n} from './controlSchemaTypes'\n\nexport {\n getBioTemplateControlSchema,\n requireBioTemplateControlSchema,\n}\n\nexport type {\n BioTemplateControlSchema,\n BioTemplateControlTarget,\n}\n\n/** Build FormBuilder schema, AppSidebar panels, and defaults for a built-in biology template or preset. */\nexport function useBioTemplateControls(\n target: BioTemplateControlTarget,\n options: ControlWorkspaceOptions = {},\n): UseControlSchemaReturn<BioTemplateControlSchema> {\n const { initialValues, topBarSettings: _topBarSettings, ...schemaOptions } = options\n const controls = createBioTemplateControlToolkit(target, schemaOptions as ControlSchemaOptions)\n\n if (initialValues === undefined) return controls\n\n return {\n ...controls,\n initialValues: {\n ...controls.initialValues,\n ...initialValues,\n },\n }\n}\n","import {\n getBioTemplateComponentBindings,\n getBioTemplateComponentProps as getTemplateComponentProps,\n toBioTemplateComponentImports,\n toBioTemplateComponentBindings,\n toBioTemplateComponentBindingsById,\n toBioTemplateComponentProps,\n toBioTemplateComponentPropsByComponent as toTemplateComponentPropsByComponent,\n toBioTemplateComponentPropsById,\n toBioTemplateComponentSnippets,\n toBioTemplateComponentUsage,\n type BioTemplateComponentBinding,\n type BioTemplateComponentBindingsById,\n type BioTemplateComponentImport,\n type BioTemplateComponentPropsByComponent,\n type BioTemplateComponentPropsBinding,\n type BioTemplateComponentPropsById,\n type BioTemplateComponentPropsLookupOptions,\n type BioTemplateResolvedComponentBinding,\n type BioTemplateComponentSnippet,\n type BioTemplateComponentTarget,\n type BioTemplateComponentUsage,\n} from '../templates/componentBindings'\nimport type {\n BioTemplateEnvelope,\n TemplateCollectionEnvelope,\n} from '../templates/types'\n\nexport {\n getBioTemplateComponentBindings,\n toBioTemplateComponentBindings,\n toBioTemplateComponentBindingsById,\n toBioTemplateComponentImports,\n toBioTemplateComponentProps,\n toBioTemplateComponentPropsById,\n toBioTemplateComponentSnippets,\n toBioTemplateComponentUsage,\n}\n\nexport type {\n BioTemplateComponentBinding,\n BioTemplateComponentBindingsById,\n BioTemplateComponentImport,\n BioTemplateComponentPropsByComponent,\n BioTemplateComponentPropsBinding,\n BioTemplateComponentPropsById,\n BioTemplateComponentPropsLookupOptions,\n BioTemplateResolvedComponentBinding,\n BioTemplateComponentSnippet,\n BioTemplateComponentTarget,\n BioTemplateComponentUsage,\n}\n\nexport interface UseBioTemplateComponentsReturn {\n bindings: BioTemplateComponentBinding[]\n imports: BioTemplateComponentImport[]\n componentBindings: BioTemplateResolvedComponentBinding[]\n componentBindingsById: BioTemplateComponentBindingsById\n componentProps: BioTemplateComponentPropsBinding[]\n componentPropsById: BioTemplateComponentPropsById\n componentPropsByComponent: BioTemplateComponentPropsByComponent\n snippets: BioTemplateComponentSnippet[]\n usage: BioTemplateComponentUsage | null\n getComponentProps: (\n component: string,\n options?: BioTemplateComponentPropsLookupOptions,\n ) => Record<string, unknown> | undefined\n}\n\n/** Return component props grouped by SDK component name for direct WellPlate/DoseCalculator binding. */\nexport function toBioTemplateComponentPropsByComponent(\n target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,\n): BioTemplateComponentPropsByComponent {\n return toTemplateComponentPropsByComponent(target)\n}\n\n/** Return the first props object for an SDK component, optionally restricted by template id or binding id. */\nexport function getBioTemplateComponentProps(\n target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,\n component: string,\n options: BioTemplateComponentPropsLookupOptions = {},\n): Record<string, unknown> | undefined {\n return getTemplateComponentProps(target, component, options)\n}\n\n/** Map built-in biology templates to SDK components such as WellPlate, DataFrame, DoseCalculator, and PlateMapEditor. */\nexport function useBioTemplateComponents(\n target: BioTemplateComponentTarget\n): UseBioTemplateComponentsReturn {\n return {\n bindings: getBioTemplateComponentBindings(target),\n imports: toBioTemplateComponentImports(target),\n componentBindings: typeof target === 'string' ? [] : toBioTemplateComponentBindings(target),\n componentBindingsById: typeof target === 'string' ? {} : toBioTemplateComponentBindingsById(target),\n componentProps: typeof target === 'string' ? [] : toBioTemplateComponentProps(target),\n componentPropsById: typeof target === 'string' ? {} : toBioTemplateComponentPropsById(target),\n componentPropsByComponent: typeof target === 'string' ? {} : toBioTemplateComponentPropsByComponent(target),\n snippets: typeof target === 'string' ? [] : toBioTemplateComponentSnippets(target),\n usage: typeof target === 'string' ? null : toBioTemplateComponentUsage(target),\n getComponentProps: (component, options) =>\n typeof target === 'string'\n ? undefined\n : getBioTemplateComponentProps(target, component, options),\n }\n}\n","import type {\n BioTemplateEnvelope,\n TemplateCollectionEnvelope,\n} from '../templates'\nimport type {\n ControlWorkspaceFormBinding,\n ControlWorkspaceOptions,\n ControlWorkspacePillNavBinding,\n ControlWorkspaceSidebarBinding,\n ControlWorkspaceTopBarSettingsBinding,\n UseControlSchemaReturn,\n UseControlWorkspaceReturn,\n} from './controlSchemaTypes'\nimport {\n useControlWorkspace,\n} from './useControlSchema'\nimport {\n useBioTemplateControls,\n type BioTemplateControlSchema,\n type BioTemplateControlTarget,\n} from './useBioTemplateControls'\nimport {\n useBioTemplateComponents,\n type BioTemplateComponentBinding,\n type BioTemplateComponentBindingsById,\n type BioTemplateComponentImport,\n type BioTemplateComponentPropsByComponent,\n type BioTemplateComponentPropsBinding,\n type BioTemplateComponentPropsById,\n type BioTemplateComponentSnippet,\n type BioTemplateComponentUsage,\n type UseBioTemplateComponentsReturn,\n} from './useBioTemplateComponents'\n\nexport type BioTemplateWorkspaceTarget = BioTemplateControlTarget\n\nexport interface BioTemplateRendererBinding {\n target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope\n}\n\nexport interface BioTemplateWorkspaceBindings {\n renderer: BioTemplateRendererBinding | null\n form: ControlWorkspaceFormBinding\n sidebar: ControlWorkspaceSidebarBinding\n topBar: UseControlWorkspaceReturn<BioTemplateControlSchema>['bindings']['topBar']\n topBarSettings: ControlWorkspaceTopBarSettingsBinding\n pillNav: ControlWorkspacePillNavBinding\n componentBindings: BioTemplateComponentBinding[]\n componentBindingsById: BioTemplateComponentBindingsById\n componentProps: BioTemplateComponentPropsBinding[]\n componentPropsById: BioTemplateComponentPropsById\n componentPropsByComponent: BioTemplateComponentPropsByComponent\n componentSnippets: BioTemplateComponentSnippet[]\n componentUsage: BioTemplateComponentUsage | null\n getComponentProps: UseBioTemplateComponentsReturn['getComponentProps']\n}\n\nexport interface UseBioTemplateWorkspaceReturn {\n target: BioTemplateWorkspaceTarget\n controls: UseControlSchemaReturn<BioTemplateControlSchema>\n workspace: UseControlWorkspaceReturn<BioTemplateControlSchema>\n controlSchema: BioTemplateControlSchema\n components: UseBioTemplateComponentsReturn\n values: UseControlWorkspaceReturn<BioTemplateControlSchema>['values']\n activeView: UseControlWorkspaceReturn<BioTemplateControlSchema>['activeView']\n form: ControlWorkspaceFormBinding\n sidebar: ControlWorkspaceSidebarBinding\n topBar: UseControlWorkspaceReturn<BioTemplateControlSchema>['topBar']\n pillNav: ControlWorkspacePillNavBinding\n topBarSettings: ControlWorkspaceTopBarSettingsBinding\n bindings: BioTemplateWorkspaceBindings\n renderer: BioTemplateRendererBinding | null\n componentBindings: BioTemplateComponentBinding[]\n componentBindingsById: BioTemplateComponentBindingsById\n componentImports: BioTemplateComponentImport[]\n componentProps: BioTemplateComponentPropsBinding[]\n componentPropsById: BioTemplateComponentPropsById\n componentPropsByComponent: BioTemplateComponentPropsByComponent\n componentSnippets: BioTemplateComponentSnippet[]\n componentUsage: BioTemplateComponentUsage | null\n getComponentProps: UseBioTemplateComponentsReturn['getComponentProps']\n}\n\n/** One-stop wiring for BioTemplateRenderer, FormBuilder, AppSidebar, and template-aware SDK components. */\nexport function useBioTemplateWorkspace(\n target: BioTemplateWorkspaceTarget,\n options: ControlWorkspaceOptions = {}\n): UseBioTemplateWorkspaceReturn {\n const controls = useBioTemplateControls(target, options)\n const workspace = useControlWorkspace(controls.controls, options)\n const components = useBioTemplateComponents(target)\n const renderer = isTemplateObjectTarget(target) ? { target } : null\n const bindings: BioTemplateWorkspaceBindings = {\n renderer,\n form: workspace.bindings.form,\n sidebar: workspace.bindings.sidebar,\n topBar: workspace.bindings.topBar,\n topBarSettings: workspace.bindings.topBarSettings,\n pillNav: workspace.bindings.pillNav,\n componentBindings: components.bindings,\n componentBindingsById: components.componentBindingsById,\n componentProps: components.componentProps,\n componentPropsById: components.componentPropsById,\n componentPropsByComponent: components.componentPropsByComponent,\n componentSnippets: components.snippets,\n componentUsage: components.usage,\n getComponentProps: components.getComponentProps,\n }\n\n return {\n target,\n controls,\n workspace,\n controlSchema: controls.controls,\n components,\n values: workspace.values,\n activeView: workspace.activeView,\n form: workspace.form,\n sidebar: workspace.sidebar,\n topBar: workspace.topBar,\n pillNav: workspace.pillNav,\n topBarSettings: workspace.topBarSettings,\n bindings,\n renderer,\n componentBindings: components.bindings,\n componentBindingsById: components.componentBindingsById,\n componentImports: components.imports,\n componentProps: components.componentProps,\n componentPropsById: components.componentPropsById,\n componentPropsByComponent: components.componentPropsByComponent,\n componentSnippets: components.snippets,\n componentUsage: components.usage,\n getComponentProps: components.getComponentProps,\n }\n}\n\nfunction isTemplateObjectTarget(\n target: BioTemplateWorkspaceTarget\n): target is BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope {\n return typeof target !== 'string'\n}\n","import {\n computed,\n effectScope,\n getCurrentScope,\n onScopeDispose,\n shallowRef,\n watch,\n type ComputedRef,\n type EffectScope,\n type Ref,\n} from 'vue'\nimport {\n createBioTemplatePackCollection,\n getBioTemplatePackInfo,\n type BioTemplatePackEntry,\n type TemplateCollectionEnvelope,\n type TemplatePackId,\n} from '../templates'\nimport {\n useTemplateCollection,\n type UseTemplateCollectionOptions,\n type UseTemplateCollectionReturn,\n} from './useTemplateCollection'\nimport {\n useBioTemplateWorkspace,\n type BioTemplateRendererBinding,\n type BioTemplateWorkspaceBindings,\n type UseBioTemplateWorkspaceReturn,\n} from './useBioTemplateWorkspace'\nimport type {\n BioTemplateComponentBinding,\n BioTemplateComponentBindingsById,\n BioTemplateComponentImport,\n BioTemplateComponentPropsByComponent,\n BioTemplateComponentPropsBinding,\n BioTemplateComponentPropsById,\n BioTemplateComponentSnippet,\n BioTemplateComponentUsage,\n} from './useBioTemplateComponents'\n\nexport type UseBioTemplatePackWorkspaceOptions = UseTemplateCollectionOptions\n\nexport interface UseBioTemplatePackWorkspaceReturn {\n packId: TemplatePackId\n pack: BioTemplatePackEntry\n collection: Ref<TemplateCollectionEnvelope>\n templates: UseTemplateCollectionReturn['templates']\n templateIds: UseTemplateCollectionReturn['templateIds']\n currentExperimentId: UseTemplateCollectionReturn['currentExperimentId']\n hasCurrentExperiment: UseTemplateCollectionReturn['hasCurrentExperiment']\n loading: UseTemplateCollectionReturn['isLoading']\n saving: UseTemplateCollectionReturn['isSaving']\n error: UseTemplateCollectionReturn['error']\n lastLoadedAt: UseTemplateCollectionReturn['lastLoadedAt']\n lastSavedAt: UseTemplateCollectionReturn['lastSavedAt']\n renderer: ComputedRef<BioTemplateRendererBinding>\n workspace: ComputedRef<UseBioTemplateWorkspaceReturn>\n form: ComputedRef<UseBioTemplateWorkspaceReturn['form']>\n sidebar: ComputedRef<UseBioTemplateWorkspaceReturn['sidebar']>\n topBar: ComputedRef<UseBioTemplateWorkspaceReturn['topBar']['value']>\n pillNav: ComputedRef<UseBioTemplateWorkspaceReturn['pillNav']>\n topBarSettings: ComputedRef<UseBioTemplateWorkspaceReturn['topBarSettings']>\n bindings: ComputedRef<BioTemplateWorkspaceBindings>\n componentBindings: ComputedRef<BioTemplateComponentBinding[]>\n componentBindingsById: ComputedRef<BioTemplateComponentBindingsById>\n componentImports: ComputedRef<BioTemplateComponentImport[]>\n componentProps: ComputedRef<BioTemplateComponentPropsBinding[]>\n componentPropsById: ComputedRef<BioTemplateComponentPropsById>\n componentPropsByComponent: ComputedRef<BioTemplateComponentPropsByComponent>\n componentSnippets: ComputedRef<BioTemplateComponentSnippet[]>\n componentUsage: ComputedRef<BioTemplateComponentUsage | null>\n getComponentProps: UseBioTemplateWorkspaceReturn['getComponentProps']\n applyCollection: (value: unknown, metadata?: Record<string, unknown>) => TemplateCollectionEnvelope\n resetToDefaultCollection: () => TemplateCollectionEnvelope\n save: UseTemplateCollectionReturn['save']\n saveCurrent: UseTemplateCollectionReturn['saveCurrent']\n load: UseTemplateCollectionReturn['load']\n loadCurrent: UseTemplateCollectionReturn['loadCurrent']\n requireCurrentExperimentId: UseTemplateCollectionReturn['requireCurrentExperimentId']\n}\n\n/** Create a complete workspace for a curated biology template pack from a single pack id. */\nexport function useBioTemplatePackWorkspace(\n packId: TemplatePackId | string,\n options: UseBioTemplatePackWorkspaceOptions = {},\n): UseBioTemplatePackWorkspaceReturn {\n const pack = getBioTemplatePackInfo(packId)\n if (!pack) {\n throw new Error(`Unknown template pack '${packId}'.`)\n }\n const resolvedPack = pack\n\n const templateCollection = useTemplateCollection({\n ...options,\n initial: createDefaultCollection,\n })\n const collection = templateCollection.templateCollection\n let workspaceScope: EffectScope | undefined\n\n function createWorkspace(value: TemplateCollectionEnvelope): UseBioTemplateWorkspaceReturn {\n workspaceScope?.stop()\n workspaceScope = effectScope()\n return workspaceScope.run(() => useBioTemplateWorkspace(value))!\n }\n\n const workspaceRef = shallowRef<UseBioTemplateWorkspaceReturn>(createWorkspace(collection.value))\n const workspace = computed(() => workspaceRef.value)\n const renderer = computed<BioTemplateRendererBinding>(() => ({ target: collection.value }))\n const form = computed(() => workspace.value.form)\n const sidebar = computed(() => workspace.value.sidebar)\n const topBar = computed(() => workspace.value.topBar.value)\n const pillNav = computed(() => workspace.value.pillNav)\n const topBarSettings = computed(() => workspace.value.topBarSettings)\n const bindings = computed(() => workspace.value.bindings)\n const componentBindings = computed(() => workspace.value.componentBindings)\n const componentBindingsById = computed(() => workspace.value.componentBindingsById)\n const componentImports = computed(() => workspace.value.componentImports)\n const componentProps = computed(() => workspace.value.componentProps)\n const componentPropsById = computed(() => workspace.value.componentPropsById)\n const componentPropsByComponent = computed(() => workspace.value.componentPropsByComponent)\n const componentSnippets = computed(() => workspace.value.componentSnippets)\n const componentUsage = computed(() => workspace.value.componentUsage)\n\n watch(collection, value => {\n workspaceRef.value = createWorkspace(value)\n })\n\n if (getCurrentScope()) {\n onScopeDispose(() => workspaceScope?.stop())\n }\n\n function createDefaultCollection(): TemplateCollectionEnvelope {\n return createBioTemplatePackCollection(resolvedPack.name)\n }\n\n function applyCollection(\n value: unknown,\n metadata?: Record<string, unknown>,\n ): TemplateCollectionEnvelope {\n return templateCollection.apply(value, metadata)\n }\n\n function resetToDefaultCollection(): TemplateCollectionEnvelope {\n return templateCollection.apply(createDefaultCollection())\n }\n\n return {\n packId: resolvedPack.name,\n pack: resolvedPack,\n collection,\n templates: templateCollection.templates,\n templateIds: templateCollection.templateIds,\n currentExperimentId: templateCollection.currentExperimentId,\n hasCurrentExperiment: templateCollection.hasCurrentExperiment,\n loading: templateCollection.isLoading,\n saving: templateCollection.isSaving,\n error: templateCollection.error,\n lastLoadedAt: templateCollection.lastLoadedAt,\n lastSavedAt: templateCollection.lastSavedAt,\n renderer,\n workspace,\n form,\n sidebar,\n topBar,\n pillNav,\n topBarSettings,\n bindings,\n componentBindings,\n componentBindingsById,\n componentImports,\n componentProps,\n componentPropsById,\n componentPropsByComponent,\n componentSnippets,\n componentUsage,\n getComponentProps: (component, options) => workspace.value.getComponentProps(component, options),\n applyCollection,\n resetToDefaultCollection,\n save: templateCollection.save,\n saveCurrent: templateCollection.saveCurrent,\n load: templateCollection.load,\n loadCurrent: templateCollection.loadCurrent,\n requireCurrentExperimentId: templateCollection.requireCurrentExperimentId,\n }\n}\n","import {\n computed,\n reactive,\n ref,\n watch,\n type ComputedRef,\n type Ref,\n} from 'vue'\nimport type { PillNavItem } from '../types'\nimport {\n createBioTemplatePresetCollectionFromControls,\n getBioTemplatePresetInfo,\n type BioTemplateControlValues,\n type BioTemplatePresetEntry,\n type TemplateCollectionEnvelope,\n type TemplatePresetId,\n} from '../templates'\nimport type {\n ControlWorkspaceOptions,\n ControlWorkspaceFormBinding,\n ControlWorkspacePillNavBinding,\n ControlWorkspaceSidebarBinding,\n ControlWorkspaceTopBarBinding,\n ControlWorkspaceTopBarSettingsBinding,\n UseControlSchemaReturn,\n} from './controlSchemaTypes'\nimport {\n useTemplateCollection,\n type UseTemplateCollectionOptions,\n type UseTemplateCollectionReturn,\n} from './useTemplateCollection'\nimport {\n useBioTemplateWorkspace,\n type BioTemplateRendererBinding,\n type UseBioTemplateWorkspaceReturn,\n} from './useBioTemplateWorkspace'\nimport type {\n BioTemplateComponentBinding,\n BioTemplateComponentBindingsById,\n BioTemplateComponentImport,\n BioTemplateComponentPropsByComponent,\n BioTemplateComponentPropsBinding,\n BioTemplateComponentPropsById,\n BioTemplateComponentSnippet,\n BioTemplateComponentUsage,\n} from './useBioTemplateComponents'\nimport type {\n BioTemplateControlSchema,\n} from './useBioTemplateControls'\n\nexport interface UseBioTemplatePresetWorkspaceOptions extends UseTemplateCollectionOptions {\n /** Optional schema-rendering options for the generated FormBuilder/AppSidebar controls. */\n controlOptions?: ControlWorkspaceOptions\n /** Initial control values for the generated preset. Merged over controlOptions.initialValues. */\n initialValues?: BioTemplateControlValues\n}\n\nexport interface UseBioTemplatePresetWorkspaceReturn {\n presetId: TemplatePresetId\n presetInfo: ComputedRef<BioTemplatePresetEntry | undefined>\n presetLabel: ComputedRef<string>\n collection: Ref<TemplateCollectionEnvelope>\n templates: UseTemplateCollectionReturn['templates']\n templateIds: UseTemplateCollectionReturn['templateIds']\n currentExperimentId: UseTemplateCollectionReturn['currentExperimentId']\n hasCurrentExperiment: UseTemplateCollectionReturn['hasCurrentExperiment']\n saving: UseTemplateCollectionReturn['isSaving']\n error: UseTemplateCollectionReturn['error']\n lastSavedAt: UseTemplateCollectionReturn['lastSavedAt']\n controls: UseControlSchemaReturn<BioTemplateControlSchema>\n controlSchema: BioTemplateControlSchema\n controlValues: Ref<BioTemplateControlValues>\n controlViewIds: ComputedRef<string[]>\n controlViewItems: ComputedRef<PillNavItem[]>\n activeControlView: Ref<string>\n form: ControlWorkspaceFormBinding\n sidebar: ControlWorkspaceSidebarBinding\n topBar: ComputedRef<ControlWorkspaceTopBarBinding>\n pillNav: ControlWorkspacePillNavBinding\n topBarSettings: ControlWorkspaceTopBarSettingsBinding\n bindings: BioTemplatePresetWorkspaceBindings\n renderer: ComputedRef<BioTemplateRendererBinding>\n workspace: ComputedRef<UseBioTemplateWorkspaceReturn>\n componentBindings: ComputedRef<BioTemplateComponentBinding[]>\n componentBindingsById: ComputedRef<BioTemplateComponentBindingsById>\n componentImports: ComputedRef<BioTemplateComponentImport[]>\n componentProps: ComputedRef<BioTemplateComponentPropsBinding[]>\n componentPropsById: ComputedRef<BioTemplateComponentPropsById>\n componentPropsByComponent: ComputedRef<BioTemplateComponentPropsByComponent>\n componentSnippets: ComputedRef<BioTemplateComponentSnippet[]>\n componentUsage: ComputedRef<BioTemplateComponentUsage | null>\n getComponentProps: UseBioTemplateWorkspaceReturn['getComponentProps']\n setActiveControlView: (viewId: string) => void\n setControlValues: (values: BioTemplateControlValues) => TemplateCollectionEnvelope\n updateControlValues: (values: Partial<BioTemplateControlValues>) => TemplateCollectionEnvelope\n applyCollection: (value: TemplateCollectionEnvelope) => TemplateCollectionEnvelope\n resetToDefaultCollection: () => TemplateCollectionEnvelope\n save: UseTemplateCollectionReturn['save']\n saveCurrent: UseTemplateCollectionReturn['saveCurrent']\n}\n\nexport interface BioTemplatePresetWorkspaceBindings {\n renderer: ComputedRef<BioTemplateRendererBinding>\n form: ControlWorkspaceFormBinding\n sidebar: ControlWorkspaceSidebarBinding\n topBar: ComputedRef<ControlWorkspaceTopBarBinding>\n topBarSettings: ControlWorkspaceTopBarSettingsBinding\n pillNav: ControlWorkspacePillNavBinding\n componentBindings: ComputedRef<BioTemplateComponentBinding[]>\n componentBindingsById: ComputedRef<BioTemplateComponentBindingsById>\n componentProps: ComputedRef<BioTemplateComponentPropsBinding[]>\n componentPropsById: ComputedRef<BioTemplateComponentPropsById>\n componentPropsByComponent: ComputedRef<BioTemplateComponentPropsByComponent>\n componentSnippets: ComputedRef<BioTemplateComponentSnippet[]>\n componentUsage: ComputedRef<BioTemplateComponentUsage | null>\n getComponentProps: UseBioTemplateWorkspaceReturn['getComponentProps']\n}\n\n/** Create a complete editable WellPlate/DoseCalculator workspace for dose design from a built-in biology template preset with current-experiment save wiring. */\nexport function useBioTemplatePresetWorkspace(\n presetId: TemplatePresetId,\n options: UseBioTemplatePresetWorkspaceOptions = {},\n): UseBioTemplatePresetWorkspaceReturn {\n const { controlOptions, initialValues, ...collectionOptions } = options\n const resolvedControlOptions = mergeInitialValues(controlOptions, initialValues)\n const presetWorkspace = useBioTemplateWorkspace(presetId, resolvedControlOptions)\n const presetInfo = computed(() => getBioTemplatePresetInfo(presetId))\n const presetLabel = computed(() => presetInfo.value?.label ?? humanizePresetId(presetId))\n const controls = presetWorkspace.controls\n const controlValues = ref<BioTemplateControlValues>({ ...controls.initialValues })\n const controlViewIds = computed(() => controls.viewIds)\n const controlViewItems = computed(() => controls.viewItems)\n const activeControlView = ref(controls.defaultView)\n const templateCollection = useTemplateCollection({\n ...collectionOptions,\n initial: createDefaultCollection,\n })\n const collection = templateCollection.templateCollection\n const workspace = computed(() => useBioTemplateWorkspace(collection.value, resolvedControlOptions))\n const renderer = computed<BioTemplateRendererBinding>(() => ({ target: collection.value }))\n const componentBindings = computed(() => workspace.value.componentBindings)\n const componentBindingsById = computed(() => workspace.value.componentBindingsById)\n const componentImports = computed(() => workspace.value.componentImports)\n const componentProps = computed(() => workspace.value.componentProps)\n const componentPropsById = computed(() => workspace.value.componentPropsById)\n const componentPropsByComponent = computed(() => workspace.value.componentPropsByComponent)\n const componentSnippets = computed(() => workspace.value.componentSnippets)\n const componentUsage = computed(() => workspace.value.componentUsage)\n const form = reactive({\n ...controls.form,\n modelValue: controlValues.value,\n 'onUpdate:modelValue': setControlValues,\n }) as ControlWorkspaceFormBinding\n const sidebar = reactive({\n ...controls.sidebar,\n activeView: activeControlView.value,\n modelValue: controlValues.value,\n 'onUpdate:modelValue': setControlValues,\n values: controlValues.value,\n 'onUpdate:values': setControlValues,\n }) as ControlWorkspaceSidebarBinding\n const pillNav = reactive({\n items: controls.viewItems,\n currentItemId: activeControlView.value,\n onSelect: (item: { id: string }) => setActiveControlView(item.id),\n }) as ControlWorkspacePillNavBinding\n const topBarSettings = reactive({\n showSettings: true,\n settingsConfig: {\n ...controls.topBarSettingsConfig,\n values: controlValues.value,\n },\n onSettingsValuesChange: setControlValues,\n }) as ControlWorkspaceTopBarSettingsBinding\n const topBarProps = computed<ControlWorkspaceTopBarBinding>(() => ({\n pillNav: pillNav.items,\n currentPillId: pillNav.currentItemId,\n onPillSelect: pillNav.onSelect,\n ...topBarSettings,\n }))\n\n function getComponentProps(\n component: string,\n options?: Parameters<UseBioTemplateWorkspaceReturn['getComponentProps']>[1],\n ) {\n return workspace.value.getComponentProps(component, options)\n }\n\n const bindings: BioTemplatePresetWorkspaceBindings = {\n renderer,\n form,\n sidebar,\n topBar: topBarProps,\n topBarSettings,\n pillNav,\n componentBindings,\n componentBindingsById,\n componentProps,\n componentPropsById,\n componentPropsByComponent,\n componentSnippets,\n componentUsage,\n getComponentProps,\n }\n\n function createDefaultCollection(): TemplateCollectionEnvelope {\n return createBioTemplatePresetCollectionFromControls(presetId, controls.initialValues)\n }\n\n watch(\n controlValues,\n (values) => {\n syncControlValueBindings(values)\n templateCollection.apply(createBioTemplatePresetCollectionFromControls(presetId, values))\n },\n { deep: true },\n )\n\n watch(activeControlView, (viewId) => {\n sidebar.activeView = viewId\n pillNav.currentItemId = viewId\n }, { flush: 'sync' })\n watch(() => sidebar.activeView, setActiveControlView, { flush: 'sync' })\n watch(() => pillNav.currentItemId, setActiveControlView, { flush: 'sync' })\n\n function applyCollection(value: TemplateCollectionEnvelope): TemplateCollectionEnvelope {\n return templateCollection.apply(value)\n }\n\n function setActiveControlView(viewId: string): void {\n if (!controlViewIds.value.includes(viewId)) return\n activeControlView.value = viewId\n }\n\n function setControlValues(values: BioTemplateControlValues): TemplateCollectionEnvelope {\n const nextValues = { ...values }\n controlValues.value = nextValues\n syncControlValueBindings(nextValues)\n return templateCollection.apply(createBioTemplatePresetCollectionFromControls(presetId, nextValues))\n }\n\n function updateControlValues(values: Partial<BioTemplateControlValues>): TemplateCollectionEnvelope {\n return setControlValues({\n ...controlValues.value,\n ...values,\n })\n }\n\n function resetToDefaultCollection(): TemplateCollectionEnvelope {\n return setControlValues({ ...controls.initialValues })\n }\n\n function syncControlValueBindings(values: BioTemplateControlValues): void {\n form.modelValue = values\n sidebar.modelValue = values\n sidebar.values = values\n topBarSettings.settingsConfig = {\n ...topBarSettings.settingsConfig,\n values,\n }\n }\n\n return {\n presetId,\n presetInfo,\n presetLabel,\n collection,\n templates: templateCollection.templates,\n templateIds: templateCollection.templateIds,\n currentExperimentId: templateCollection.currentExperimentId,\n hasCurrentExperiment: templateCollection.hasCurrentExperiment,\n saving: templateCollection.isSaving,\n error: templateCollection.error,\n lastSavedAt: templateCollection.lastSavedAt,\n controls,\n controlSchema: controls.controls,\n controlValues,\n controlViewIds,\n controlViewItems,\n activeControlView,\n form,\n sidebar,\n topBar: topBarProps,\n pillNav,\n topBarSettings,\n bindings,\n renderer,\n workspace,\n componentBindings,\n componentBindingsById,\n componentImports,\n componentProps,\n componentPropsById,\n componentPropsByComponent,\n componentSnippets,\n componentUsage,\n getComponentProps,\n setActiveControlView,\n setControlValues,\n updateControlValues,\n applyCollection,\n resetToDefaultCollection,\n save: templateCollection.save,\n saveCurrent: templateCollection.saveCurrent,\n }\n}\n\nfunction humanizePresetId(value: string): string {\n return value\n .replace(/[-_]+/g, ' ')\n .replace(/\\b\\w/g, match => match.toUpperCase())\n}\n\nfunction mergeInitialValues(\n controlOptions: ControlWorkspaceOptions | undefined,\n initialValues: BioTemplateControlValues | undefined,\n): ControlWorkspaceOptions | undefined {\n if (initialValues === undefined) return controlOptions\n return {\n ...(controlOptions ?? {}),\n initialValues: {\n ...(controlOptions?.initialValues ?? {}),\n ...initialValues,\n },\n }\n}\n","import { ref, computed, type Ref, type ComputedRef } from 'vue'\nimport type { Rack, SlotPosition, WellPlateFormat, Well } from '../types'\n\nexport interface UseRackEditorOptions {\n defaultFormat?: WellPlateFormat\n defaultInjectionVolume?: number\n maxRacks?: number\n minRacks?: number\n}\n\nexport interface UseRackEditorReturn {\n racks: Ref<Rack[]>\n activeRack: ComputedRef<Rack | undefined>\n activeRackId: Ref<string>\n\n addRack(name?: string): Rack\n removeRack(rackId: string): void\n reorderRacks(fromIndex: number, toIndex: number): void\n updateRack(rackId: string, data: Partial<Rack>): void\n setActiveRack(rackId: string): void\n\n setWellData(rackId: string, wellId: string, data: Partial<Well>): void\n clearWell(rackId: string, wellId: string): void\n clearAllWells(rackId: string): void\n fillSeries(rackId: string, prefix?: string): void\n\n getAllWells(): Array<{ rackId: string; wellId: string; well: Partial<Well> }>\n totalSampleCount: ComputedRef<number>\n reset(): void\n}\n\nconst SLOT_CYCLE: SlotPosition[] = ['R', 'G', 'B', 'Y']\n\nconst PLATE_CONFIGS: Record<WellPlateFormat, { rows: number; cols: number }> = {\n 6: { rows: 2, cols: 3 },\n 12: { rows: 3, cols: 4 },\n 24: { rows: 4, cols: 6 },\n 48: { rows: 6, cols: 8 },\n 54: { rows: 6, cols: 9 },\n 96: { rows: 8, cols: 12 },\n 384: { rows: 16, cols: 24 },\n}\n\nlet globalIdCounter = 0\n\nfunction generateId(): string {\n globalIdCounter++\n return `rack-${Date.now()}-${globalIdCounter}`\n}\n\n/** Manages a set of sample racks with well-level assignment, series fill, and multi-rack reordering. */\nexport function useRackEditor(\n initialRacks?: Rack[],\n options?: UseRackEditorOptions,\n): UseRackEditorReturn {\n const defaultFormat = options?.defaultFormat ?? 54\n const defaultInjVol = options?.defaultInjectionVolume ?? 5\n const maxRacks = options?.maxRacks ?? 10\n const minRacks = options?.minRacks ?? 1\n\n function createDefaultRack(name?: string, slotIndex?: number): Rack {\n const slot = SLOT_CYCLE[(slotIndex ?? 0) % SLOT_CYCLE.length]!\n return {\n id: generateId(),\n name: name ?? `Rack ${racks.value.length + 1}`,\n format: defaultFormat,\n slot,\n injectionVolume: defaultInjVol,\n wells: {},\n }\n }\n\n const racks = ref<Rack[]>(\n initialRacks && initialRacks.length > 0\n ? initialRacks.map(r => ({ ...r }))\n : [createDefaultRack('Rack 1', 0)]\n ) as Ref<Rack[]>\n\n const activeRackId = ref<string>(racks.value[0]?.id ?? '')\n\n const activeRack = computed(() =>\n racks.value.find(r => r.id === activeRackId.value) ?? racks.value[0]\n )\n\n function addRack(name?: string): Rack {\n if (racks.value.length >= maxRacks) {\n return racks.value[racks.value.length - 1]!\n }\n const rack = createDefaultRack(name, racks.value.length)\n racks.value.push(rack)\n activeRackId.value = rack.id\n return rack\n }\n\n function removeRack(rackId: string): void {\n if (racks.value.length <= minRacks) return\n const index = racks.value.findIndex(r => r.id === rackId)\n if (index === -1) return\n racks.value.splice(index, 1)\n if (activeRackId.value === rackId && racks.value[0]) {\n activeRackId.value = racks.value[0].id\n }\n }\n\n function reorderRacks(fromIndex: number, toIndex: number): void {\n if (fromIndex < 0 || fromIndex >= racks.value.length) return\n if (toIndex < 0 || toIndex >= racks.value.length) return\n if (fromIndex === toIndex) return\n const [moved] = racks.value.splice(fromIndex, 1)\n if (moved) {\n racks.value.splice(toIndex, 0, moved)\n }\n }\n\n function updateRack(rackId: string, data: Partial<Rack>): void {\n const rack = racks.value.find(r => r.id === rackId)\n if (!rack) return\n if (data.name !== undefined) rack.name = data.name\n if (data.format !== undefined) rack.format = data.format\n if (data.slot !== undefined) rack.slot = data.slot\n if (data.injectionVolume !== undefined) rack.injectionVolume = data.injectionVolume\n if (data.wells !== undefined) rack.wells = data.wells\n }\n\n function setActiveRack(rackId: string): void {\n if (racks.value.some(r => r.id === rackId)) {\n activeRackId.value = rackId\n }\n }\n\n function setWellData(rackId: string, wellId: string, data: Partial<Well>): void {\n const rack = racks.value.find(r => r.id === rackId)\n if (!rack) return\n rack.wells[wellId] = { ...rack.wells[wellId], ...data }\n }\n\n function clearWell(rackId: string, wellId: string): void {\n const rack = racks.value.find(r => r.id === rackId)\n if (!rack) return\n delete rack.wells[wellId]\n }\n\n function clearAllWells(rackId: string): void {\n const rack = racks.value.find(r => r.id === rackId)\n if (!rack) return\n rack.wells = {}\n }\n\n function fillSeries(rackId: string, prefix: string = 'S'): void {\n const rack = racks.value.find(r => r.id === rackId)\n if (!rack) return\n\n const config = PLATE_CONFIGS[rack.format]\n if (!config) return\n\n let counter = 1\n for (let row = 0; row < config.rows; row++) {\n for (let col = 0; col < config.cols; col++) {\n const rowLabel = String.fromCharCode(65 + row)\n const wellId = `${rowLabel}${col + 1}`\n // Only fill empty wells\n if (!rack.wells[wellId]?.sampleType) {\n const paddedNum = String(counter).padStart(3, '0')\n rack.wells[wellId] = {\n id: wellId,\n row,\n col,\n state: 'filled',\n sampleType: 'sample',\n metadata: {\n label: `${prefix}${paddedNum}`,\n injectionVolume: rack.injectionVolume,\n injectionCount: 1,\n },\n }\n }\n counter++\n }\n }\n }\n\n function getAllWells(): Array<{ rackId: string; wellId: string; well: Partial<Well> }> {\n const result: Array<{ rackId: string; wellId: string; well: Partial<Well> }> = []\n for (const rack of racks.value) {\n for (const [wellId, well] of Object.entries(rack.wells)) {\n result.push({ rackId: rack.id, wellId, well })\n }\n }\n return result\n }\n\n const totalSampleCount = computed(() => {\n let count = 0\n for (const rack of racks.value) {\n count += Object.keys(rack.wells).length\n }\n return count\n })\n\n function reset(): void {\n const rack = createDefaultRack('Rack 1', 0)\n racks.value = [rack]\n activeRackId.value = rack.id\n }\n\n return {\n racks,\n activeRack,\n activeRackId,\n addRack,\n removeRack,\n reorderRacks,\n updateRack,\n setActiveRack,\n setWellData,\n clearWell,\n clearAllWells,\n fillSeries,\n getAllWells,\n totalSampleCount,\n reset,\n }\n}\n","import { computed, toValue, type ComputedRef, type Ref } from 'vue'\nimport type { GroupItem } from '../types'\n\nexport type GroupAssignmentZone = 'zone1' | 'zone2'\n\nexport type GroupAssignmentSource<T> =\n | T\n | Ref<T>\n | ComputedRef<T>\n | (() => T)\n\nexport interface GroupAssignmentState {\n group1: string[]\n group2: string[]\n}\n\nexport interface UseGroupAssignmentOptions {\n groups: GroupAssignmentSource<readonly GroupItem[]>\n group1: GroupAssignmentSource<readonly string[]>\n group2: GroupAssignmentSource<readonly string[]>\n label1?: GroupAssignmentSource<string | null | undefined>\n label2?: GroupAssignmentSource<string | null | undefined>\n minPerGroup?: GroupAssignmentSource<number | null | undefined>\n}\n\nexport interface UseGroupAssignmentReturn {\n unassignedGroups: ComputedRef<GroupItem[]>\n zone1Groups: ComputedRef<GroupItem[]>\n zone2Groups: ComputedRef<GroupItem[]>\n zone1Count: ComputedRef<number>\n zone2Count: ComputedRef<number>\n isValid: ComputedRef<boolean>\n validationMessage: ComputedRef<string | null>\n getZoneForGroup: (groupName: string) => GroupAssignmentZone | null\n assignToZone: (groupName: string, zone: GroupAssignmentZone) => GroupAssignmentState\n removeFromZone: (groupName: string, zone: GroupAssignmentZone) => GroupAssignmentState\n clearAll: () => GroupAssignmentState\n}\n\n/** Shared two-zone group assignment model for control/treatment style workflows. */\nexport function useGroupAssignment(options: UseGroupAssignmentOptions): UseGroupAssignmentReturn {\n const groups = computed(() => [...toValue(options.groups)])\n const group1 = computed(() => [...toValue(options.group1)])\n const group2 = computed(() => [...toValue(options.group2)])\n const label1 = computed(() => toValue(options.label1) || 'Control')\n const label2 = computed(() => toValue(options.label2) || 'Treatment')\n const minPerGroup = computed(() => normalizeMinimum(toValue(options.minPerGroup)))\n const group1Set = computed(() => new Set(group1.value))\n const group2Set = computed(() => new Set(group2.value))\n\n const unassignedGroups = computed(() =>\n groups.value.filter(group => !group1Set.value.has(group.name) && !group2Set.value.has(group.name))\n )\n\n const zone1Groups = computed(() =>\n groups.value.filter(group => group1Set.value.has(group.name))\n )\n\n const zone2Groups = computed(() =>\n groups.value.filter(group => group2Set.value.has(group.name))\n )\n\n const zone1Count = computed(() =>\n zone1Groups.value.reduce((sum, group) => sum + group.count, 0)\n )\n\n const zone2Count = computed(() =>\n zone2Groups.value.reduce((sum, group) => sum + group.count, 0)\n )\n\n const isValid = computed(() =>\n group1.value.length >= minPerGroup.value &&\n group2.value.length >= minPerGroup.value\n )\n\n const validationMessage = computed(() => {\n if (isValid.value) return null\n const missing1 = Math.max(0, minPerGroup.value - group1.value.length)\n const missing2 = Math.max(0, minPerGroup.value - group2.value.length)\n const parts: string[] = []\n if (missing1 > 0) parts.push(`${missing1} more to ${label1.value}`)\n if (missing2 > 0) parts.push(`${missing2} more to ${label2.value}`)\n return `Add ${parts.join(' and ')}`\n })\n\n function getZoneForGroup(groupName: string): GroupAssignmentZone | null {\n if (group1Set.value.has(groupName)) return 'zone1'\n if (group2Set.value.has(groupName)) return 'zone2'\n return null\n }\n\n function assignToZone(groupName: string, zone: GroupAssignmentZone): GroupAssignmentState {\n const next = removeFromBoth(groupName)\n if (zone === 'zone1') {\n return {\n group1: [...next.group1, groupName],\n group2: next.group2,\n }\n }\n return {\n group1: next.group1,\n group2: [...next.group2, groupName],\n }\n }\n\n function removeFromZone(groupName: string, zone: GroupAssignmentZone): GroupAssignmentState {\n if (zone === 'zone1') {\n return {\n group1: group1.value.filter(name => name !== groupName),\n group2: group2.value,\n }\n }\n return {\n group1: group1.value,\n group2: group2.value.filter(name => name !== groupName),\n }\n }\n\n function clearAll(): GroupAssignmentState {\n return { group1: [], group2: [] }\n }\n\n function removeFromBoth(groupName: string): GroupAssignmentState {\n return {\n group1: group1.value.filter(name => name !== groupName),\n group2: group2.value.filter(name => name !== groupName),\n }\n }\n\n return {\n unassignedGroups,\n zone1Groups,\n zone2Groups,\n zone1Count,\n zone2Count,\n isValid,\n validationMessage,\n getZoneForGroup,\n assignToZone,\n removeFromZone,\n clearAll,\n }\n}\n\nfunction normalizeMinimum(value: number | null | undefined): number {\n if (value === null || value === undefined || !Number.isFinite(value)) return 1\n return Math.max(0, Math.floor(value))\n}\n","/**\n * Composable for generating dilution series used in plate-based experiments.\n * Provides geometric series generation from presets (2x, 3x, ½ log, 10x)\n * and utility functions for series manipulation.\n */\n\nexport interface LevelEntry {\n value: number\n replicates: number\n}\n\nexport interface DilutionPreset {\n label: string\n factor: number | 'half-log'\n}\n\nexport const DEFAULT_PRESETS: DilutionPreset[] = [\n { label: '2x', factor: 2 },\n { label: '3x', factor: 3 },\n { label: '½ log', factor: 'half-log' },\n { label: '10x', factor: 10 },\n]\n\nexport const DEFAULT_UNITS = ['µM', 'nM', 'mM', 'pM']\n\nfunction resolveFactor(factor: number | 'half-log'): number {\n return factor === 'half-log' ? Math.sqrt(10) : factor\n}\n\nfunction roundToSignificant(value: number, digits: number = 4): number {\n if (value === 0) return 0\n const magnitude = Math.floor(Math.log10(Math.abs(value))) + 1\n const factor = Math.pow(10, digits - magnitude)\n return Math.round(value * factor) / factor\n}\n\nexport function generateDilutionSeries(\n startConcentration: number,\n count: number,\n factor: number | 'half-log',\n defaultReplicates: number = 1,\n): LevelEntry[] {\n if (count < 1 || startConcentration <= 0) return []\n\n const divisor = resolveFactor(factor)\n const levels: LevelEntry[] = []\n\n for (let i = 0; i < count; i++) {\n levels.push({\n value: roundToSignificant(startConcentration / Math.pow(divisor, i)),\n replicates: defaultReplicates,\n })\n }\n\n return levels\n}\n\nexport interface UseReagentSeriesReturn {\n generateSeries: (\n start: number,\n count: number,\n factor: number | 'half-log',\n defaultReplicates?: number,\n ) => LevelEntry[]\n sortLevels: (levels: LevelEntry[], order?: 'asc' | 'desc') => LevelEntry[]\n totalPositions: (levels: LevelEntry[]) => number\n}\n\n/** Generates and sorts geometric dilution series (2x, 3x, ½ log, 10x) for plate-based experiments. */\nexport function useReagentSeries(): UseReagentSeriesReturn {\n function generateSeries(\n start: number,\n count: number,\n factor: number | 'half-log',\n defaultReplicates: number = 1,\n ): LevelEntry[] {\n return generateDilutionSeries(start, count, factor, defaultReplicates)\n }\n\n function sortLevels(levels: LevelEntry[], order: 'asc' | 'desc' = 'desc'): LevelEntry[] {\n return [...levels].sort((a, b) =>\n order === 'desc' ? b.value - a.value : a.value - b.value,\n )\n }\n\n function totalPositions(levels: LevelEntry[]): number {\n return levels.reduce((sum, l) => sum + l.replicates, 0)\n }\n\n return { generateSeries, sortLevels, totalPositions }\n}\n","import type { ProtocolStepType } from '../types'\n\n// Parameter definition for step templates\nexport interface ParameterDefinition {\n key: string\n label: string\n type: 'number' | 'text' | 'select' | 'concentration' | 'temperature' | 'duration' | 'reagent'\n unit?: string\n options?: Array<{ value: string; label: string }>\n required?: boolean\n default?: unknown\n min?: number\n max?: number\n placeholder?: string\n}\n\n// Step template definition\nexport interface StepTemplate {\n id: string\n type: ProtocolStepType\n name: string\n description?: string\n defaultDuration?: number\n parameters: ParameterDefinition[]\n isBuiltIn?: boolean\n}\n\nexport const BUILT_IN_PROTOCOL_TEMPLATES: StepTemplate[] = [\n {\n id: 'builtin-incubation',\n type: 'incubation',\n name: 'Incubation',\n description: 'Incubate samples at specified conditions',\n defaultDuration: 60,\n isBuiltIn: true,\n parameters: [\n {\n key: 'temperature',\n label: 'Temperature',\n type: 'temperature',\n unit: '°C',\n required: true,\n default: 37,\n min: -80,\n max: 100,\n },\n {\n key: 'duration',\n label: 'Duration',\n type: 'duration',\n unit: 'hours',\n required: true,\n default: 24,\n min: 0,\n },\n {\n key: 'co2',\n label: 'CO2',\n type: 'number',\n unit: '%',\n required: false,\n default: 5,\n min: 0,\n max: 100,\n },\n {\n key: 'humidity',\n label: 'Humidity',\n type: 'number',\n unit: '%',\n required: false,\n default: 95,\n min: 0,\n max: 100,\n },\n ],\n },\n {\n id: 'builtin-wash',\n type: 'wash',\n name: 'Wash',\n description: 'Wash samples with buffer',\n defaultDuration: 5,\n isBuiltIn: true,\n parameters: [\n {\n key: 'buffer',\n label: 'Buffer',\n type: 'select',\n required: true,\n default: 'PBS',\n options: [\n { value: 'PBS', label: 'PBS' },\n { value: 'PBST', label: 'PBST' },\n { value: 'TBS', label: 'TBS' },\n { value: 'TBST', label: 'TBST' },\n { value: 'Water', label: 'Water' },\n { value: 'Other', label: 'Other' },\n ],\n },\n {\n key: 'volume',\n label: 'Volume',\n type: 'number',\n unit: 'µL',\n required: true,\n default: 200,\n min: 0,\n },\n {\n key: 'cycles',\n label: 'Wash Cycles',\n type: 'number',\n required: true,\n default: 3,\n min: 1,\n max: 10,\n },\n ],\n },\n {\n id: 'builtin-addition',\n type: 'addition',\n name: 'Addition',\n description: 'Add reagent or compound to samples',\n defaultDuration: 5,\n isBuiltIn: true,\n parameters: [\n {\n key: 'reagent',\n label: 'Reagent',\n type: 'text',\n required: true,\n placeholder: 'Enter reagent name',\n },\n {\n key: 'volume',\n label: 'Volume',\n type: 'number',\n unit: 'µL',\n required: true,\n default: 100,\n min: 0,\n },\n {\n key: 'concentration',\n label: 'Concentration',\n type: 'concentration',\n required: false,\n },\n ],\n },\n {\n id: 'builtin-measurement',\n type: 'measurement',\n name: 'Measurement',\n description: 'Take measurements using specified instrument',\n defaultDuration: 15,\n isBuiltIn: true,\n parameters: [\n {\n key: 'instrument',\n label: 'Instrument',\n type: 'select',\n required: true,\n options: [\n { value: 'plate_reader', label: 'Plate Reader' },\n { value: 'microscope', label: 'Microscope' },\n { value: 'flow_cytometer', label: 'Flow Cytometer' },\n { value: 'spectrophotometer', label: 'Spectrophotometer' },\n { value: 'other', label: 'Other' },\n ],\n },\n {\n key: 'readout',\n label: 'Readout Type',\n type: 'text',\n required: false,\n placeholder: 'e.g., Absorbance 450nm',\n },\n {\n key: 'notes',\n label: 'Parameters',\n type: 'text',\n required: false,\n placeholder: 'Additional parameters',\n },\n ],\n },\n {\n id: 'builtin-centrifuge',\n type: 'centrifuge',\n name: 'Centrifuge',\n description: 'Centrifuge samples',\n defaultDuration: 10,\n isBuiltIn: true,\n parameters: [\n {\n key: 'speed',\n label: 'Speed',\n type: 'number',\n unit: 'RPM',\n required: true,\n default: 1000,\n min: 100,\n max: 20000,\n },\n {\n key: 'duration',\n label: 'Duration',\n type: 'number',\n unit: 'min',\n required: true,\n default: 5,\n min: 1,\n },\n {\n key: 'temperature',\n label: 'Temperature',\n type: 'temperature',\n unit: '°C',\n required: false,\n default: 4,\n min: -10,\n max: 40,\n },\n ],\n },\n {\n id: 'builtin-transfer',\n type: 'transfer',\n name: 'Transfer',\n description: 'Transfer samples between containers',\n defaultDuration: 10,\n isBuiltIn: true,\n parameters: [\n {\n key: 'source',\n label: 'Source',\n type: 'text',\n required: true,\n placeholder: 'Source container',\n },\n {\n key: 'destination',\n label: 'Destination',\n type: 'text',\n required: true,\n placeholder: 'Destination container',\n },\n {\n key: 'volume',\n label: 'Volume',\n type: 'number',\n unit: 'µL',\n required: true,\n default: 100,\n min: 0,\n },\n ],\n },\n {\n id: 'builtin-mix',\n type: 'mix',\n name: 'Mix',\n description: 'Mix samples',\n defaultDuration: 5,\n isBuiltIn: true,\n parameters: [\n {\n key: 'method',\n label: 'Method',\n type: 'select',\n required: true,\n default: 'pipette',\n options: [\n { value: 'pipette', label: 'Pipette mixing' },\n { value: 'vortex', label: 'Vortex' },\n { value: 'shaker', label: 'Orbital shaker' },\n { value: 'inversion', label: 'Inversion' },\n ],\n },\n {\n key: 'duration',\n label: 'Duration',\n type: 'number',\n unit: 'sec',\n required: false,\n default: 30,\n min: 1,\n },\n {\n key: 'speed',\n label: 'Speed/Intensity',\n type: 'text',\n required: false,\n placeholder: 'e.g., 300 RPM',\n },\n ],\n },\n {\n id: 'builtin-custom',\n type: 'custom',\n name: 'Custom Step',\n description: 'Custom protocol step',\n defaultDuration: 15,\n isBuiltIn: true,\n parameters: [\n {\n key: 'description',\n label: 'Description',\n type: 'text',\n required: true,\n placeholder: 'Describe the step',\n },\n {\n key: 'notes',\n label: 'Notes',\n type: 'text',\n required: false,\n placeholder: 'Additional notes',\n },\n ],\n },\n]\n","import { ref, computed, type Ref, type ComputedRef } from 'vue'\nimport type { ProtocolStep, ProtocolStepType, ProtocolStepStatus } from '../types'\nimport {\n BUILT_IN_PROTOCOL_TEMPLATES,\n type ParameterDefinition,\n type StepTemplate,\n} from './protocolTemplateCatalog'\n\nexport type { ParameterDefinition, StepTemplate } from './protocolTemplateCatalog'\n\n// Validation result\nexport interface ValidationResult {\n valid: boolean\n errors: Record<string, string>\n}\n\nexport interface UseProtocolTemplatesReturn {\n builtInTemplates: StepTemplate[]\n customTemplates: Ref<StepTemplate[]>\n allTemplates: ComputedRef<StepTemplate[]>\n getTemplateByType: (type: ProtocolStepType) => StepTemplate | undefined\n getTemplateById: (id: string) => StepTemplate | undefined\n saveCustomTemplate: (template: StepTemplate) => void\n deleteCustomTemplate: (templateId: string) => void\n validateStep: (step: ProtocolStep, template: StepTemplate) => ValidationResult\n createStepFromTemplate: (template: StepTemplate, overrides?: Partial<ProtocolStep>) => ProtocolStep\n formatParameterValue: (value: unknown, param: ParameterDefinition) => string\n}\n\n// Storage key for custom templates\nconst STORAGE_KEY = 'mint-custom-protocol-templates'\n\nfunction isStepTemplate(value: unknown): value is StepTemplate {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Partial<StepTemplate>\n return (\n typeof candidate.id === 'string'\n && typeof candidate.type === 'string'\n && typeof candidate.name === 'string'\n && Array.isArray(candidate.parameters)\n )\n}\n\nfunction loadCustomTemplates(): StepTemplate[] {\n try {\n const stored = localStorage.getItem(STORAGE_KEY)\n if (stored) {\n const parsed = JSON.parse(stored)\n return Array.isArray(parsed) ? parsed.filter(isStepTemplate) : []\n }\n } catch {\n // Ignore errors\n }\n return []\n}\n\nfunction saveCustomTemplatesToStorage(templates: StepTemplate[]) {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(templates))\n } catch {\n // Ignore errors\n }\n}\n\n/** Provides built-in and custom protocol step templates with validation and step creation helpers. */\nexport function useProtocolTemplates(): UseProtocolTemplatesReturn {\n const customTemplates = ref<StepTemplate[]>(loadCustomTemplates())\n\n const allTemplates = computed(() => {\n return [...BUILT_IN_PROTOCOL_TEMPLATES, ...customTemplates.value]\n })\n\n function getTemplateByType(type: ProtocolStepType): StepTemplate | undefined {\n // First check custom templates, then built-in\n return (\n customTemplates.value.find((t) => t.type === type) ||\n BUILT_IN_PROTOCOL_TEMPLATES.find((t) => t.type === type)\n )\n }\n\n function getTemplateById(id: string): StepTemplate | undefined {\n return (\n customTemplates.value.find((t) => t.id === id) ||\n BUILT_IN_PROTOCOL_TEMPLATES.find((t) => t.id === id)\n )\n }\n\n function saveCustomTemplate(template: StepTemplate) {\n const newTemplate = {\n ...template,\n isBuiltIn: false,\n id: template.id || `custom-${Date.now()}`,\n }\n\n const index = customTemplates.value.findIndex((t) => t.id === newTemplate.id)\n if (index >= 0) {\n customTemplates.value[index] = newTemplate\n } else {\n customTemplates.value.push(newTemplate)\n }\n\n saveCustomTemplatesToStorage(customTemplates.value)\n }\n\n function deleteCustomTemplate(templateId: string) {\n const index = customTemplates.value.findIndex((t) => t.id === templateId)\n if (index >= 0) {\n customTemplates.value.splice(index, 1)\n saveCustomTemplatesToStorage(customTemplates.value)\n }\n }\n\n function validateStep(step: ProtocolStep, template: StepTemplate): ValidationResult {\n const errors: Record<string, string> = {}\n\n if (!step.name || step.name.trim() === '') {\n errors.name = 'Step name is required'\n }\n\n for (const param of template.parameters) {\n if (param.required) {\n const value = step.parameters?.[param.key]\n if (value === undefined || value === null || value === '') {\n errors[param.key] = `${param.label} is required`\n }\n }\n\n if (param.type === 'number' || param.type === 'temperature' || param.type === 'duration') {\n const value = step.parameters?.[param.key]\n if (value !== undefined && value !== null && value !== '') {\n const numValue = Number(value)\n if (isNaN(numValue)) {\n errors[param.key] = `${param.label} must be a number`\n } else {\n if (param.min !== undefined && numValue < param.min) {\n errors[param.key] = `${param.label} must be at least ${param.min}`\n }\n if (param.max !== undefined && numValue > param.max) {\n errors[param.key] = `${param.label} must be at most ${param.max}`\n }\n }\n }\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors,\n }\n }\n\n function createStepFromTemplate(\n template: StepTemplate,\n overrides?: Partial<ProtocolStep>\n ): ProtocolStep {\n // Build default parameters from template\n const parameters: Record<string, unknown> = {}\n for (const param of template.parameters) {\n if (param.default !== undefined) {\n parameters[param.key] = param.default\n }\n }\n\n return {\n id: `step-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n type: template.type,\n name: template.name,\n description: template.description,\n duration: template.defaultDuration,\n status: 'pending' as ProtocolStepStatus,\n parameters,\n order: 0,\n ...overrides,\n }\n }\n\n function formatParameterValue(value: unknown, param: ParameterDefinition): string {\n if (value === undefined || value === null || value === '') {\n return '-'\n }\n\n if (param.type === 'select' && param.options) {\n const option = param.options.find((o) => o.value === value)\n if (option) return option.label\n }\n\n if (param.type === 'concentration' && typeof value === 'object') {\n const conc = value as { value: number; unit: string }\n return `${conc.value} ${conc.unit}`\n }\n\n const strValue = String(value)\n if (param.unit) {\n return `${strValue} ${param.unit}`\n }\n\n return strValue\n }\n\n return {\n builtInTemplates: BUILT_IN_PROTOCOL_TEMPLATES,\n customTemplates,\n allTemplates,\n getTemplateByType,\n getTemplateById,\n saveCustomTemplate,\n deleteCustomTemplate,\n validateStep,\n createStepFromTemplate,\n formatParameterValue,\n }\n}\n"],"mappings":";;;;;;AA+CA,SAAgB,eACd,SACmC;CACnC,MAAM,OAAO,eAAe,QAAQ,QAAQ,KAAK,CAAC;CAClD,MAAM,UAAU,eAAe,QAAQ,QAAQ,QAAQ,IAAI,KAAK;AA4BhE,QAAO;EACL;EACA,aA5BkB,eAAe;GACjC,MAAM,QAAQ,CAAC,GAAG,QAAQ,QAAQ,MAAM,CAAC;GACzC,MAAM,aAAa,KAAK;AAExB,OAAI,CAAC,QAAQ,SAAS,CAAC,YAAY,OAAO,CAAC,WAAW,UAAW,QAAO;GAExE,MAAM,YAAY,WAAW;GAC7B,MAAM,gBAAgB,QAAQ,QAAQ,cAAc,IAAI;AAExD,UAAO,MAAM,MAAM,GAAG,MAAM;IAC1B,MAAM,SAAS,QAAQ,SAAS,GAAG,WAAW,IAAI;IAClD,MAAM,SAAS,QAAQ,SAAS,GAAG,WAAW,IAAI;AAElD,QAAI,QAAQ,QACV,QAAO,QAAQ,QAAQ,QAAQ,QAAQ;KACrC,KAAK,WAAW;KAChB;KACA;KACA;KACD,CAAC;AAGJ,WAAO,kBAAkB,QAAQ,QAAQ,WAAW,EAAE,eAAe,CAAC;KACtE;IACF;EAKD;;AAGH,SAAgB,kBACd,QACA,QACA,YAAuB,OACvB,UAAoC,EAAE,EAC9B;CACR,MAAM,SAAS,WAAW,QAAQ,WAAW,KAAA;CAC7C,MAAM,SAAS,WAAW,QAAQ,WAAW,KAAA;AAE7C,KAAI,UAAU,OAAQ,QAAO;AAC7B,KAAI,OAAQ,QAAO;AACnB,KAAI,OAAQ,QAAO;CAEnB,MAAM,cAAc,sBAAsB,QAAQ,QAAQ;CAC1D,MAAM,cAAc,sBAAsB,QAAQ,QAAQ;CAE1D,IAAI,aAAa;AACjB,KAAI,OAAO,gBAAgB,YAAY,OAAO,gBAAgB,SAC5D,cAAa,cAAc;KAE3B,cAAa,OAAO,YAAY,CAAC,cAAc,OAAO,YAAY,CAAC;AAGrE,QAAO,cAAc,QAAQ,aAAa,CAAC;;AAG7C,SAAS,sBAAsB,OAAgB,SAA4C;AACzF,KAAI,iBAAiB,KAAM,QAAO,MAAM,SAAS;AACjD,KAAI,OAAO,UAAU,YAAY,QAAQ,kBAAkB,MACzD,QAAO,MAAM,aAAa;AAE5B,QAAO;;;;;AC5FT,SAAgB,cAAqB,SAAkE;CACrG,MAAM,QAAQ,eAAe,qBAAqB,QAAQ,QAAQ,MAAM,CAAC,CAAC;CAC1E,MAAM,UAAU,eAAe,QAAQ,QAAQ,QAAQ,IAAI,KAAK;CAEhE,SAAS,QAAQ,MAAa,eAAoD;EAChF,MAAM,cAAc,kBAAkB,KAAA,IAAY,MAAM,QAAQ,qBAAqB,cAAc;AACnG,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,uBAAuB,QAAQ,QAAQ,KAAK,EAAE,YAAY;;AASnE,QAAO;EACL;EACA,eARoB,eAAe;GACnC,MAAM,QAAQ,CAAC,GAAG,QAAQ,QAAQ,MAAM,CAAC;AACzC,OAAI,CAAC,QAAQ,SAAS,CAAC,MAAM,MAAO,QAAO;AAC3C,UAAO,MAAM,QAAO,SAAQ,QAAQ,KAAK,CAAC;IAC1C;EAKA;EACD;;AAGH,SAAgB,qBAAqB,OAA0C;AAC7E,SAAQ,SAAS,IAAI,MAAM,CAAC,aAAa;;AAG3C,SAAgB,uBAAuB,WAAgC,OAAwB;CAC7F,MAAM,kBAAkB,qBAAqB,MAAM;AACnD,KAAI,CAAC,gBAAiB,QAAO;AAE7B,SADe,MAAM,QAAQ,UAAU,GAAG,YAAY,CAAC,UAAU,EACnD,MAAM,UAAU;AAC5B,MAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAClD,SAAO,OAAO,MAAM,CAAC,aAAa,CAAC,SAAS,gBAAgB;GAC5D;;;;ACvDJ,IAAM,SAAS,IAAa,EAAE,CAAC;AAC/B,IAAI,SAAS;;AAGb,SAAgB,WAAW;CACzB,SAAS,KAAK,SAAiB,OAAsB,WAAW,WAAW,MAAM;EAC/E,MAAM,KAAK;AACX,SAAO,MAAM,KAAK;GAAE;GAAI;GAAS;GAAM;GAAU,CAAC;AAClD,mBAAiB,QAAQ,GAAG,EAAE,SAAS;;CAGzC,SAAS,QAAQ,SAAiB,WAAW,MAAM;AACjD,OAAK,SAAS,WAAW,SAAS;;CAGpC,SAAS,MAAM,SAAiB,WAAW,KAAM;AAC/C,OAAK,SAAS,SAAS,SAAS;;CAGlC,SAAS,QAAQ,SAAiB,WAAW,KAAM;AACjD,OAAK,SAAS,WAAW,SAAS;;CAGpC,SAAS,KAAK,SAAiB,WAAW,MAAM;AAC9C,OAAK,SAAS,QAAQ,SAAS;;CAGjC,SAAS,QAAQ,IAAY;AAC3B,SAAO,QAAQ,OAAO,MAAM,QAAO,MAAK,EAAE,OAAO,GAAG;;CAGtD,SAAS,QAAQ;AACf,SAAO,QAAQ,EAAE;;AAGnB,QAAO;EAAE;EAAQ;EAAM;EAAS;EAAO;EAAS;EAAM;EAAS;EAAO;;;;;AC5BxE,SAAgB,WAA2B;CACzC,MAAM,WAAW,kBAAkB;AAGnC,UAAS,YAAY;CAErB,MAAM,SAAS,eAAe,SAAS,QAAQ,CAAC;CAEhD,SAAS,cAAc;AACrB,WAAS,QAAQ,OAAO,QAAQ,UAAU;;CAG5C,SAAS,SAAS,OAAyB;AACzC,WAAS,QAAQ;;AAGnB,QAAO;EACL;EACA;EACA;EACD;;;;ACVH,IAAM,aAAa;CACjB,WAAW,OAAgB,UAAU,6BAA4C;AAC/E,MAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,GACrD,QAAO;AAET,MAAI,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,EAC3C,QAAO;AAET,SAAO;;CAGT,YAAY,OAAgB,KAAa,YAAoC;AAC3E,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,MAAM,SAAS,IACjB,QAAO,WAAW,oBAAoB,IAAI;AAE5C,SAAO;;CAGT,YAAY,OAAgB,KAAa,YAAoC;AAC3E,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,MAAM,SAAS,IACjB,QAAO,WAAW,mBAAmB,IAAI;AAE3C,SAAO;;CAGT,MAAM,OAAgB,KAAa,YAAoC;AACrE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,QAAQ,IACV,QAAO,WAAW,oBAAoB;AAExC,SAAO;;CAGT,MAAM,OAAgB,KAAa,YAAoC;AACrE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,QAAQ,IACV,QAAO,WAAW,mBAAmB;AAEvC,SAAO;;CAGT,UAAU,OAAgB,SAAiB,YAAoC;AAC7E,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,CAAC,QAAQ,KAAK,MAAM,CACtB,QAAO,WAAW;AAEpB,SAAO;;CAGT,QAAQ,OAAgB,UAAU,4BAA2C;AAC3E,MAAI,OAAO,UAAU,YAAY,CAAC,MAAO,QAAO;AAEhD,MAAI,CADe,6BACH,KAAK,MAAM,CACzB,QAAO;AAET,SAAO;;CAEV;AAED,SAAgB,mBACd,OACA,YACA,UACe;AACf,KAAI,CAAC,WAAY,QAAO;AAExB,KAAI,WAAW,UAAU;EACvB,MAAM,UAAU,OAAO,WAAW,aAAa,WAAW,WAAW,WAAW,KAAA;EAChF,MAAM,QAAQ,WAAW,SAAS,OAAO,QAAQ;AACjD,MAAI,MAAO,QAAO;;AAGpB,KAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,GACrD,QAAO;AAGT,KAAI,WAAW,cAAc,KAAA,GAAW;EACtC,MAAM,SAAS,OAAO,WAAW,cAAc,WAC3C;GAAE,OAAO,WAAW;GAAW,SAAS,KAAA;GAAW,GACnD,WAAW;EACf,MAAM,QAAQ,WAAW,UAAU,OAAO,OAAO,OAAO,OAAO,QAAQ;AACvE,MAAI,MAAO,QAAO;;AAGpB,KAAI,WAAW,cAAc,KAAA,GAAW;EACtC,MAAM,SAAS,OAAO,WAAW,cAAc,WAC3C;GAAE,OAAO,WAAW;GAAW,SAAS,KAAA;GAAW,GACnD,WAAW;EACf,MAAM,QAAQ,WAAW,UAAU,OAAO,OAAO,OAAO,OAAO,QAAQ;AACvE,MAAI,MAAO,QAAO;;AAGpB,KAAI,WAAW,QAAQ,KAAA,GAAW;EAChC,MAAM,SAAS,OAAO,WAAW,QAAQ,WACrC;GAAE,OAAO,WAAW;GAAK,SAAS,KAAA;GAAW,GAC7C,WAAW;EACf,MAAM,QAAQ,WAAW,IAAI,OAAO,OAAO,OAAO,OAAO,QAAQ;AACjE,MAAI,MAAO,QAAO;;AAGpB,KAAI,WAAW,QAAQ,KAAA,GAAW;EAChC,MAAM,SAAS,OAAO,WAAW,QAAQ,WACrC;GAAE,OAAO,WAAW;GAAK,SAAS,KAAA;GAAW,GAC7C,WAAW;EACf,MAAM,QAAQ,WAAW,IAAI,OAAO,OAAO,OAAO,OAAO,QAAQ;AACjE,MAAI,MAAO,QAAO;;AAGpB,KAAI,WAAW,YAAY,KAAA,GAAW;EACpC,MAAM,SAAS,WAAW,mBAAmB,SACzC;GAAE,OAAO,WAAW;GAAS,SAAS,KAAA;GAAW,GACjD,WAAW;EACf,MAAM,QAAQ,WAAW,QAAQ,OAAO,OAAO,OAAO,OAAO,QAAQ;AACrE,MAAI,MAAO,QAAO;;AAGpB,KAAI,WAAW,OAAO;EACpB,MAAM,UAAU,OAAO,WAAW,UAAU,WAAW,WAAW,QAAQ,KAAA;EAC1E,MAAM,QAAQ,WAAW,MAAM,OAAO,QAAQ;AAC9C,MAAI,MAAO,QAAO;;AAGpB,KAAI,WAAW,QAAQ;EACrB,MAAM,cAAc,MAAM,QAAQ,WAAW,OAAO,GAAG,WAAW,SAAS,CAAC,WAAW,OAAO;AAC9F,OAAK,MAAM,QAAQ,aAAa;GAC9B,MAAM,QAAQ,KAAK,OAAO,SAAS;AACnC,OAAI,MAAO,QAAO;;;AAItB,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3ET,SAAgB,QACd,eACA,QAA8C,EAAE,EAC9B;CAClB,MAAM,yBAAyB,UAAU,cAAc;CAGvD,IAAI,iBAAiB,gBAAgB,uBAAuB;CAG5D,MAAM,OAAO,SAAS,gBAAgB,uBAAuB,CAAC;CAG9D,MAAM,SAAS,SACb,OAAO,KAAK,cAAc,CAAC,QAAQ,KAAK,QAAQ;AAC9C,MAAI,OAAO;AACX,SAAO;IACN,EAAE,CAAkC,CACxC;CAED,MAAM,UAAU,SACd,OAAO,KAAK,cAAc,CAAC,QAAQ,KAAK,QAAQ;AAC9C,MAAI,OAAO;AACX,SAAO;IACN,EAAE,CAA4B,CAClC;CAED,MAAM,QAAQ,SACZ,OAAO,KAAK,cAAc,CAAC,QAAQ,KAAK,QAAQ;AAC9C,MAAI,OAAO;AACX,SAAO;IACN,EAAE,CAA4B,CAClC;CAED,MAAM,eAAe,IAAI,MAAM;AAG/B,cACS,EAAE,GAAG,MAAM,IACjB,YAAY;AACX,OAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,OAAM,OAAO,QAAQ,SAAoB,eAAe;IAG5D,EAAE,MAAM,MAAM,CACf;CAGD,SAAS,cAAc,OAAwB;EAC7C,MAAM,QAAQ,KAAK;EACnB,MAAM,aAAa,MAAM;EACzB,MAAM,QAAQ,mBAAmB,OAAO,YAAY,KAAgC;AACpF,SAAO,SAAS;AAChB,SAAO,UAAU;;CAInB,SAAS,WAAoB;EAC3B,IAAI,aAAa;AACjB,OAAK,MAAM,SAAS,OAAO,KAAK,KAAK,CACnC,KAAI,CAAC,cAAc,MAAM,CACvB,cAAa;AAGjB,SAAO;;CAIT,MAAM,UAAU,eAAe;AAC7B,SAAO,OAAO,OAAO,OAAO,CAAC,OAAM,UAAS,UAAU,KAAK;GAC3D;CAGF,MAAM,UAAU,eAAe;AAC7B,SAAO,OAAO,OAAO,MAAM,CAAC,MAAK,MAAK,EAAE;GACxC;CAGF,SAAS,cAAiC,OAAU,OAAmB;AACnE,OAAiC,SAAmB;AACtD,MAAI,QAAQ,OACV,eAAc,MAAgB;;CAKlC,SAAS,cAAc,OAAe,OAA4B;AAChE,SAAO,SAAS;;CAIlB,SAAS,gBAAgB,OAAe,YAAY,MAAY;AAC9D,UAAQ,SAAS;AACjB,MAAI,UACF,eAAc,MAAM;;CAKxB,SAAS,MAAM,QAA2B;EACxC,MAAM,cAAc,SAAS;GAAE,GAAG;GAAgB,GAAG;GAAQ,GAAG;AAChE,OAAK,MAAM,OAAO,OAAO,KAAK,KAAK,EAAE;AACjC,QAAiC,OAAO,gBAAgB,UAAU,YAAY,KAAgB,CAAC;AACjG,UAAO,OAAO;AACd,WAAQ,OAAO;AACf,SAAM,OAAO;;;CAIjB,SAAS,aAAa,QAAiB;EACrC,MAAM,aAAa,gBAAgB,UAAU,OAAO,CAAC;AACrD,mBAAiB,gBAAgB,WAAW;AAE5C,OAAK,MAAM,OAAO,OAAO,KAAK,KAAK,EAAE;AACnC,OAAI,OAAO,WAAY;AACvB,UAAQ,KAAiC;AACzC,UAAO,OAAO;AACd,UAAO,QAAQ;AACf,UAAO,MAAM;;AAGf,OAAK,MAAM,OAAO,OAAO,KAAK,WAAW,EAAE;AACvC,QAAiC,OAAO,gBAAgB,UAAU,WAAW,KAAgB,CAAC;AAChG,UAAO,OAAO;AACd,WAAQ,OAAO;AACf,SAAM,OAAO;;;CAKjB,SAAS,aAAa,UAA6C;AACjE,SAAO,OAAO,MAA6B;AACzC,MAAG,gBAAgB;AAGnB,QAAK,MAAM,SAAS,OAAO,KAAK,KAAK,CACnC,SAAQ,SAAS;AAGnB,OAAI,CAAC,UAAU,CACb;AAGF,gBAAa,QAAQ;AACrB,OAAI;AACF,UAAM,SAAS,KAAK;aACZ;AACR,iBAAa,QAAQ;;;;CAM3B,SAAS,cAAiC,OAAU;EAClD,MAAM,WAAW;AACjB,SAAO;GACL,YAAY,KAAK;GACjB,wBAAwB,UAAgB,cAAc,OAAO,MAAM;GACnE,cAAc,gBAAgB,SAAS;GACvC,OAAO,QAAQ,YAAY,OAAO,YAAY;GAC/C;;AAGH,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,UAAU,OAAyB;CAC1C,MAAM,MAAM,MAAM,MAAM;AAExB,KAAI,MAAM,QAAQ,IAAI,CACpB,QAAO,IAAI,KAAI,SAAQ,UAAU,KAAK,CAAC;AAGzC,KAAI,cAAc,IAAI,CACpB,QAAO,OAAO,YACZ,OAAO,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC,CACjE;AAGH,QAAO;;AAGT,SAAS,cAAc,OAAkD;AACvE,QAAO,OAAO,UAAU,SAAS,KAAK,MAAM,KAAK;;;;;;;;;;;ACnQnD,SAAgB,kBACd,WACA,MACS;AACT,KAAI,SAAS,UACX,QAAO,UAAU,IAAI,OAAO,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAE/D,KAAI,QAAQ,UACV,QAAO,UAAU,GAAG,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAE7D,KAAI,SAAS,UACX,QAAO,CAAC,kBAAkB,UAAU,KAAK,KAAK;CAGhD,MAAM,QAAQ,KAAK,UAAU;AAE7B,KAAI,QAAQ,UAAW,QAAO,UAAU,UAAU;AAClD,KAAI,SAAS,UAAW,QAAO,UAAU,UAAU;AACnD,KAAI,QAAQ,UAAW,QAAO,OAAO,UAAU,YAAY,QAAQ,UAAU;AAC7E,KAAI,QAAQ,UAAW,QAAO,OAAO,UAAU,YAAY,QAAQ,UAAU;AAC7E,KAAI,SAAS,UAAW,QAAO,OAAO,UAAU,YAAY,SAAS,UAAU;AAC/E,KAAI,SAAS,UAAW,QAAO,OAAO,UAAU,YAAY,SAAS,UAAU;AAC/E,KAAI,QAAQ,UAAW,QAAO,UAAU,GAAG,SAAS,MAAM;AAC1D,KAAI,WAAW,UAAW,QAAO,CAAC,UAAU,MAAM,SAAS,MAAM;AACjE,KAAI,YAAY,UAAW,QAAO,CAAC,CAAC;AACpC,KAAI,WAAW,UAAW,QAAO,CAAC;AAClC,KAAI,cAAc,WAAW;AAC3B,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,SAAS,UAAU,SAAS;AACxE,MAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,SAAS,UAAU,SAAS;AACnE,SAAO;;AAGT,QAAO;;;AAIT,SAAgB,gBAAgB,QAAyC;AACvE,QAAO,OAAO,QAAQ,OAAO,MAAM,SAAS,SAAS,KAAK,SAAS,GAAG,OAAO;;;AAI/E,SAAgB,cAAc,QAAuC;AACnE,QAAO,gBAAgB,OAAO,CAAC,SAAS,YAAY,QAAQ,OAAO;;;AAIrE,SAAS,kBAAkB,GAAgC;CACzD,MAAM,QAAoB,EAAE;AAE5B,KAAI,EAAE,aAAa,KAAA,EAAW,OAAM,WAAW,EAAE;AACjD,KAAI,EAAE,cAAc,KAAA,EAAW,OAAM,YAAY,EAAE;AACnD,KAAI,EAAE,cAAc,KAAA,EAAW,OAAM,YAAY,EAAE;AACnD,KAAI,EAAE,QAAQ,KAAA,EAAW,OAAM,MAAM,EAAE;AACvC,KAAI,EAAE,QAAQ,KAAA,EAAW,OAAM,MAAM,EAAE;AACvC,KAAI,EAAE,UAAU,KAAA,EAAW,OAAM,QAAQ,EAAE;AAE3C,KAAI,EAAE,YAAY,KAAA,EAChB,OAAM,UACJ,OAAO,EAAE,YAAY,WACjB,IAAI,OAAO,EAAE,QAAQ,GACrB;EAAE,OAAO,IAAI,OAAO,EAAE,QAAQ,MAAM;EAAE,SAAS,EAAE,QAAQ;EAAS;AAG1E,QAAO;;AAGT,SAAgB,mBACd,QACA,aACyB;CACzB,MAAM,gBAAgB,EAAE;AAExB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM;AAClB,MAAI,eAAe,UAAU,aAAa,IAAI,CAC5C,eAAc,OAAQ,YAAwC;WACrD,MAAM,iBAAiB,KAAA,EAChC,eAAc,OAAO,MAAM;MAE3B,eAAc,OAAO,eAAe,MAAM,KAAK;;AAInD,QAAO;;AAGT,SAAgB,WACd,QACA,cACqC;CACrC,MAAM,QAA6C,EAAE;AAErD,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAmB,MAAM,aAAa,kBAAkB,MAAM,WAAW,GAAG,EAAE;EACpF,MAAM,cAAc,cAAc,SAAS,MAAM;EAEjD,MAAM,mBAG4B,EAAE;AAEpC,MAAI,aAAa,UAAU;GACzB,MAAM,KAAK,YAAY;AACvB,oBAAiB,MAAM,OAAO,aAAa,GAAG,OAAO,SAAc,CAAC;;AAGtE,MAAI,iBAAiB,SAAS,EAC5B,MAAK,SAAS;AAGhB,MAAI,OAAO,KAAK,KAAK,CAAC,SAAS,EAC7B,OAAM,MAAM,QAAQ;;AAIxB,QAAO;;AAGT,SAAgB,aAAgB,QAAa,QAA4B;AACvE,QAAO,OAAO,GAAG,OAAO,QAAQ,GAAG,OAAO;;AAG5C,SAAgB,cACd,QACA,QACM;AACN,MAAK,MAAM,OAAO,OAAO,KAAK,OAAO,CACnC,QAAO,OAAO;AAEhB,QAAO,OAAO,QAAQ,OAAO;;AAG/B,SAAS,UAAU,QAAgB,KAAsB;AACvD,QAAO,OAAO,UAAU,eAAe,KAAK,QAAQ,IAAI;;;;;;;;;;;;;;;;;;AC9G1D,SAAgB,eACd,QACA,aACA,cACyB;CACzB,MAAM,gBAAgB,WAAW,OAAO;CACxC,MAAM,SAAS,SAA4B,cAAc,OAAO,CAAC;CACjE,MAAM,WAAW,SAA8B,gBAAgB,OAAO,CAAC;CACvE,MAAM,QAAQ,SAA8C,WAAW,QAAQ,aAAa,CAAC;CAG7F,MAAM,OAAO,QACX,mBAAsB,QAAQ,YAAY,EAC1C,MACD;CAGD,SAAS,eAAe,MAAuB;EAC7C,MAAM,cAAc,cAAc,SAAS;AAC3C,MAAI,aAAa,QACf,QAAO,YAAY,QAAQ,KAAK,KAAU;EAG5C,MAAM,QAAQ,OAAO,MAAM,MAAM,EAAE,SAAS,KAAK;AACjD,MAAI,OAAO,UACT,QAAO,kBAAkB,MAAM,WAAW,KAAK,KAAgC;AAGjF,SAAO;;CAGT,SAAS,iBAAiB,IAAqB;EAC7C,MAAM,UAAU,SAAS,MAAM,MAAM,EAAE,OAAO,GAAG;AACjD,MAAI,SAAS,UACX,QAAO,kBAAkB,QAAQ,WAAW,KAAK,KAAgC;AAEnF,SAAO;;CAIT,SAAS,sBAAsB,OAAiD;EAC9E,MAAM,QAAQ,sBAAsB,MAAM,KAAK;EAC/C,MAAM,YAAY,KAAK,cAAc,MAAM,KAAgB;EAE3D,MAAM,SAAkC;GACtC,GAAG,MAAM;GACT,GAAI,MAAM,SAAS,EAAE;GACtB;EAGD,MAAM,cAAc,cAAc,SAAS,MAAM;AACjD,MAAI,aAAa,MACf,QAAO,OAAO,QAAQ,YAAY,MAAM,KAAK,KAAU,CAAC;EAI1D,MAAM,UAAU,gBAAgB,MAAM,KAAK;AAC3C,MAAI,QACF,QAAO,UAAU;AAInB,MAAI,MAAM,QAAQ;AAChB,UAAO,aAAa,UAAU;AAC9B,UAAO,yBAAyB,UAAU;;AAE5C,SAAO,SAAS,UAAU;AAI1B,MADiB,UAAU,MAEzB,QAAO,QAAQ;AAIjB,MAAI,MAAM,YAAa,QAAO,cAAc,MAAM;AAClD,MAAI,MAAM,KAAM,QAAO,OAAO,MAAM;AACpC,MAAI,MAAM,SAAU,QAAO,WAAW;AACtC,MAAI,MAAM,SAAU,QAAO,WAAW;AAGtC,MAAI,MAAM,SAAS,WAAW,CAAC,OAAO,KACpC,QAAO,OAAO,MAAM;AAGtB,SAAO;;CAGT,SAAS,gBAAgB,MAA6C;EACpE,MAAM,cAAc,cAAc,SAAS;AAC3C,MAAI,aAAa,QACf,QAAO,YAAY,QAAQ,KAAK,KAAU;EAG5C,MAAM,QAAQ,OAAO,MAAM,MAAM,EAAE,SAAS,KAAK;AACjD,MAAI,OAAO,OAAO,QAChB,QAAO,MAAM,MAAM;;CAOvB,MAAM,cAAc,IAAI,EAAE;CAE1B,MAAM,qBAAqB,eAAe;AACxC,MAAI,CAAC,cAAc,MAAM,MAAO,QAAO,KAAK,QAAQ;EAEpD,MAAM,OAAO,cAAc,MAAM,MAAM,YAAY;AACnD,MAAI,CAAC,KAAM,QAAO;AAElB,OAAK,MAAM,WAAW,KAAK,UAAU;AACnC,OAAI,CAAC,iBAAiB,QAAQ,GAAG,CAAE;AACnC,QAAK,MAAM,SAAS,QAAQ,QAAQ;AAClC,QAAI,CAAC,eAAe,MAAM,KAAK,CAAE;AACjC,QAAI,CAAC,KAAK,cAAc,MAAM,KAAK,CAAE,QAAO;;;AAGhD,SAAO;GACP;CAEF,SAAS,SAAkB;AACzB,MAAI,CAAC,cAAc,MAAM,MAAO,QAAO;EAGvC,MAAM,OAAO,cAAc,MAAM,MAAM,YAAY;AACnD,MAAI,CAAC,KAAM,QAAO;EAElB,IAAI,QAAQ;AACZ,OAAK,MAAM,WAAW,KAAK,UAAU;AACnC,OAAI,CAAC,iBAAiB,QAAQ,GAAG,CAAE;AACnC,QAAK,MAAM,SAAS,QAAQ,QAAQ;AAClC,QAAI,CAAC,eAAe,MAAM,KAAK,CAAE;AACjC,SAAK,gBAAgB,MAAM,MAAM,KAAK;AACtC,QAAI,CAAC,KAAK,cAAc,MAAM,KAAK,CAAE,SAAQ;;;AAIjD,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,YAAY,QAAQ,cAAc,MAAM,MAAM,SAAS,EACzD,aAAY;AAEd,SAAO;;CAGT,SAAS,SAAe;AACtB,MAAI,YAAY,QAAQ,EACtB,aAAY;;CAIhB,SAAS,SAAS,OAAqB;AACrC,MAAI,CAAC,cAAc,MAAM,MAAO;AAChC,MAAI,SAAS,KAAK,QAAQ,cAAc,MAAM,MAAM,OAClD,aAAY,QAAQ;;CAKxB,SAAS,WAAoB;EAC3B,IAAI,WAAW;AACf,OAAK,MAAM,SAAS,QAAQ;AAC1B,OAAI,CAAC,eAAe,MAAM,KAAK,CAAE;AACjC,QAAK,gBAAgB,MAAM,MAAM,KAAK;AACtC,OAAI,CAAC,KAAK,cAAc,MAAM,KAAK,CACjC,YAAW;;AAGf,SAAO;;CAIT,eAAe,SAAwB;AACrC,MAAI,CAAC,UAAU,CAAE;EAGjB,IAAI,aAAa,EAAE;AACnB,OAAK,MAAM,SAAS,OAClB,KAAI,eAAe,MAAM,KAAK,CAC5B,YAAW,MAAM,QAAS,KAAK,KAAiC,MAAM;AAI1E,MAAI,cAAc,UAChB,cAAa,aAAa,UAAU,WAAgB;AAGtD,MAAI,cAAc,UAAU;AAC1B,QAAK,aAAa,QAAQ;AAC1B,OAAI;AACF,UAAM,aAAa,SAAS,WAAgB;aACpC;AACR,SAAK,aAAa,QAAQ;;;;CAMhC,SAAS,MAAM,QAA2B;AACxC,OAAK,MAAM,OAAO;AAClB,cAAY,QAAQ;;CAGtB,SAAS,aAAa,YAAwB,UAA6B;AACzE,gBAAc,QAAQ;EAEtB,MAAM,aAAa,cAAc,WAAW;AAC5C,eAAa,QAAQ,WAAW;AAChC,eAAa,UAAU,gBAAgB,WAAW,CAAC;AACnD,gBAAc,OAAO,WAAW,YAAY,aAAa,CAAC;AAC1D,OAAK,aAAa,mBAAsB,YAAY,SAAS,CAAM;AACnE,cAAY,QAAQ;;AAItB,KAAI,cAAc,eAAe;EAC/B,MAAM,WAAW,aAAa;AAC9B,eACS,EAAE,GAAI,KAAK,MAAkC,IACnD,SAAS,YAAY;AACpB,OAAI,CAAC,QAAS;AACd,QAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,QAAQ,SAAS,QAAQ,KAC3B,UAAS,KAAgB,QAAQ,MAAM,QAAa;KAI1D,EAAE,MAAM,MAAM,CACf;;AAGH,QAAO;EACC;EACC;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;AC/RH,IAAM,kBAAmC;CACvC,cAAc;CACd,OAAO;CACR;AAED,IAAM,kBAAkB,IAAqB,EAAE,GAAG,iBAAiB,CAAC;AAKpE,IAAI,kCAA+B,IAAI,KAAK;AAC5C,IAAI,iCAA8B,IAAI,KAAK;AAC3C,IAAI,iBAAiB;AACrB,IAAI,cAAc;AAClB,IAAI,gBAAgB;AACpB,IAAI,iBAAiB;AACrB,IAAI,iBAAyD;AAC7D,IAAM,kCAA4C,IAAI,KAAK;AAC3D,IAAM,0CAAuC,IAAI,KAAK;;;;AAKtD,SAAS,iBAAiB,KAA4B;AACpD,KAAI;AAEF,SADe,IAAI,IAAI,IAAI,CACb;SACR;AACN,SAAO;;;AAIX,SAAS,wBAAwB,SAA4C;CAC3E,MAAM,6BAAa,IAAI,KAAa;AACpC,KAAI,CAAC,QACH,QAAO;AAGT,MAAK,MAAM,UAAU,QACnB,YAAW,IAAI,iBAAiB,OAAO,IAAI,OAAO;AAEpD,QAAO;;AAGT,SAAS,wBAA8B;AACrC,kBAAiB,IAAI,IAAI,gBAAgB;AACzC,MAAK,MAAM,WAAW,gBAAgB,QAAQ,CAC5C,MAAK,MAAM,UAAU,QACnB,gBAAe,IAAI,OAAO;AAG9B,kBAAiB,wBAAwB,OAAO;;AAGlD,SAAS,4BAAkC;AACzC,mCAAkB,IAAI,KAAK;AAC3B,kCAAiB,IAAI,KAAK;AAC1B,kBAAiB;AACjB,eAAc;AACd,iBAAgB;AAChB,kBAAiB;AACjB,iBAAgB,OAAO;AACvB,yBAAwB,OAAO;AAC/B,iBAAgB,QAAQ,EAAE,GAAG,iBAAiB;;;;;AAMhD,SAAS,gBAAgB,QAAyB;AAEhD,KAAI,gBAAgB;AAClB,UAAQ,KAAK,8EAA8E;AAC3F,SAAO;;AAIT,KAAI,WAAW,OAAO,SAAS,OAC7B,QAAO;AAIT,QAAO,eAAe,IAAI,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BnC,SAAgB,mBAAmB,UAAkC,EAAE,EAAE;CACvE,MAAM,aAAa,EAAE;CACrB,MAAM,kBAAkB,wBAAwB,QAAQ,eAAe;CACvE,MAAM,yBAAyB,QAAQ,mBAAmB;CAE1D,SAAS,iBAAuB;EAC9B,MAAM,kCAAkB,IAAI,KAAa;EAGzC,MAAM,eAAgB,OAA8D;AAEpF,MAAI,cAAc;AAChB,mBAAgB,QAAQ;IACtB,GAAG;IACH,cAAc;IACf;AAGD,OAAI,aAAa,eACf,iBAAgB,IAAI,aAAa,eAAe;YACvC,aAAa,gBAAgB;IACtC,MAAM,SAAS,iBAAiB,aAAa,eAAe;AAC5D,QAAI,OACF,iBAAgB,IAAI,OAAO;;SAG1B;GAEL,MAAM,YAAY,IAAI,gBAAgB,OAAO,SAAS,OAAO;GAC7D,MAAM,iBAAiB,UAAU,IAAI,cAAc;GAGnD,MAAM,iBAAiB,UAAU,IAAI,cAAc;AACnD,OAAI,gBAAgB;IAClB,MAAM,SAAS,iBAAiB,eAAe;AAC/C,QAAI,OACF,iBAAgB,IAAI,OAAO;;AAI/B,mBAAgB,QAAQ;IACtB,cAAc;IACd,cAAc;AACZ,SAAI;MACF,MAAM,IAAI,aAAa,QAAQ,gBAAgB;AAC/C,UAAI,EAAG,QAAQ,KAAK,MAAM,EAAE,CAAC,SAAyC;aAChE;AACR,YAAO;QACL;IACJ,gBAAgB,kBAAkB,KAAA;IACnC;;AAGH,oBAAkB;;CAGpB,SAAS,sBAAsB,OAA2B;AAExD,MAAI,MAAM,WAAW,OAAO,OAAQ;AAGpC,MAAI,CAAC,gBAAgB,MAAM,OAAO,EAAE;AAClC,WAAQ,KAAK,0DAA0D,MAAM,SAAS;AACtF;;AAGF,MAAI;GACF,MAAM,gBAAgB,MAAM;AAC5B,OAAI,CAAC,cAAc,MAAM,WAAW,QAAQ,CAAE;AAE9C,WAAQ,cAAc,MAAtB;IACE,KAAK;AACH,qBAAgB,MAAM,QAAQ,cAAc;AAC5C;IACF,KAAK;AACH,qBAAgB,MAAM,OAAO,cAAc;AAC3C;;UAEE;;;;;;CASV,SAAS,eAAe,OAA4B;AAClD,MAAI,CAAC,gBAAgB,MAAM,gBAAgB,OAAO,WAAW,OAC3D;EAIF,IAAI;AAEJ,MAAI,gBAAgB,MAAM,eAExB,gBAAe,gBAAgB,MAAM;WAC5B,eAAe,OAAO,EAE/B,gBAAe,eAAe,QAAQ,CAAC,MAAM,CAAC;WACrC,gBAAgB;AAEzB,kBAAe;AACf,WAAQ,KAAK,6EAA6E;SACrF;AAEL,WAAQ,KAAK,oEAAoE;AACjF;;AAGF,SAAO,OAAO,YAAY,OAAO,aAAa;;;;;CAMhD,SAAS,SAAS,MAAoB;AACpC,iBAAe;GACb,MAAM;GACN,SAAS;GACV,CAAC;;;;;CAMJ,SAAS,OAAO,SAAiB,OAAiD,QAAc;AAC9F,iBAAe;GACb,MAAM;GACN,SAAS;IAAE;IAAS;IAAM;GAC3B,CAAC;;AAGJ,iBAAgB;AACd,kBAAgB,IAAI,YAAY,gBAAgB;AAChD,MAAI,uBACF,yBAAwB,IAAI,WAAW;AAGzC,MAAI,CAAC,aAAa;AAChB,mBAAgB;AAChB,oBAAiB;AACjB,UAAO,iBAAiB,WAAW,sBAAsB;AACzD,iBAAc;;AAGhB;AACA,yBAAuB;GACvB;AAEF,mBAAkB;AAChB,kBAAgB,OAAO,WAAW;AAClC,0BAAwB,OAAO,WAAW;AAE1C,kBAAgB,KAAK,IAAI,GAAG,gBAAgB,EAAE;AAC9C,MAAI,kBAAkB,GAAG;AACvB,OAAI,eACF,QAAO,oBAAoB,WAAW,eAAe;AAEvD,8BAA2B;AAC3B;;AAGF,yBAAuB;GACvB;AAQF,QAAO;EACL,SAAS;EACT,cARmB,eAAe,gBAAgB,MAAM,aAAa;EASrE,QARa,eAAe,gBAAgB,MAAM,OAAO;EASzD,MARW,eAAe,gBAAgB,MAAM,KAAK;EASrD,OARY,eAAe,gBAAgB,MAAM,MAAM;EASvD,UARe,eAAe,gBAAgB,MAAM,SAAS;EAS7D;EACA;EACA;EACD;;;;AClSH,SAAgB,qBAAqB,SAAyB;AAC5D,KAAI;AACF,SAAO,IAAI,KAAK,QAAQ,CAAC,mBAAmB,KAAA,GAAW;GACrD,MAAM;GACN,OAAO;GACP,KAAK;GACN,CAAC;SACI;AACN,SAAO;;;AAIX,SAAgB,gBAAgB,QAA4B;CAC1D,MAAM,sBAAM,IAAI,MAAM;CACtB,MAAM,OAAO,WAAW,gBAAgB,IAAI,WAAW,iBAAiB,KAAK;AAE7E,yBADU,IAAI,KAAK,IAAI,SAAS,GAAG,OAAO,MAAW,EAC5C,aAAa;;AAGxB,IAAa,4BAAoD;CAC/D;EAAE,OAAO;EAAI,OAAO;EAAgB;CACpC;EAAE,OAAO;EAAW,OAAO;EAAW;CACtC;EAAE,OAAO;EAAW,OAAO;EAAW;CACtC;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAa,OAAO;EAAa;CAC3C;AAED,IAAa,gCAAuE;CAClF,SAAS;CACT,SAAS;CACT,WAAW;CACX,WAAW;CACZ;AAED,IAAa,2BAA6D;CACxE,SAAS;CACT,SAAS;CACT,WAAW;CACX,WAAW;CACZ;AAED,SAAgB,uBAAuB,QAAmD;AACxF,KAAI,CAAC,OAAQ,QAAO;AACpB,KAAI,UAAU,yBACZ,QAAO,yBAAyB;CAElC,MAAM,QAAQ,OAAO,OAAO,CAAC,QAAQ,UAAU,IAAI,CAAC,MAAM;AAC1D,QAAO,QAAQ,MAAM,QAAQ,QAAO,MAAK,EAAE,aAAa,CAAC,GAAG;;AAG9D,SAAgB,2BAA2B,QAAwD;AACjG,KAAI,UAAU,UAAU,8BACtB,QAAO,8BAA8B;AAEvC,QAAO;;AAGT,SAAgB,sBAAsB,YAAsD;AAC1F,KAAI,WAAW,gBAAiB,QAAO,WAAW;AAClD,QAAO,WAAW,MAAM,OAAO,OAAO,WAAW,OAAO,KAAA;;AAG1D,IAAa,sBAA8C;CACzD;EAAE,OAAO;EAAI,OAAO;EAAY;CAChC;EAAE,OAAO;EAAe,OAAO;EAAe;CAC9C;EAAE,OAAO;EAAgB,OAAO;EAAgB;CAChD;EAAE,OAAO;EAAgB,OAAO;EAAgB;CACjD;AAED,IAAa,eAAuC;CAClD;EAAE,OAAO;EAAmB,OAAO;EAAgB;CACnD;EAAE,OAAO;EAAkB,OAAO;EAAgB;CAClD;EAAE,OAAO;EAAmB,OAAO;EAAoB;CACvD;EAAE,OAAO;EAAY,OAAO;EAAiB;CAC7C;EAAE,OAAO;EAAa,OAAO;EAAiB;CAC/C;;;AC9ED,IAAI,oBAA0C;AAC9C,IAAI,sBAAsB;AAgB1B,SAAS,YAAY,SAAiB,MAAsB;AAC1D,KAAI,CAAC,KAAM,QAAO;AAClB,KAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,CAAE,QAAO,GAAG,QAAQ,QAAQ,QAAQ,GAAG,GAAG;CAC1F,MAAM,iBAAiB,QAAQ,QAAQ,QAAQ,GAAG;CAClD,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,IAAI;AAChD,QAAO,GAAG,iBAAiB,eAAe,WAAW,IAAI,GAAG,iBAAiB,IAAI;;AAGnF,SAAS,YAAY,SAAyB;AAC5C,KAAI,CAAC,QAAS,QAAO;AAErB,KAAI;EACF,MAAM,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS;AACxE,SAAO,IAAI,IAAI,SAAS,OAAO,CAAC,SAAS,QAAQ,QAAQ,GAAG,IAAI;SAC1D;AAEN,SADa,QAAQ,QAAQ,sBAAsB,GAAG,CAC1C,QAAQ,QAAQ,GAAG,IAAI;;;AAIvC,SAAS,oBAAoB,SAAiB,KAAqB;AACjE,KAAI,CAAC,OAAO,eAAe,KAAK,IAAI,CAAE,QAAO;CAE7C,MAAM,WAAW,YAAY,QAAQ;AACrC,KAAI,aAAa,IAAK,QAAO;CAE7B,MAAM,gBAAgB,IAAI,WAAW,IAAI,GAAG,MAAM,IAAI;AACtD,KACE,kBAAkB,YACf,cAAc,WAAW,GAAG,SAAS,GAAG,IACxC,cAAc,WAAW,GAAG,SAAS,GAAG,CAE3C,QAAO,cAAc,MAAM,SAAS,OAAO;AAE7C,KAAI,cAAc,WAAW,GAAG,SAAS,GAAG,CAC1C,QAAO,cAAc,MAAM,SAAS,OAAO,IAAI;AAEjD,QAAO;;AAGT,SAAS,iBAAiB,SAA+D;AACvF,QAAO,UAAW,UAA6B;;AAGjD,SAAS,uBAAuB,SAAiD;CAC/E,MAAM,MAAM,iBAAiB,QAAQ;AACrC,KAAI,CAAC,IAAK,QAAO;AACjB,KAAI,OAAO,IAAI,QAAQ,WAAY,QAAO,IAAI,IAAI,gBAAgB;AAClE,QAAO,OAAO,KAAK,IAAI,CAAC,MAAM,QAAQ,IAAI,aAAa,KAAK,gBAAgB;;AAG9E,SAAS,uBAAuB,SAAwC,OAAqB;CAC3F,MAAM,MAAM,iBAAiB,QAAQ;AACrC,KAAI,CAAC,IAAK;AACV,KAAI,OAAO,IAAI,QAAQ,YAAY;AACjC,MAAI,IAAI,iBAAiB,MAAM;AAC/B;;AAEF,KAAI,gBAAgB;;AAGtB,SAAS,0BAA0B,SAA8C;CAC/E,MAAM,MAAM,iBAAiB,QAAQ;AACrC,KAAI,CAAC,IAAK;AACV,KAAI,OAAO,IAAI,WAAW,YAAY;AACpC,MAAI,OAAO,gBAAgB;AAC3B;;AAEF,MAAK,MAAM,OAAO,OAAO,KAAK,IAAI,CAChC,KAAI,IAAI,aAAa,KAAK,gBAAiB,QAAO,IAAI;;AAI1D,SAAS,eAA8B;AACrC,KAAI,CAAC,kBACH,qBAAoB,MAAM,OAAO,EAC/B,SAAS,EACP,gBAAgB,oBACjB,EACF,CAAC;AAEJ,QAAO;;;AAuBT,SAAgB,OAAO,UAA4B,EAAE,EAAgB;CACnE,MAAM,gBAAgB,kBAAkB;CACxC,MAAM,YAAY,cAAc;CAChC,MAAM,YAAY,cAAc;AAGhC,KAAI,CAAC,UAAU,cACb,WAAU,YAAY;AAIxB,KAAI,CAAC,qBAAqB;AACxB,YAAU,aAAa,QAAQ,KAAK,WAAW;GAC7C,MAAM,UAAU;AAChB,OAAI,QAAQ,eAAe;AACzB,WAAO,QAAQ;AACf,8BAA0B,QAAQ,QAAQ;AAC1C,WAAO;;GAGT,MAAM,mBAAmB,cAAc;AACvC,OAAI,iBAAiB,SAAS,OAAO,WAAW,CAAC,uBAAuB,OAAO,QAAQ,CACrF,wBAAuB,OAAO,SAAS,UAAU,iBAAiB,QAAQ;AAE5E,UAAO;IACP;AACF,wBAAsB;;CAGxB,SAAS,aAAqB;AAC5B,SAAO,QAAQ,WAAW,cAAc,eAAe;;CAGzD,SAAS,aAAa,KAAqB;AACzC,SAAO,oBAAoB,YAAY,EAAE,IAAI;;CAI/C,SAAS,cAAc,QAAqD;EAC1E,MAAM,OAA+B;GACnC,SAAS,YAAY;GACrB,SAAS,QAAQ,WAAW,cAAc;GAC1C,GAAG;GACJ;AAED,MAAI,QAAQ,aAAa,OAAO;AAC9B,QAAK,gBAAgB;AACrB,6BAA0B,KAAK,QAAQ;;AAEzC,SAAO;;CAIT,eAAe,IAAO,KAAa,QAAyC;AAE1E,UADiB,MAAM,UAAU,IAAO,aAAa,IAAI,EAAE,cAAc,OAAO,CAAC,EACjE;;CAGlB,eAAe,KAAQ,KAAa,MAAgB,QAAyC;AAE3F,UADiB,MAAM,UAAU,KAAQ,aAAa,IAAI,EAAE,MAAM,cAAc,OAAO,CAAC,EACxE;;CAGlB,eAAe,IAAO,KAAa,MAAgB,QAAyC;AAE1F,UADiB,MAAM,UAAU,IAAO,aAAa,IAAI,EAAE,MAAM,cAAc,OAAO,CAAC,EACvE;;CAGlB,eAAe,MAAS,KAAa,MAAgB,QAAyC;AAE5F,UADiB,MAAM,UAAU,MAAS,aAAa,IAAI,EAAE,MAAM,cAAc,OAAO,CAAC,EACzE;;CAGlB,eAAe,IAAO,KAAa,QAAyC;AAE1E,UADiB,MAAM,UAAU,OAAU,aAAa,IAAI,EAAE,cAAc,OAAO,CAAC,EACpE;;CAIlB,eAAe,OAAU,KAAa,MAAY,YAAY,QAAQ,gBAAsD;EAC1H,MAAM,WAAW,IAAI,UAAU;AAC/B,WAAS,OAAO,WAAW,KAAK;AAEhC,MAAI,eACF,QAAO,QAAQ,eAAe,CAAC,SAAS,CAAC,KAAK,WAAW;AACvD,OAAI,UAAU,KAAA,KAAa,UAAU,KACnC,UAAS,OAAO,KAAK,OAAO,UAAU,WAAW,KAAK,UAAU,MAAM,GAAG,OAAO,MAAM,CAAC;IAEzF;AAOJ,UAJiB,MAAM,UAAU,KAAQ,aAAa,IAAI,EAAE,UAAU,cAAc,EAElF,SAAS,EAAE,gBAAgB,KAAA,GAAW,EACvC,CAAC,CAAC,EACa;;CAIlB,eAAe,SAAS,KAAa,UAAoC;EACvE,MAAM,WAAW,MAAM,UAAU,IAAI,aAAa,IAAI,EAAE,cAAc,EAAE,cAAc,QAAQ,CAAC,CAAC;EAChG,MAAM,OAAO,IAAI,KAAK,CAAC,SAAS,KAAK,CAAC;EACtC,MAAM,UAAU,IAAI,gBAAgB,KAAK;AAGzC,MAAI,UAAU;GACZ,MAAM,OAAO,SAAS,cAAc,IAAI;AACxC,QAAK,OAAO;AACZ,QAAK,WAAW;AAChB,YAAS,KAAK,YAAY,KAAK;AAC/B,QAAK,OAAO;AACZ,YAAS,KAAK,YAAY,KAAK;AAG/B,oBAAiB,IAAI,gBAAgB,QAAQ,EAAE,IAAI;AACnD,UAAO;;AAIT,SAAO;;CAIT,SAAS,SAAS,MAAsB;EACtC,MAAM,UAAU,YAAY;AAC5B,SAAO,YAAY,SAAS,oBAAoB,SAAS,KAAK,CAAC;;CAIjE,SAAS,WAAW,MAAsB;AACxC,SAAO,YAAY,cAAc,cAAc,EAAE,KAAK;;AAGxD,QAAO;EACL,QAAQ;EACR;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACD;;;;;AC1OH,SAAgB,kBACd,QACA,UACA,UAAoC,EAAE,EACb;CACzB,MAAM,EAAE,QAAQ,KAAK,GAAG,iBAAiB;CACzC,MAAM,YAAY,IAAI,MAAM;CAE5B,IAAI,QAA8C;CAClD,IAAI,iBAAiB;CACrB,IAAI;CACJ,IAAI;CACJ,IAAI,kBAAgD;CACpD,IAAI,UAAU;CAEd,SAAS,aAAmB;AAC1B,MAAI,OAAO;AACT,gBAAa,MAAM;AACnB,WAAQ;;AAEV,YAAU,QAAQ;;CAGpB,SAAS,qBAA2B;AAClC,MAAI,CAAC,gBAAiB;AACtB,mBAAiB;AACjB,oBAAkB;;CAGpB,SAAS,SAAe;AACtB,cAAY;AACZ,mBAAiB;AACjB,mBAAiB,KAAA;;CAGnB,SAAS,QAAc;AACrB,MAAI,CAAC,eAAgB;EACrB,MAAM,QAAQ;EACd,MAAM,WAAW;AACjB,UAAQ;AACR,sBAAoB;AACf,WAAS,OAAO,WAAW,YAAY;AAC1C,qBAAkB;IAClB;;CAGJ,SAAS,SAAS,OAAU,UAA+B;AACzD,UAAQ;AACR,mBAAiB;AACjB,gBAAc;AACd,mBAAiB;AACjB,YAAU,QAAQ;AAClB,UAAQ,WAAW,OAAO,MAAM;;CAGlC,MAAM,YAAY,MAChB,SACC,OAAO,UAAU,cAAc;AAC9B,WAAS,OAAY,SAA0B;AAC/C,kBAAgB;AACd,WAAQ;AACR,uBAAoB;IACpB;IAEJ,aACD;CAED,SAAS,OAAa;AACpB,MAAI,QAAS;AACb,YAAU;AACV,aAAW;AACX,UAAQ;AACR,sBAAoB;;AAGtB,gBAAe,KAAK;AAEpB,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;AChFH,SAAgB,oBACd,sBAAsB,mBACK;CAC3B,MAAM,UAAU,IAAI,MAAM;CAC1B,MAAM,QAAQ,IAAmB,KAAK;CACtC,MAAM,eAAe,IAAiB,KAAK;CAC3C,MAAM,cAAc,IAAiB,KAAK;CAC1C,MAAM,YAAY,IAAiB,KAAK;CAExC,SAAS,aAAmB;AAC1B,QAAM,QAAQ;;CAGhB,SAAS,iBAAiB,OAAgB,WAAW,qBAA6B;AAChF,MAAI,iBAAiB,SAAS,MAAM,QAClC,QAAO,MAAM;AAEf,MAAI,OAAO,UAAU,YAAY,MAAM,MAAM,CAC3C,QAAO;AAET,MACE,OAAO,UAAU,YACd,UAAU,QACV,aAAa,SACb,OAAQ,MAAgC,YAAY,YACnD,MAA8B,QAElC,QAAQ,MAA8B;AAExC,SAAO;;CAGT,SAAS,SAAS,OAAgB,UAA2B;EAC3D,MAAM,UAAU,iBAAiB,OAAO,SAAS;AACjD,QAAM,QAAQ;AACd,SAAO;;CAGT,SAAS,WAAW,uBAAO,IAAI,MAAM,EAAQ;AAC3C,eAAa,QAAQ;;CAGvB,SAAS,UAAU,uBAAO,IAAI,MAAM,EAAQ;AAC1C,cAAY,QAAQ;;CAGtB,SAAS,QAAQ,uBAAO,IAAI,MAAM,EAAQ;AACxC,YAAU,QAAQ;;CAGpB,eAAe,IACb,WACA,UAAiC,EAAE,EACvB;AACZ,UAAQ,QAAQ;AAChB,cAAY;AACZ,MAAI;GACF,MAAM,WAAW,MAAM,WAAW;AAClC,OAAI,QAAQ,YAAY,OACtB,aAAY;YACH,QAAQ,YAAY,OAC7B,YAAW;YACF,QAAQ,YAAY,MAC7B,UAAS;AAEX,UAAO;WACA,KAAK;AACZ,YAAS,KAAK,QAAQ,aAAa;AACnC,SAAM;YACE;AACR,WAAQ,QAAQ;;;AAIpB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;AC7GH,SAAS,qBAAkD;AACzD,KAAI,OAAO,WAAW,YAAa,QAAO,KAAA;AAC1C,QAAQ,OAA8D;;AAGxE,SAAS,oBAAwC;AAC/C,QAAO,oBAAoB,EAAE;;;AAwD/B,SAAgB,sBACd,UAAwC,EAAE,EACb;CAC7B,MAAM,EAAE,QAAQ,KAAK,YAAY,OAAO,gBAAgB,eAAe;CACvE,MAAM,eAAe,cAAc,mBAAmB;CACtD,MAAM,MAAM,QAAQ;CAEpB,MAAM,cAAc,IAAyB,EAAE,CAAC;CAChD,MAAM,QAAQ,IAAI,EAAE;CACpB,MAAM,qBAAqB,IAA8B,KAAK;CAC9D,MAAM,UAAU,oBAAoB,8BAA8B;CAClE,MAAM,YAAY,QAAQ;CAC1B,MAAM,QAAQ,QAAQ;CACtB,MAAM,eAAe,QAAQ;CAC7B,MAAM,OAAO,IAAI,EAAE;CAGnB,MAAM,UAAU,IAAY,kBAAkB;CAG9C,MAAM,kBAAkB,IAA4B,EAAE,CAAC;CACvD,MAAM,WAAW,IAA4B,EAAE,CAAC;CAChD,IAAI,uBAAuB;CAE3B,MAAM,UAAU,eAAe,YAAY,MAAM,SAAS,MAAM,MAAM;CAEtE,MAAM,UAA6B,SAAS;EAC1C,QAAQ,KAAA;EACR,QAAQ,KAAA;EACR,SAAS,KAAA;EACT,gBAAgB,KAAA;EAChB,YAAY,KAAA;EACb,CAAC;CAEF,SAAS,eAA2E;EAClF,MAAM,CAAC,OAAO,SAAS,QAAQ,MAAM,MAAM,IAAI;AAC/C,SAAO;GACL,QAAS,SAAS;GAClB,WAAY,SAAS;GACtB;;CAGH,eAAe,mBAAkC;AAC/C,MAAI;AACF,SAAM,QAAQ,IAAI,YAAY;IAC5B,MAAM,SAAS,IAAI,iBAAiB;IAEpC,MAAM,eAAe,oBAAoB,EAAE;IAC3C,MAAM,gBAAgB,mBAChB,cAAc,WAAW,IAAI,aAAa,KAAK,KAAA,MAChD,QAAQ,kBACR,KAAA;AACL,QAAI,cAAe,QAAO,IAAI,mBAAmB,cAAc;AAC/D,QAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,OAAO;AACxD,QAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,OAAO;AACxD,QAAI,QAAQ,QAAS,QAAO,IAAI,WAAW,QAAQ,QAAQ;IAG3D,MAAM,EAAE,QAAQ,cAAc,cAAc;AAC5C,WAAO,IAAI,WAAW,OAAO;AAC7B,WAAO,IAAI,cAAc,UAAU;AAGnC,QAAI,QAAQ,WACV,QAAO,IAAI,iBAAiB,gBAAgB,QAAQ,WAAW,CAAC;AAGlE,WAAO,IAAI,SAAS,OAAO,MAAM,CAAC;AAClC,WAAO,IAAI,QAAQ,OAAO,KAAK,QAAQ,MAAM,CAAC;IAE9C,MAAM,QAAQ,OAAO,UAAU;IAE/B,MAAM,MAAM,GADC,gBAAgB,GACT,cAAc,QAAQ,IAAI,UAAU;IACxD,MAAM,OAAO,MAAM,IAAI,IAA4B,IAAI;IAGvD,IAAI,WAAW,KAAK;AACpB,QAAI,CAAC,iBAAiB,gBAAgB,aAAa,SAAS,GAAG;KAC7D,MAAM,UAAU,IAAI,IAAI,aAAa;AACrC,gBAAW,SAAS,QAAO,MAAK,QAAQ,IAAI,EAAE,gBAAgB,CAAC;;AAGjE,QAAI,KAAK,UAAU,EACjB,aAAY,QAAQ;QAEpB,aAAY,QAAQ,CAAC,GAAG,YAAY,OAAO,GAAG,SAAS;AAIzD,QAAI,CAAC,iBAAiB,gBAAgB,aAAa,SAAS,EAC1D,KAAI,KAAK,YAAY,SAAS,MAE5B,OAAM,QAAQ,YAAY,MAAM;QAGhC,OAAM,QAAQ,YAAY,MAAM,SAAS;QAG3C,OAAM,QAAQ,KAAK;MAEpB;IAAE,SAAS;IAAQ,cAAc;IAA+B,CAAC;UAC9D;AACN,OAAI,KAAK,UAAU,GAAG;AACpB,gBAAY,QAAQ,EAAE;AACtB,UAAM,QAAQ;;;;CAKpB,eAAe,qBAAoC;AACjD,MAAI,qBAAsB;AAC1B,yBAAuB;EAEvB,MAAM,OAAO,gBAAgB;EAC7B,MAAM,CAAC,UAAU,eAAe,MAAM,QAAQ,WAAW,CACvD,IAAI,IAA6D,GAAG,KAAK,+BAA+B,EACxG,IAAI,IAAsE,GAAG,KAAK,WAAW,CAC9F,CAAC;AAEF,MAAI,SAAS,WAAW,eAAe,MAAM,QAAQ,SAAS,MAAM,CAClE,iBAAgB,QAAQ,SAAS,MAAM,KAAI,OAAM;GAC/C,OAAO,EAAE;GACT,OAAO,EAAE;GACT,OAAO,EAAE;GACV,EAAE;AAGL,MAAI,YAAY,WAAW,eAAe,YAAY,OAAO,YAAY,MAAM,QAAQ,YAAY,MAAM,SAAS,CAChH,UAAS,QAAQ,YAAY,MAAM,SAAS,KAAI,OAAM;GACpD,OAAO,EAAE;GACT,OAAO,EAAE;GACV,EAAE;;CAIP,eAAe,WAA0B;AACvC,MAAI,CAAC,QAAQ,SAAS,UAAU,MAAO;AACvC,OAAK;AACL,QAAM,kBAAkB;;CAG1B,SAAS,QAAc;AACrB,OAAK,QAAQ;AACb,cAAY,QAAQ,EAAE;AACtB,QAAM,QAAQ;AACd,oBAAkB;;CAGpB,SAAS,OAAO,YAAqC;AACnD,qBAAmB,QAAQ;;CAG7B,SAAS,QAAc;AACrB,qBAAmB,QAAQ;AAC3B,UAAQ,SAAS,KAAA;AACjB,UAAQ,SAAS,KAAA;AACjB,UAAQ,UAAU,KAAA;AAClB,UAAQ,iBAAiB,KAAA;AACzB,UAAQ,aAAa,KAAA;AACrB,UAAQ,QAAQ;AAChB,OAAK,QAAQ;AACb,UAAQ,YAAY;;CAItB,MAAM,mBAAmB,eAAgD;EACvE,MAAM,yBAAS,IAAI,KAAkC;AACrD,OAAK,MAAM,OAAO,YAAY,OAAO;GACnC,MAAM,MAAM,IAAI,gBAAgB,IAAI,WAAW;GAC/C,MAAM,OAAO,OAAO,IAAI,IAAI;AAC5B,OAAI,KACF,MAAK,KAAK,IAAI;OAEd,QAAO,IAAI,KAAK,CAAC,IAAI,CAAC;;AAI1B,SAAO,CAAC,GAAG,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO;AAC9C,OAAI,MAAM,aAAc,QAAO;AAC/B,OAAI,MAAM,aAAc,QAAO;AAC/B,UAAO,EAAE,cAAc,EAAE;IACzB;GACF;CAEF,MAAM,cAAc,wBACZ,QAAQ,cACR;AACJ,OAAK,QAAQ;AACb,oBAAkB;IAEpB,EAAE,OAAO,KAAK,CACf;AAGD,aACQ;EAAC,QAAQ;EAAQ,QAAQ;EAAS,QAAQ;EAAgB,QAAQ;EAAY,QAAQ;EAAM,QAC5F;AACJ,cAAY,QAAQ;AACpB,OAAK,QAAQ;AACb,oBAAkB;GAErB;AAED,KAAI,UACF,mBAAkB;AAGpB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;EACA;EACA;EACA;EACD;;;;ACvNH,IAAa,qBAAuD,OAAO,iBAAiB;;AAG5F,SAAgB,iBAAiB,UAAmC,EAAE,EAA0B;CAC9F,MAAM,iBAAiB,KAAyB;CAChD,MAAM,iBAAiB,KAAyB;CAChD,MAAM,mBAAmB,KAAmC;CAC5D,MAAM,eAAe,IAAmB,KAAK;CAC7C,MAAM,YAAY,IAAI,MAAM;CAC5B,MAAM,cAAc,IAAI,MAAM;CAC9B,MAAM,qBAAqB,KAAyB;CAEpD,IAAI,eAAqD;CAEzD,MAAM,WAAW,IAAI,CAAC,CAAC,QAAQ,OAAO;CACtC,MAAM,aAAa,eAAe,aAAa,UAAU,KAAK;CAC9D,MAAM,eAAe,eAAe,QAAQ,QAAQ,aAAa,IAAI,MAAM;CAC3E,MAAM,sBAAsB,eAAe,QAAQ,QAAQ,oBAAoB,CAAC;CAChF,MAAM,UAAU,gBAA6C;EAC3D,gBAAgB,eAAe;EAC/B,gBAAgB,eAAe;EAC/B,kBAAkB,iBAAiB;EACnC,UAAU,SAAS;EACnB,YAAY,WAAW;EACvB,cAAc,aAAa;EAC3B,qBAAqB,oBAAoB;EACzC,aAAa,YAAY;EACzB,oBAAoB,mBAAmB;EACxC,EAAE;CACH,MAAM,gBAAgB,gBAAmD;EACvE,YAAY,UAAU;EACtB,qBAAqB,aAAa;EACnC,EAAE;CAEH,SAAS,IAAI,YAAiC;AAC5C,eAAa,QAAQ,WAAW;AAChC,iBAAe,QAAQ,WAAW;AAClC,iBAAe,QAAQ,sBAAsB,WAAW;AACxD,mBAAiB,QAAQ,WAAW;;CAGtC,SAAS,QAAQ;AACf,eAAa,QAAQ;AACrB,iBAAe,QAAQ,KAAA;AACvB,iBAAe,QAAQ,KAAA;AACvB,mBAAiB,QAAQ,KAAA;;AAG3B,KAAI,QAAQ,eAAe,KAAA,EACzB,aACQ,QAAQ,QAAQ,WAAW,GAChC,eAAe;AACd,MAAI,WACF,KAAI,WAAW;MAEf,QAAO;IAGX,EAAE,WAAW,MAAM,CACpB;CAGH,SAAS,YAAY;AACnB,YAAU,QAAQ;;CAGpB,SAAS,aAAa;AACpB,YAAU,QAAQ;;CAGpB,SAAS,aAAa,YAA+B;AACnD,MAAI,WAAW;AACf,YAAU,QAAQ;AAClB,UAAQ,WAAW,WAAW;;CAGhC,eAAe,aAAa;AAC1B,MAAI,CAAC,QAAQ,UAAU,YAAY,MAAO;AAC1C,cAAY,QAAQ;AACpB,MAAI;GACF,MAAM,UAAU,MAAM,QAAQ,QAAQ;AACtC,OAAI,SAAS;AACX,uBAAmB,QAAQ;AAC3B,QAAI,aAAc,cAAa,aAAa;AAC5C,mBAAe,iBAAiB;AAC9B,wBAAmB,QAAQ,KAAA;AAC3B,oBAAe;OACd,IAAK;;YAEF;AACR,eAAY,QAAQ;;;CAIxB,SAAS,eAAe;AACtB,SAAO;AACP,UAAQ,YAAY;;AAGtB,sBAAqB;AACnB,MAAI,aAAc,cAAa,aAAa;GAC5C;AAuBF,SAAQ,oBArB0B;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAEiC;AAElC,QAAO;EACL;EACA;EACA,gBAAgB,SAAS,eAAe;EACxC,gBAAgB,SAAS,eAAe;EACxC,kBAAkB,SAAS,iBAAiB;EAC5C,cAAc,SAAS,aAAa;EACpC;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;AC7IH,IAAM,eAA6B;CAAC;CAAM;CAAM;CAAI;AAEpD,IAAM,iBAA6C;CACjD,MAAM;CACN,MAAM;CACN,KAAK;CACN;;AAGD,SAAgB,oBAA6C;CAC3D,MAAM,EAAE,YAAY,uBAAuB;CAE3C,SAAS,cAAc,OAAe,MAAkB,IAAwB;AAC9E,MAAI,SAAS,GAAI,QAAO;AAExB,SADkB,QAAQ,eAAe,QACtB,eAAe;;CAGpC,SAAS,aAAa,QAAqB,YAAoB,GAAW;EACxE,MAAM,EAAE,OAAO,SAAS;AACxB,MAAI,UAAU,EAAG,QAAO,KAAK;EAE7B,IAAI;AACJ,MAAI,KAAK,IAAI,MAAM,IAAI,IACrB,kBAAiB,MAAM,cAAc,YAAY,EAAE;WAC1C,KAAK,IAAI,MAAM,GAAG,KAC3B,kBAAiB,MAAM,cAAc,YAAY,EAAE;MAEnD,kBAAiB,MAAM,YAAY,UAAU;AAI/C,mBAAiB,eAAe,QAAQ,UAAU,GAAG;AACrD,mBAAiB,eAAe,QAAQ,UAAU,IAAI;AAEtD,SAAO,GAAG,eAAe,GAAG;;CAG9B,SAAS,kBAAkB,QAAwC;EACjE,MAAM,EAAE,oBAAoB,oBAAoB,gBAAgB;AAGhE,MAAI,mBAAmB,SAAS,EAC9B,QAAO;GACL,aAAa;IAAE,OAAO;IAAG,MAAM;IAAM;GACrC,eAAe;IAAE,OAAO;IAAG,MAAM;IAAM;GACvC,gBAAgB;GAChB,OAAO;GACP,OAAO;GACR;AAGH,MAAI,mBAAmB,SAAS,EAC9B,QAAO;GACL,aAAa;IAAE,OAAO;IAAG,MAAM;IAAM;GACrC,eAAe;IAAE,OAAO;IAAG,MAAM;IAAM;GACvC,gBAAgB;GAChB,OAAO;GACP,OAAO;GACR;AAGH,MAAI,YAAY,SAAS,EACvB,QAAO;GACL,aAAa;IAAE,OAAO;IAAG,MAAM;IAAM;GACrC,eAAe;IAAE,OAAO;IAAG,MAAM;IAAM;GACvC,gBAAgB;GAChB,OAAO;GACP,OAAO;GACR;EAIH,MAAM,iBAAiB,QACrB,mBAAmB,OACnB,mBAAmB,MACnB,mBAAmB,KACpB;AAED,MAAI,mBAAmB,KACrB,QAAO;GACL,aAAa;IAAE,OAAO;IAAG,MAAM;IAAM;GACrC,eAAe;IAAE,OAAO;IAAG,MAAM;IAAM;GACvC,gBAAgB;GAChB,OAAO;GACP,OAAO;GACR;AAIH,MAAI,kBAAkB,mBAAmB,MACvC,QAAO;GACL,aAAa;IAAE,OAAO;IAAG,MAAM;IAAM;GACrC,eAAe;IAAE,OAAO;IAAG,MAAM;IAAM;GACvC,gBAAgB;GAChB,OAAO;GACP,OAAO;GACR;EAIH,MAAM,iBAAiB,mBAAmB,QAAQ;EAClD,MAAM,iBAAiB,cAAc,YAAY,OAAO,YAAY,MAAM,IAAI;EAC9E,MAAM,iBAAiB,iBAAiB;EACxC,MAAM,mBAAmB,iBAAiB;EAG1C,IAAI,aAAyB;AAC7B,MAAI,kBAAkB,KAAO,cAAa;AAC1C,MAAI,kBAAkB,EAAG,cAAa;AAEtC,SAAO;GACL,aAAa;IACX,OAAO,cAAc,gBAAgB,KAAK,WAAW;IACrD,MAAM;IACP;GACD,eAAe;IACb,OAAO,cAAc,kBAAkB,KAAK,WAAW;IACvD,MAAM;IACP;GACD;GACA,OAAO;GACR;;CAGH,SAAS,wBAAwB,QAAoD;EACnF,MAAM,EAAE,uBAAuB,gBAAgB,mBAAmB,kBAAkB;AAGpF,MAAI,sBAAsB,SAAS,EACjC,QAAO;GACL,OAAO,EAAE;GACT,kBAAkB;IAAE,OAAO;IAAG,MAAM;IAAM;GAC1C,OAAO;GACP,OAAO;GACR;AAGH,MAAI,kBAAkB,EACpB,QAAO;GACL,OAAO,EAAE;GACT,kBAAkB;IAAE,OAAO;IAAG,MAAM;IAAM;GAC1C,OAAO;GACP,OAAO;GACR;AAGH,MAAI,oBAAoB,EACtB,QAAO;GACL,OAAO,EAAE;GACT,kBAAkB;IAAE,OAAO;IAAG,MAAM;IAAM;GAC1C,OAAO;GACP,OAAO;GACR;AAGH,MAAI,cAAc,SAAS,EACzB,QAAO;GACL,OAAO,EAAE;GACT,kBAAkB;IAAE,OAAO;IAAG,MAAM;IAAM;GAC1C,OAAO;GACP,OAAO;GACR;EAGH,MAAM,QAA8B,EAAE;EACtC,IAAI,uBAAuB,sBAAsB;EACjD,MAAM,iBAAiB,cAAc,SAAS,iBAAiB;EAC/D,MAAM,gBAAgB,cAAc,QAAQ;AAE5C,OAAK,IAAI,IAAI,GAAG,IAAI,mBAAmB,KAAK;AAC1C,SAAM,KAAK;IACT,YAAY,IAAI;IAChB,eAAe;KACb,OAAO;KACP,MAAM,sBAAsB;KAC7B;IACD,gBAAgB;KACd,OAAO,MAAM,IAAI,cAAc,QAAQ;KACvC,MAAM,cAAc;KACrB;IACD,eAAe;KACb,OAAO,MAAM,IAAI,IAAI;KACrB,MAAM,cAAc;KACrB;IACF,CAAC;AACF,2BAAwB;;AAS1B,SAAO;GACL;GACA,kBAPoC;IACpC,OAAO,cAAc,QAAS,kBAAkB,oBAAoB;IACpE,MAAM,cAAc;IACrB;GAKC,OAAO;GACR;;CAGH,SAAS,mBACP,MACA,UACA,IACoB;EAEpB,MAAM,YAAY,QAAQ,MAAM,UAAU,MAAM,GAAG;AACnD,MAAI,cAAc,KAChB,QAAO;GAAE,OAAO;GAAW,MAAM;GAAM;AAMzC,SAAO;GAAE,OAFS,OAAO,oBAAoB,SAAS,GACxB,KACF;GAAK,MAAM;GAAM;;CAG/C,SAAS,mBACP,OACA,WACA,IACoB;EAEpB,MAAM,YAAY,QAAQ,OAAO,WAAW,SAAS,GAAG;AACxD,MAAI,cAAc,KAChB,QAAO;GAAE,OAAO;GAAW,MAAM;GAAS;AAM5C,SAAO;GAAE,OAFQ,QAAQ,kBAAkB,UAAU,GACzB,KAAK;GACP,MAAM;GAAS;;CAG3C,SAAS,oBAAoB,MAA8B;AAQzD,SAPgD;GAC9C,SAAS;GACT,SAAS;GACT,SAAS;GACT,SAAS;GACT,QAAQ;GACT,CACc;;CAGjB,SAAS,kBAAkB,MAA4B;AAQrD,SAP8C;GAC5C,MAAM;GACN,MAAM;GACN,MAAM;GACN,MAAM;GACN,KAAK;GACN,CACc;;CAGjB,SAAS,2BACP,QACA,SACqB;AACrB,MAAI,CAAC,OAAO,SAAS,OAAO,MAAM,WAAW,EAAG,QAAO,EAAE;AAEzD,SAAO,OAAO,MAAM,MAAM,GAAG,QAAQ,OAAO,CAAC,KAAK,MAAM,WAAW;GACjE,QAAQ,QAAQ;GAChB,eAAe,KAAK;GACpB,QAAQ,KAAK;GACd,EAAE;;AAGL,QAAO;EACL,aAAa;EACb;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;ACxWH,IAAM,kBAAkB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,IAAM,cAAc;AAsCpB,SAAS,iBACP,MACA,QACA,IACU;AACV,QAAO;EACL,IAAI,MAAM,SAAS,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;EACvE;EACA;EACA,OAAO,EAAE;EACV;;AAGH,SAAS,mBAA2B;AAClC,QAAO,UAAU,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;;AAGvE,SAAS,WAAc,OAAa;AAClC,QAAO,gBAAgB,MAAM,MAAM,CAAC;;;AAItC,SAAgB,mBACd,cACA,UAAqC,EAAE,EACb;CAC1B,MAAM,EAAE,aAAa,aAAa,gBAAgB,OAAO;CAEzD,MAAM,eAAe,iBAAiB,WAAW,cAAc;CAE/D,MAAM,gBAAgB,IAAyB;EAC7C,QAAQ,cAAc,UAAU,CAAC,aAAa;EAC9C,eAAe,cAAc,iBAAiB,aAAa;EAC3D,SAAS,cAAc,WAAW,EAAE;EACpC,eAAe,cAAc,iBAAiB,EAAE;EAChD,gBAAgB,cAAc;EAC/B,CAAC;CAEF,MAAM,UAAU,IAAoB,EAAE,CAAC;CACvC,MAAM,eAAe,IAAI,GAAG;CAE5B,MAAM,QAAQ,eAAe,cAAc,MAAM;CACjD,MAAM,SAAS,eAAe,cAAc,MAAM,OAAO;CACzD,MAAM,cAAc,eAClB,cAAc,MAAM,OAAO,MAAK,MAAK,EAAE,OAAO,cAAc,MAAM,cAAc,CACjF;CACD,MAAM,UAAU,eAAe,cAAc,MAAM,QAAQ;CAC3D,MAAM,gBAAgB,eAAe,cAAc,MAAM,cAAc;CACvE,MAAM,iBAAiB,eAAe,cAAc,MAAM,eAAe;CAEzE,MAAM,UAAU,eAAe,aAAa,SAAS,EAAE;CACvD,MAAM,UAAU,eAAe,aAAa,QAAQ,QAAQ,MAAM,SAAS,EAAE;CAE7E,SAAS,gBAAgB;EACvB,MAAM,QAAsB;GAC1B,QAAQ,WAAW,cAAc,MAAM,OAAO;GAC9C,SAAS,WAAW,cAAc,MAAM,QAAQ;GACjD;AAED,MAAI,aAAa,QAAQ,QAAQ,MAAM,SAAS,EAC9C,SAAQ,QAAQ,QAAQ,MAAM,MAAM,GAAG,aAAa,QAAQ,EAAE;AAGhE,UAAQ,MAAM,KAAK,MAAM;AAEzB,MAAI,QAAQ,MAAM,SAAS,WACzB,SAAQ,MAAM,OAAO;MAErB,cAAa;;CAIjB,SAAS,eAAe,SAAiB;AACvC,MAAI,cAAc,MAAM,OAAO,MAAK,MAAK,EAAE,OAAO,QAAQ,EAAE;AAC1D,iBAAc,MAAM,gBAAgB;AACpC,iBAAc,MAAM,gBAAgB,EAAE;;;CAI1C,SAAS,gBAAgB,UAA8B;AACrD,gBAAc,MAAM,iBAAiB;;CAGvC,SAAS,iBAAiB,SAAmB;AAC3C,gBAAc,MAAM,gBAAgB;;CAGtC,SAAS,SAAS,MAAe,QAAoC;AACnE,iBAAe;EACf,MAAM,cAAc,cAAc,MAAM,OAAO,SAAS;EACxD,MAAM,QAAQ,iBACZ,QAAQ,SAAS,eACjB,UAAU,cACX;AACD,gBAAc,MAAM,OAAO,KAAK,MAAM;AACtC,gBAAc,MAAM,gBAAgB,MAAM;AAC1C,gBAAc,MAAM,gBAAgB,EAAE;AACtC,SAAO;;CAGT,SAAS,YAAY,SAAiB;AACpC,MAAI,cAAc,MAAM,OAAO,UAAU,EAAG;AAE5C,iBAAe;EACf,MAAM,QAAQ,cAAc,MAAM,OAAO,WAAU,MAAK,EAAE,OAAO,QAAQ;AACzE,MAAI,UAAU,GAAI;AAElB,gBAAc,MAAM,OAAO,OAAO,OAAO,EAAE;AAC3C,MAAI,cAAc,MAAM,kBAAkB,SAAS;AACjD,iBAAc,MAAM,gBAAgB,cAAc,MAAM,OAAO,GAAG;AAClE,iBAAc,MAAM,gBAAgB,EAAE;;;CAI1C,SAAS,UAAU,MAAc,OAA4B;AAC3D,iBAAe;EACf,MAAM,SAAqB;GACzB,IAAI,kBAAkB;GACtB;GACA,OAAO,SAAS,gBAAgB,cAAc,MAAM,QAAQ,SAAS,gBAAgB;GACrF,OAAO;GACR;AACD,gBAAc,MAAM,QAAQ,KAAK,OAAO;AACxC,SAAO;;CAGT,SAAS,aAAa,UAAkB;AACtC,iBAAe;EACf,MAAM,QAAQ,cAAc,MAAM,QAAQ,WAAU,MAAK,EAAE,OAAO,SAAS;AAC3E,MAAI,UAAU,GAAI;AAElB,gBAAc,MAAM,QAAQ,OAAO,OAAO,EAAE;AAE5C,OAAK,MAAM,SAAS,cAAc,MAAM,OACtC,MAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,MAAM,CAC3C,KAAI,KAAK,eAAe,UAAU;AAChC,UAAO,KAAK;AACZ,QAAK,QAAQ;;AAKnB,MAAI,cAAc,MAAM,mBAAmB,SACzC,eAAc,MAAM,iBAAiB,KAAA;AAGvC,sBAAoB;;CAGtB,SAAS,aAAa,SAAmB,UAA8B;AACrE,MAAI,QAAQ,WAAW,EAAG;AAE1B,iBAAe;EACf,MAAM,QAAQ,YAAY;AAC1B,MAAI,CAAC,MAAO;AAEZ,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,OAAO,MAAM,MAAM,WAAW;IAClC,IAAI;IACJ,KAAK,OAAO,WAAW,EAAE,GAAG;IAC5B,KAAK,SAAS,OAAO,MAAM,EAAE,CAAC,GAAG;IACjC,OAAO;IACR;AAED,OAAI,UAAU;AACZ,SAAK,aAAa;AAClB,SAAK,QAAQ;UACR;AACL,WAAO,KAAK;AACZ,SAAK,QAAQ;;AAGf,SAAM,MAAM,UAAU;;AAExB,sBAAoB;;CAGtB,SAAS,WAAW,SAAmB;AACrC,eAAa,SAAS,KAAA,EAAU;;CAGlC,SAAS,qBAAqB;EAC5B,MAAM,SAAiC,EAAE;AAEzC,OAAK,MAAM,SAAS,cAAc,MAAM,OACtC,MAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,MAAM,CAC3C,KAAI,KAAK,WACP,QAAO,KAAK,eAAe,OAAO,KAAK,eAAe,KAAK;AAKjE,OAAK,MAAM,UAAU,cAAc,MAAM,QACvC,QAAO,QAAQ,OAAO,OAAO,OAAO;;CAIxC,SAAS,OAAO;AACd,MAAI,CAAC,QAAQ,MAAO;EAEpB,MAAM,QAAQ,QAAQ,MAAM,aAAa;AACzC,eAAa;AAEb,gBAAc,MAAM,SAAS,WAAW,MAAM,OAAO;AACrD,gBAAc,MAAM,UAAU,WAAW,MAAM,QAAQ;AAGvD,MAAI,CADsB,cAAc,MAAM,OAAO,MAAK,MAAK,EAAE,OAAO,cAAc,MAAM,cAAc,CAExG,eAAc,MAAM,gBAAgB,cAAc,MAAM,OAAO,IAAI,MAAM;AAE3E,gBAAc,MAAM,gBAAgB,EAAE;;CAGxC,SAAS,OAAO;AACd,MAAI,CAAC,QAAQ,MAAO;AAEpB,eAAa;EACb,MAAM,QAAQ,QAAQ,MAAM,aAAa;AAEzC,gBAAc,MAAM,SAAS,WAAW,MAAM,OAAO;AACrD,gBAAc,MAAM,UAAU,WAAW,MAAM,QAAQ;AACvD,gBAAc,MAAM,gBAAgB,EAAE;;CAGxC,SAAS,WAAW,QAAgC;AAClD,MAAI,WAAW,OACb,QAAO,KAAK,UAAU;GACpB,QAAQ,cAAc,MAAM;GAC5B,SAAS,cAAc,MAAM;GAC9B,EAAE,MAAM,EAAE;EAGb,MAAM,YAAY,IAAI,IAAI,cAAc,MAAM,QAAQ,KAAI,MAAK,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;EAC/E,MAAM,OAAO,CAAC,qCAAqC;AAEnD,OAAK,MAAM,SAAS,cAAc,MAAM,OACtC,MAAK,MAAM,CAAC,QAAQ,SAAS,OAAO,QAAQ,MAAM,MAAM,CACtD,KAAI,KAAK,YAAY;GACnB,MAAM,aAAa,UAAU,IAAI,KAAK,WAAW,IAAI;AACrD,QAAK,KAAK,IAAI,MAAM,KAAK,KAAK,OAAO,KAAK,KAAK,WAAW,KAAK,WAAW,GAAG;;AAKnF,SAAO,KAAK,KAAK,KAAK;;CAGxB,SAAS,WAAW,MAAc,QAAiC;AACjE,MAAI;AACF,kBAAe;AAEf,OAAI,WAAW,QAAQ;IACrB,MAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,QAAI,OAAO,UAAU,MAAM,QAAQ,OAAO,OAAO,EAAE;AACjD,mBAAc,MAAM,SAAS,OAAO;AACpC,mBAAc,MAAM,gBAAgB,OAAO,OAAO,IAAI,MAAM;;AAE9D,QAAI,OAAO,WAAW,MAAM,QAAQ,OAAO,QAAQ,CACjD,eAAc,MAAM,UAAU,OAAO;AAEvC,kBAAc,MAAM,gBAAgB,EAAE;AACtC,wBAAoB;AACpB,WAAO;;GAGT,MAAM,QAAQ,KAAK,MAAM,CAAC,MAAM,KAAK;AACrC,OAAI,MAAM,SAAS,EAAG,QAAO;GAE7B,MAAM,2BAAW,IAAI,KAAuB;GAC5C,MAAM,4BAAY,IAAI,KAAyB;AAE/C,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,QAAQ,MAAM,GAAG,MAAM,uBAAuB;AACpD,QAAI,CAAC,SAAS,MAAM,SAAS,EAAG;IAEhC,MAAM,YAAY,MAAM,GAAG,QAAQ,UAAU,GAAG;IAChD,MAAM,SAAS,MAAM,GAAG,QAAQ,UAAU,GAAG;IAC7C,MAAM,WAAW,MAAM,GAAG,QAAQ,UAAU,GAAG;IAC/C,MAAM,aAAa,MAAM,GAAG,QAAQ,UAAU,GAAG;AAEjD,QAAI,CAAC,SAAS,IAAI,UAAU,CAC1B,UAAS,IAAI,WAAW,iBAAiB,WAAW,cAAc,CAAC;AAGrE,QAAI,YAAY,CAAC,UAAU,IAAI,SAAS,CACtC,WAAU,IAAI,UAAU;KACtB,IAAI;KACJ,MAAM,cAAc;KACpB,OAAO,gBAAgB,UAAU,OAAO,gBAAgB;KACxD,OAAO;KACR,CAAC;IAGJ,MAAM,QAAQ,SAAS,IAAI,UAAU;AACrC,UAAM,MAAM,UAAU;KACpB,IAAI;KACJ,KAAK,OAAO,WAAW,EAAE,GAAG;KAC5B,KAAK,SAAS,OAAO,MAAM,EAAE,CAAC,GAAG;KACjC,OAAO,WAAW,WAAW;KAC7B,YAAY,YAAY,KAAA;KACzB;;AAGH,iBAAc,MAAM,SAAS,MAAM,KAAK,SAAS,QAAQ,CAAC;AAC1D,iBAAc,MAAM,UAAU,MAAM,KAAK,UAAU,QAAQ,CAAC;AAC5D,iBAAc,MAAM,gBAAgB,cAAc,MAAM,OAAO,IAAI,MAAM;AACzE,iBAAc,MAAM,gBAAgB,EAAE;AACtC,uBAAoB;AACpB,UAAO;UACD;AACN,UAAO;;;CAIX,SAAS,UAAU,OAAqC;AACtD,iBAAe;AACf,MAAI,MAAM,UAAU,MAAM,OAAO,SAAS,GAAG;AAC3C,iBAAc,MAAM,SAAS,WAAW,MAAM,OAAO;AACrD,iBAAc,MAAM,gBAAgB,MAAM,iBAAiB,MAAM,OAAO,GAAG;;AAE7E,MAAI,MAAM,QACR,eAAc,MAAM,UAAU,WAAW,MAAM,QAAQ;AAEzD,gBAAc,MAAM,gBAAgB,MAAM,iBAAiB,EAAE;AAC7D,gBAAc,MAAM,iBAAiB,MAAM;AAC3C,sBAAoB;;CAGtB,SAAS,QAAQ;EACf,MAAM,QAAQ,iBAAiB,WAAW,cAAc;AACxD,gBAAc,QAAQ;GACpB,QAAQ,CAAC,MAAM;GACf,eAAe,MAAM;GACrB,SAAS,EAAE;GACX,eAAe,EAAE;GACjB,gBAAgB,KAAA;GACjB;AACD,UAAQ,QAAQ,EAAE;AAClB,eAAa,QAAQ;;AAGvB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;ACvaH,IAAM,uBAAuB;CAAC;CAAK;CAAK;CAAI;;AAI5C,SAAgB,qBAAqB,OAA4B;AAC/D,KAAI,MAAM,WAAW,EAAG,QAAO;CAE/B,IAAI,OAAkB;CACtB,IAAI,YAAY;AAEhB,MAAK,MAAM,aAAa,sBAAsB;EAC5C,MAAM,SAAS,MAAM,KAAI,MAAK,EAAE,MAAM,UAAU,CAAC,OAAO;EACxD,MAAM,4BAAY,IAAI,KAAqB;AAC3C,OAAK,MAAM,KAAK,OAAQ,WAAU,IAAI,IAAI,UAAU,IAAI,EAAE,IAAI,KAAK,EAAE;EAGrE,IAAI,OAAO;EACX,IAAI,WAAW;AACf,OAAK,MAAM,CAAC,GAAG,MAAM,UACnB,KAAI,IAAI,YAAa,MAAM,YAAY,IAAI,MAAO;AAChD,UAAO;AACP,cAAW;;EAKf,MAAM,QAAQ,OAAO,IAAI,WAAW,MAAM,SAAS;AACnD,MAAI,QAAQ,WAAW;AACrB,UAAO;AACP,eAAY;;;AAIhB,QAAO;;;;AAKT,SAAgB,WAAW,MAAc,WAAgC;AACvE,QAAO,KAAK,MAAM,UAAU;;;;ACrC9B,SAAgB,SAAS,GAA6D;AACpF,QAAO,EAAE,UAAU,GAAG,EAAE,KAAK,GAAG,EAAE,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AEwBlD,SAAS,aAAa,MAA6B;AACjD,QAAO;EACL,cAAc,IAAI,OAAO,KAAK,SAAS,OAAO,IAAI;EAClD,YAAY,IAAI,OAAO,KAAK,SAAS,KAAK,IAAI;EAC9C,YAAY,IAAI,OAAO,KAAK,SAAS,KAAK,IAAI;EAC9C,iBAAiB,IAAI,OAAO,KAAK,SAAS,UAAU,IAAI;EACxD,gBAAgB,IAAI,OAAO,KAAK,SAAS,SAAS,IAAI;EACtD,cAAc,IAAI,OAAO,KAAK,SAAS,OAAO,IAAI;EAClD,eAAe,IAAI,IAAI,KAAK,cAAc;EAC1C,aAAa,IAAI,IAAI,OAAO,QAAQ,KAAK,YAAY,CAAC;EACtD,iBAAiB,IAAI,IACnB,OAAO,QAAQ,KAAK,gBAAgB,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,EAAa,CAAC,CACxE;EACD,oBAAoB,IAAI,IAAI,KAAK,mBAAmB;EACrD;;AAGH,IAAa,aAAyB,aAAa,cAAU;AAE7D,SAAS,gBAAgB,OAAe,OAAuC;AAC7E,QAAO,MAAM,YAAY,IAAI,MAAM,aAAa,CAAC;;AAGnD,SAAS,oBAAoB,OAAe,OAA4B;AACtE,QAAO,MAAM,mBAAmB,IAAI,MAAM,aAAa,CAAC;;AAS1D,IAAM,sBAAiE;CACrE,YAAY;CACZ,KAAK;CACL,KAAK;CACL,UAAU;CACV,YAAY;CACZ,OAAO;CACP,OAAO;CACP,SAAS;CACV;AAED,SAAS,eACP,QACA,OACqE;AAErE,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,KAAI,MAAM,aAAa,KAAK,OAAO,GAAG,CAAE,QAAO;EAAE,MAAM;EAAS,cAAc,CAAC,EAAE;EAAE;AAErF,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,KAAI,MAAM,WAAW,KAAK,OAAO,GAAG,CAAE,QAAO;EAAE,MAAM;EAAO,cAAc,CAAC,EAAE;EAAE;AAEjF,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,KAAI,MAAM,WAAW,KAAK,OAAO,GAAG,CAAE,QAAO;EAAE,MAAM;EAAO,cAAc,CAAC,EAAE;EAAE;AAEjF,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,KAAI,MAAM,gBAAgB,KAAK,OAAO,GAAG,IAAI,MAAM,cAAc,IAAI,OAAO,GAAG,CAC7E,QAAO;EAAE,MAAM;EAAY,cAAc,CAAC,EAAE;EAAE;AAGlD,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,KAAI,MAAM,eAAe,KAAK,OAAO,GAAG,CAAE,QAAO;EAAE,MAAM;EAAY,cAAc,CAAC,EAAE;EAAE;AAE1F,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,KAAI,MAAM,aAAa,KAAK,OAAO,GAAG,CAAE,QAAO;EAAE,MAAM;EAAS,cAAc,CAAC,EAAE;EAAE;AAErF,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACtC,MAAM,YAAY,gBAAgB,OAAO,IAAI,MAAM;AACnD,MAAI,WAAW;GACb,MAAM,iBAAiB,oBAAoB,OAAO,IAAI,MAAM;GAC5D,MAAM,eAAe,CAAC,EAAE;AACxB,OAAI,gBAAgB;AAElB,QAAI,IAAI,IAAI,OAAO,OAAQ,cAAa,KAAK,IAAI,EAAE;AACnD,WAAO;KAAE,MAAM;KAAc,SAAS;KAAW;KAAc;;AAGjE,OAAI,IAAI,KAAK,oBAAoB,OAAO,IAAI,IAAI,MAAM,EAAE;AACtD,iBAAa,QAAQ,IAAI,EAAE;AAC3B,WAAO;KAAE,MAAM;KAAc,SAAS;KAAW;KAAc;;AAEjE,UAAO;IAAE,MAAM;IAAc,SAAS;IAAW;IAAc;;;AAGnE,QAAO;EAAE,MAAM;EAAW,cAAc,EAAE;EAAE;;;;;;;;AAS9C,SAAS,kBACP,QACA,OACiD;AACjD,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACtC,MAAM,OAAO,MAAM,gBAAgB,IAAI,OAAO,GAAG,aAAa,CAAC;AAC/D,MAAI,KAAM,QAAO;GAAE;GAAM,UAAU;GAAG;;;AAK1C,SAAS,SAAS,MAAuB,SAA0B;CACjE,MAAM,OAAwC;EAC5C,YAAY;EACZ,KAAK;EACL,KAAK;EACL,UAAU;EACV,YAAY;EACZ,OAAO;EACP,OAAO;EACP,SAAS;EACV;AACD,QAAO,UAAU,GAAG,KAAK,MAAM,KAAK,YAAY,KAAK;;;AAIvD,SAAgB,YACd,kBACA,SACe;CACf,MAAM,QAAQ;EAAE,GAAG;EAAY,GAAI,QAAQ,SAAS,EAAE;EAAG;CACzD,MAAM,QAAQ,QAAQ,mBAAmB,EAAE;CAC3C,MAAM,0BAAU,IAAI,KAA0B;AAE9C,MAAK,IAAI,IAAI,GAAG,IAAI,iBAAiB,QAAQ,KAAK;EAChD,MAAM,OAAO,MAAM,IAAI,aAAa;EACpC,IAAI;EACJ,IAAI;EACJ,IAAI;AAEJ,MAAI,SAAS,MAAM;AACjB,UAAO;AACP,kBAAe,EAAE;aACR,SAAS,SAAS;AAC3B,UAAO;AACP,kBAAe,EAAE;SACZ;GACL,MAAM,IAAI,eAAe,iBAAiB,IAAI,MAAM;AACpD,UAAO,EAAE;AACT,aAAU,EAAE;AACZ,kBAAe,EAAE;;EAKnB,MAAM,OAAO,kBAAkB,iBAAiB,IAAI,MAAM;AAC1D,MAAI,MAAM;AACR,aAAU,UAAU,GAAG,QAAQ,KAAK,KAAK,SAAS,KAAK;AACvD,OAAI,CAAC,aAAa,SAAS,KAAK,SAAS,CAAE,cAAa,KAAK,KAAK,SAAS;;EAG7E,MAAM,MAAM,SAAS;GAAE;GAAM;GAAS,CAAC;EACvC,MAAM,SAAS,QAAQ,IAAI,IAAI;AAC/B,MAAI,QAAQ;AACV,UAAO,QAAQ,KAAK,EAAE;AACtB,QAAK,MAAM,KAAK,aACd,KAAI,CAAC,OAAO,kBAAkB,SAAS,EAAE,CAAE,QAAO,kBAAkB,KAAK,EAAE;QAG7E,SAAQ,IAAI,KAAK;GACf;GACA;GACA,OAAO,SAAS,MAAM,QAAQ;GAC9B,SAAS,CAAC,EAAE;GACZ,mBAAmB,CAAC,GAAG,aAAa;GACpC,aAAa,oBAAoB;GAClC,CAAC;;AAIN,QAAO,CAAC,GAAG,QAAQ,QAAQ,CAAC;;;;ACnM9B,IAAM,qBAAqB;AAC3B,IAAM,QAAQ;AACd,IAAM,oBAAoB;AAK1B,IAAM,0BAA0B;AAQhC,IAAM,iCAAiC;AAIvC,IAAM,wCAAwC;AAE9C,SAAgB,aAAa,OAAoC;CAC/D,MAAM,QAAQ,MAAM,KAAK,MAAM;AAC/B,KAAI,OAAO;EACT,MAAM,MAAM,OAAO,MAAM,GAAG;EAC5B,MAAM,MAAM,OAAO,MAAM,GAAG;AAC5B,MAAI,QAAQ,EAAG,QAAO;AACtB,SAAO;GAAE,OAAO,MAAM;GAAK,MAAM;GAAS,KAAK;GAAO;;CAExD,MAAM,IAAI,mBAAmB,KAAK,MAAM;AACxC,KAAI,CAAC,EAAG,QAAO;CACf,MAAM,WAAW,EAAE,GAAG,QAAQ,QAAQ,IAAI;CAC1C,MAAM,QAAQ,OAAO,SAAS;AAC9B,KAAI,OAAO,MAAM,MAAM,CAAE,QAAO;AAChC,QAAO;EAAE;EAAO,MAAM,EAAE;EAAI,KAAK;EAAO;;AAU1C,SAAgB,UAAU,OAAmC;CAC3D,MAAM,EAAE,QAAQ,UAAU,gBAAgB,cAAc;CAExD,MAAM,cADS,IAAI,IAAI,OAAO,CACH;AAE3B,KAAI,gBAAgB,EAAG,QAAO;AAG9B,KAAI,aAAa,iBAAiB;MACb,OAAO,OAAM,MAAK,kBAAkB,KAAK,EAAE,CAAC,IAC7C,eAAe,YAAY,GAAK,QAAO;;AAK3D,KADmB,OAAO,OAAM,MAAK,aAAa,EAAE,KAAK,KAAK,EAC9C;AAId,MAAI,eAAe,sCAAuC,QAAO;AACjE,SAAO;;AAYT,KAD0B,OAAO,OAAM,MAAK,wBAAwB,KAAK,EAAE,CAAC,CACrD,QAAO;AAE9B,KAAI,eAAe,+BAAgC,QAAO;AAE1D,QAAO;;;;;;;;;;AAWT,SAAS,gBACP,QACA,cACA,MACe;AACf,KAAI,aAAa,WAAW,EAAG,QAAO;AAMtC,MADoB,SAAS,aAAa,SAAS,aAAa,OAAO,OAAM,MAAK,aAAa,EAAE,KAAK,KAAK,EAC3F;EACd,MAAM,eAAe,MAA6B;GAChD,MAAM,KAAK,EAAE,aAAa;AAC1B,OAAI;IAAC;IAAO;IAAO;IAAK;IAAM;IAAK;IAAK;IAAO;IAAQ;IAAM;IAAQ;IAAQ;IAAQ,CAAC,SAAS,GAAG,CAAE,QAAO;AAC3G,OAAI;IAAC;IAAM;IAAM;IAAM;IAAK;IAAS;IAAM,CAAC,SAAS,GAAG,CAAE,QAAO;AACjE,OAAI;IAAC;IAAM;IAAK;IAAM;IAAM;IAAM;IAAK,CAAC,SAAS,GAAG,CAAE,QAAO;AAC7D,OAAI;IAAC;IAAM;IAAK;IAAM;IAAM;IAAK,CAAC,SAAS,GAAG,CAAE,QAAO;AACvD,OAAI,OAAO,IAAK,QAAO;AACvB,OAAI;IAAC;IAAK;IAAK;IAAW;IAAS,CAAC,SAAS,GAAG,CAAE,QAAO;AACzD,UAAO;;EAET,MAAM,wBAAQ,IAAI,KAAqB;AACvC,OAAK,MAAM,KAAK,QAAQ;GACtB,MAAM,IAAI,aAAa,EAAE;GACzB,MAAM,MAAM,IAAI,YAAY,EAAE,KAAK,GAAG;AACtC,OAAI,IAAK,OAAM,IAAI,MAAM,MAAM,IAAI,IAAI,IAAI,KAAK,EAAE;;EAEpD,IAAI,OAA8C;AAClD,OAAK,MAAM,CAAC,KAAK,UAAU,MACzB,KAAI,CAAC,QAAQ,QAAQ,KAAK,MAAO,QAAO;GAAE;GAAK;GAAO;AAExD,MAAI,QAAQ,KAAK,SAAS,OAAO,SAAS,EAAG,QAAO,KAAK;;AAI3D,KAAI,OAAO,OAAM,MAAK,UAAU,KAAK,EAAE,IAAI,UAAU,KAAK,EAAE,CAAC,CAAE,QAAO;AAGtE,KAAI,SAAS,YAAa,QAAO;AAGjC,KAAI,OAAO,OAAM,MAAK,iCAAiC,KAAK,EAAE,CAAC,CAAE,QAAO;AAGxE,KAAI,OAAO,OAAM,MAAK,8HAA8H,KAAK,EAAE,CAAC,CAC1J,QAAO;AAIT,KAAI,OAAO,OAAM,MAAK,yBAAyB,KAAK,EAAE,CAAC,CAAE,QAAO;AAOhE,KAAI,aAAa,UAAU,KAAK,OAAO,OAAM,MAAK,eAAe,KAAK,EAAE,CAAC,CAAE,QAAO;AAElF,QAAO;;AAGT,SAAgB,iBACd,kBACA,WACA,SACA,mBACa;CACb,MAAM,iBAAiB,iBAAiB,IAAI,UAAU;CACtD,MAAM,YAAY,iBAAiB;CAEnC,MAAM,UAAwB,EAAE;CAChC,MAAM,SAAS,IAAI,IAAI,kBAAkB;AAEzC,MAAK,IAAI,MAAM,GAAG,MAAM,gBAAgB,OAAO;EAC7C,MAAM,SAAS,iBAAiB,KAAI,MAAK,EAAE,QAAQ,GAAG;EACtD,MAAM,eAAe,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC;EAEzC,IAAI;AACJ,MAAI,OAAO,IAAI,IAAI,IAAI,aAAa,WAAW,EAI7C,QAAO;MAEP,QAAO,UAAU;GAAE;GAAQ,UAAU;GAAK;GAAgB;GAAW,CAAC;EAGxE,MAAM,UACJ,SAAS,YACL,EACE,SAAS,OAAO,YACd,aACG,KAAI,MAAK,CAAC,GAAG,aAAa,EAAE,CAAC,CAAU,CACvC,QAAQ,MAA4C,EAAE,OAAO,KAAK,CACtE,EACF,GACD,KAAA;EAEN,MAAM,eAAe,gBAAgB,QAAQ,cAAc,KAAK;AAChE,UAAQ,KAAK;GACX,OAAO;GACP,MAAM,gBAAgB,SAAS,MAAM;GACrC,eAAe,CAAC,IAAI;GACpB;GACA,aAAa,aAAa;GAC1B;GACA;GAEA,MAAM,QAAQ,IAAI,WAAW;GAC9B,CAAC;;CAKJ,IAAI,mBAAmB;AACvB,MAAK,MAAM,KAAK,QACd,KAAI,EAAE,SAAS,WACb,KAAI,iBAAkB,GAAE,OAAO;KAC1B,oBAAmB;AAM5B,QAAO;EAAE;EAAW;EAAS;EAAS,SAFtB,QAAQ,QAAO,MAAK,EAAE,SAAS,SAAS,CAAC,KAAI,MAAK,EAAE,MAAM;EAE3B;;;;AC9NjD,IAAM,YAAY;AAClB,IAAM,iBAAiB;AAEvB,SAAS,qBAAqB,MAAkB,QAAgB,QAAwB;CACtF,MAAM,sBAAM,IAAI,KAA0B;AAC1C,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,IAAI,IAAI,WAAW;EACzB,MAAM,IAAI,IAAI,WAAW;EACzB,MAAM,MAAM,IAAI,IAAI,EAAE;AACtB,MAAI,IAAK,KAAI,IAAI,EAAE;MACd,KAAI,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;;AAE/B,KAAI,IAAI,SAAS,EAAG,QAAO;CAC3B,IAAI,aAAa;AACjB,MAAK,MAAM,OAAO,IAAI,QAAQ,CAAE,KAAI,IAAI,SAAS,EAAG;AACpD,QAAO,aAAa,IAAI;;AAG1B,SAAgB,WAAW,QAAqB,MAAqC;AACnF,KAAI,KAAK,SAAS,eAAgB,QAAO,EAAE;CAE3C,MAAM,MAAyB,EAAE;AACjC,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,SAAS,GAAG,KAAK;EAClD,MAAM,IAAI,OAAO,QAAQ;EACzB,MAAM,IAAI,OAAO,QAAQ,IAAI;AAC7B,MAAI,EAAE,SAAS,YAAY,EAAE,SAAS,SAAU;EAEhD,MAAM,UAAU,qBAAqB,MAAM,EAAE,OAAO,EAAE,MAAM;EAC5D,MAAM,WAAW,qBAAqB,MAAM,EAAE,OAAO,EAAE,MAAM;AAC7D,MAAI,UAAU,aAAa,WAAW,UAAW;AAGjD,MADe,IAAI,IAAI,KAAK,KAAI,MAAK,GAAG,EAAE,EAAE,UAAU,GAAG,GAAG,EAAE,EAAE,UAAU,KAAK,CAAC,CACrE,OAAO,EAAG;AAErB,MAAI,KAAK;GACP,WAAW,OAAO;GAClB,SAAS,OAAO;GAChB,eAAe,CAAC,EAAE,OAAO,EAAE,MAAM;GACjC,cAAc,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ;GACjD,YAAY,KAAK,IAAI,SAAS,SAAS;GACxC,CAAC;;AAEJ,QAAO;;;;AC5CT,IAAa,iBAAiB;CAC5B;CAAW;CAAW;CAAW;CAAW;CAC5C;CAAW;CAAW;CAAW;CAAW;CAC7C;AAED,IAAa,mBAAmB;;;;;;;ACkBhC,SAAS,SAAS,KAAiB,KAA4B;CAC7D,IAAI,QAAQ;AAGZ,KAAI,IAAI,KAAK,SAAS,OAAO,IAAI,IAAI,MACnC,SAAQ,IAAI,IAAI,MAAM;AAIxB,KAAI,IAAI,KAAK,SAAS,SAAS,IAAI,IAAI,IAAI,KAAK,SAAS,SAAS,MAAM,CACtE,QAAO;AAIT,KAAI,IAAI,SAAS,aAAa,IAAI,SAAS,SAAS,UAAU;EAC5D,MAAM,SAAS,IAAI,SAAS,QAAQ;AACpC,MAAI,UAAU,IAAI,QAAQ,OAAO;GAC/B,MAAM,MAAM,QAAQ,OAAO,OAAO,IAAI,QAAQ;AAC9C,OAAI,QAAQ,KAAM,SAAQ;;;AAI9B,QAAO;;AAGT,SAAS,QAAQ,OAAe,GAAkC;AAChE,KAAI,CAAC,EAAE,SAAS,EAAE,MAAM,WAAW,EAAG,QAAO;AAC7C,MAAK,IAAI,IAAI,GAAG,IAAI,EAAE,MAAM,QAAQ,IAClC,KAAI,QAAQ,EAAE,MAAM,GAClB,QAAO,EAAE,SAAS,MAAM,KAAK,EAAE,MAAM;CAGzC,MAAM,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS;AACtC,QAAO,EAAE,SAAS,EAAE,MAAM,WAAW,KAAK;;AAG5C,SAAgB,cAAc,OAAsC;CAClE,MAAM,qBAAoC,EAAE;CAC5C,MAAM,WAA0B,EAAE;CAClC,MAAM,kBAA4B,EAAE;CACpC,MAAM,WAA0B,EAAE;CAClC,IAAI,WAAW;AAEf,MAAK,MAAM,OAAO,MAAM,SAAS;EAC/B,MAAM,SAAS,MAAM,QAAQ,SAAS,IAAI;AAC1C,MAAI,CAAC,OAAQ;AAEb,MAAI,IAAI,gBAAgB,WAAW;AACjC,QAAK,MAAM,KAAK,IAAI,QAAS,iBAAgB,KAAK,MAAM,YAAY,GAAG;AACvE;;EAKF,MAAM,2BAAW,IAAI,KAAkD;EACvE,MAAM,WAAW,IAAI,IAAI,OAAO,QAAQ,KAAI,MAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AAE/D,OAAK,MAAM,KAAK,IAAI,SAAS;GAC3B,MAAM,SAAS,MAAM,iBAAiB;GACtC,MAAM,UAAU,MAAM,YAAY;GAClC,IAAI,WAAW;GACf,MAAM,cAAsC,EAAE;AAE9C,QAAK,MAAM,OAAO,OAAO,SAAS;IAGhC,MAAM,UAAU,SAAS,KAFJ,IAAI,cAAc,KAAI,QAAO,OAAO,QAAQ,GAAG,CAC3C,KAAK,IAAI,CACA;AAClC,QAAI,YAAY,MAAM;AACpB,gBAAW;AACX;;AAEF,gBAAY,IAAI,eAAe,IAAI,QAAQ;;AAG7C,OAAI,UAAU;AACZ,oBAAgB,KAAK,QAAQ;AAC7B;;GAGF,MAAM,WACJ,IAAI,gBAAgB,UAChB,OAAO,QAAQ,KAAI,QAAO;IACxB,MAAM,MAAM,SAAS,IAAI,IAAI;AAC7B,WAAO,YAAY,IAAI,eAAe,IAAI;KAC1C,GACF,CAAC,IAAI,MAAM;GAYjB,MAAM,WADJ,IAAI,gBAAgB,WAAW,IAAI,SAAS,aAAa,SAAS,SAAS,IAEzE,GAAG,IAAI,MAAM,KAAK,SAAS,KAAK,MAAM,KACtC,SAAS,SAAS,IAChB,SAAS,KAAK,MAAM,GACpB,IAAI;GAEV,MAAM,SAAS,SAAS,IAAI,SAAS;AACrC,OAAI,OACF,QAAO,QAAQ,KAAK,QAAQ;OAE5B,UAAS,IAAI,UAAU;IAAE,MAAM;IAAU,SAAS,CAAC,QAAQ;IAAE,CAAC;AAGhE,YAAS,KAAK;IAAE,YAAY;IAAS,QAAQ;IAAa,OAAO;IAAU,CAAC;;AAG9E,OAAK,MAAM,EAAE,MAAM,aAAa,SAAS,QAAQ,EAAE;GAKjD,MAAM,IAAiB;IAAE;IAAM,OAH7B,IAAI,gBAAgB,YAChB,mBACA,eAAe,aAAa,eAAe;IACX;IAAS;AAC/C,OAAI,IAAI,gBAAgB,UAAW,UAAS,KAAK,EAAE;OAC9C,oBAAmB,KAAK,EAAE;;;AAInC,QAAO;EACL,QAAQ,CAAC,GAAG,oBAAoB,GAAG,SAAS;EAC5C;EACA;EACA;EACA;EACA,SAAS,OAAO,OAAO,MAAM,QAAQ;EACtC;;;;ACxJH,SAAgB,qBAAqB,SAA2C;AAC9E,QAAO;EACL,SAAS;EACT,SAAS,QAAQ,KAAI,OAAM;GACzB,MAAM,EAAE;GACR,SAAS,EAAE;GACX,SAAS,EAAE,QAAQ,KAAI,OAAM;IAC3B,MAAM,EAAE,eAAe,EAAE;IACzB,MAAM,EAAE,QAAQ;IAChB,eAAe,EAAE;IACjB,SAAS,EAAE;IACX,KAAK,EAAE;IACR,EAAE;GACH,SAAS,EAAE;GACZ,EAAE;EACJ;;AAGH,SAAgB,mBACd,IACA,SACe;AACf,QAAO,GAAG,QAAQ,KAAI,SAAQ;EAC5B,MAAM,SAAS,QAAQ,MAAK,MAAK,EAAE,cAAc,KAAK,QAAQ,EAAE,YAAY,KAAK,QAAQ;AACzF,MAAI,CAAC,OACH,OAAM,IAAI,MACR,mDAAmD,KAAK,OAAO,KAAK,UAAU,MAAM,KAAK,UAAU,KACpG;AAEH,MAAI,OAAO,QAAQ,WAAW,KAAK,QAAQ,OACzC,OAAM,IAAI,MACR,wBAAwB,KAAK,QAAQ,OAAO,6BAA6B,OAAO,QAAQ,OAAO,aAAa,KAAK,OAAO,KAAK,UAAU,MAAM,KAAK,UAAU,GAAG,yBAChK;AAEH,SAAO;GACL,GAAG;GACH,SAAS,KAAK;GACd,SAAS,OAAO,QAAQ,KAAK,KAAK,OAAO;IACvC,GAAG;IACH,aAAa,KAAK,QAAQ,GAAG;IAC7B,MAAM,KAAK,QAAQ,GAAG;IACtB,SAAS,KAAK,QAAQ,GAAG;IACzB,KAAK,KAAK,QAAQ,GAAG;IACtB,EAAE;GACJ;GACD;;;;ACjCJ,IAAM,wBAAwB;CAAC;CAAS;CAAS;CAAa;CAAQ;AAEtE,SAAS,aAAa,MAAsB;AAC1C,KAAI,SAAS,KAAK,KAAK,CACrB,QAAO,IAAI,KAAK,QAAQ,MAAM,OAAK,CAAC;AAEtC,QAAO;;AAGT,SAAS,QAAQ,OAAiB,KAAqB;AACrD,QAAO,QAAQ,MACX,MAAM,IAAI,aAAa,CAAC,KAAK,IAAI,GACjC,MAAM,KAAK,IAAK;;AAGtB,SAAS,QAAgB;AACvB,yBAAO,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;;AAG9C,SAAgB,gBACd,SACA,SACA,SACA,SACuC;CACvC,MAAM,MAAM,QAAQ,WAAW,QAAQ,MAAO;CAC9C,MAAM,MAAM,QAAQ,WAAW,QAAQ,QAAQ;CAG/C,MAAM,WAAW,GADJ,QAAQ,iBAAiB,GAAG,QAAQ,eAAe,aAAa,kBACpD,GAAG,OAAO,CAAC,GAAG;AAEvC,KAAI,QAAQ,SAAS,WAAW,CAAC,WAAW,CAAC,SAAS;EACpD,MAAM,UAAU,CAAC,eAAe,GAAI,QAAQ,oBAAoB,sBAAuB;EACvF,MAAM,OAAO,QAAQ,KAAI,MAAK;AAE5B,UAAO,QADG,CAAC,GAAG,GAAG,MAAM,QAAQ,SAAS,EAAE,CAAC,KAAK,GAAG,CAAC,EAClC,IAAI;IACtB;AACF,SAAO;GAAE,SAAS,CAAC,QAAQ,SAAS,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,KAAK;GAAE;GAAU;;CAI3E,MAAM,YAAY,QAAQ,oBAAoB,EAAE;CAChD,MAAM,aAAuD,EAAE;CAG/D,MAAM,gCAAgB,IAAI,KAA0B;AACpD,MAAK,MAAM,OAAO,QAAS,MAAK,MAAM,KAAK,IAAI,QAAS,eAAc,IAAI,GAAG,IAAI;CAEjF,MAAM,8BAAc,IAAI,KAA0B;AAClD,MAAK,MAAM,KAAK,SAAS;EACvB,MAAM,IAAI,EAAE,UAAU,GAAG,EAAE,UAAU,GAAG,EAAE,YAAY,EAAE;AACxD,cAAY,IAAI,GAAG,EAAE;;CAGvB,MAAM,iCAAiB,IAAI,KAAsC;CACjE,MAAM,kCAAkB,IAAI,KAA0B;AACtD,MAAK,MAAM,KAAK,SAAS;EACvB,MAAM,IAAI,EAAE,UAAU,GAAG,EAAE,UAAU,GAAG,EAAE,YAAY,EAAE;EACxD,MAAM,oBAAI,IAAI,KAAyB;AACvC,OAAK,MAAM,KAAK,EAAE,QAAS,GAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE;AAC5D,iBAAe,IAAI,GAAG,EAAE;AACxB,kBAAgB,IAAI,GAAG,IAAI,IAAI,EAAE,QAAQ,CAAC;EAE1C,MAAM,+BAAe,IAAI,KAAa;AACtC,OAAK,MAAM,KAAK,EAAE,SAAS;GACzB,MAAM,OAAO,EAAE,eAAe,EAAE;AAChC,OAAI,CAAC,aAAa,IAAI,KAAK,EAAE;AAC3B,iBAAa,IAAI,KAAK;AACtB,QAAI,CAAC,WAAW,MAAK,MAAK,EAAE,YAAY,KAAK,CAC3C,YAAW,KAAK;KAAE,WAAW;KAAG,SAAS;KAAM,CAAC;;;;CAMxD,MAAM,UAAU;EAAC;EAAe;EAAS,GAAG,WAAW,KAAI,MAAK,EAAE,QAAQ;EAAE;EAAS;EAAQ;CAE7F,MAAM,OAAiB,EAAE;AACzB,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,MAAM,cAAc,IAAI,EAAE;EAChC,MAAM,aAAa,KAAK,SAAS;EACjC,MAAM,SAAS,UAAU,MAAM,EAAE;EACjC,MAAM,QAAkB,CAAC,QAAQ,IAAI,WAAW;AAEhD,MAAI,KAAK;GACP,MAAM,IAAI,SAAS,IAAI;GACvB,MAAM,aAAa,gBAAgB,IAAI,EAAE;GACzC,MAAM,YAAiD,eAAe,IAAI,EAAE;GAC5E,MAAM,WAAqB,EAAE;AAC7B,QAAK,MAAM,OAAO,WAChB,KAAI,IAAI,cAAc,KAAK,WAAW;IACpC,MAAM,YAAY,UAAU,IAAI,IAAI,QAAQ;IAC5C,MAAM,MACJ,WAAW,cAAc,KAAI,QAAO,OAAO,QAAQ,GAAG,CAAC,KAAK,IAAI,IAAI;AACtE,UAAM,KAAK,IAAI;AACf,QAAI,aAAa,YAAY,IAAI,UAAU,MAAM,CAAE,UAAS,KAAK,IAAI;SAErE,OAAM,KAAK,GAAG;AAGlB,SAAM,KAAK,SAAS,KAAK,MAAM,CAAC;SAC3B;AACL,QAAK,MAAM,KAAK,WAAY,OAAM,KAAK,GAAG;AAC1C,SAAM,KAAK,GAAG;;AAGhB,QAAM,KAAK,GAAG;AACd,OAAK,KAAK,QAAQ,OAAO,IAAI,CAAC;;AAGhC,QAAO;EAAE,SAAS,CAAC,QAAQ,SAAS,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,KAAK;EAAE;EAAU;;;;;;;;;;;;;;;;AC7G3E,IAAM,iBAA4B,cAAU,uBAAoC,KAC9E,QAAO,IAAI,OAAO,KAAK,KAAK,CAC7B;;;;;;AAOD,SAAgB,qBAAqB,MAAsB;CACzD,IAAI,OAAO;AACX,MAAK,MAAM,MAAM,gBAAgB;EAC/B,IAAI;AACJ,KAAG;AACD,UAAO;AACP,UAAO,KAAK,QAAQ,IAAI,GAAG;AAC3B,MAAG,YAAY;WACR,SAAS;;AAEpB,QAAO;;AAeT,SAAgB,mBAAmB,SAAyC;CAC1E,MAAM,sBAAM,IAAI,KAAuB;CACvC,MAAM,YAAsB,EAAE;AAC9B,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,OAAO,qBAAqB,QAAQ,GAAG;EAC7C,MAAM,SAAS,IAAI,IAAI,KAAK;AAC5B,MAAI,OACF,QAAO,KAAK,EAAE;OACT;AACL,OAAI,IAAI,MAAM,CAAC,EAAE,CAAC;AAClB,aAAU,KAAK,KAAK;;;AAGxB,QAAO;EACL,WAAW;EACX,eAAe,UAAU,KAAI,MAAK,IAAI,IAAI,EAAE,CAAE;EAC9C,iBAAiB;EAClB;;;;;;;AAQH,SAAgB,2BACd,QACA,aACK;CACL,MAAM,gCAAgB,IAAI,KAAuB;AACjD,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,UAAU,QAAQ,IAChD,eAAc,IAAI,YAAY,UAAU,IAAI,YAAY,cAAc,GAAG;AAE3E,QAAO,OAAO,KAAI,OAAM;EACtB,GAAG;EACH,SAAS,EAAE,QAAQ,SAAQ,aAAY;GACrC,MAAM,UAAU,cAAc,IAAI,SAAS;AAC3C,UAAO,UAAU,QAAQ,KAAI,MAAK,YAAY,gBAAgB,GAAG,GAAG,CAAC,SAAS;IAC9E;EACH,EAAE;;;;AC1EL,SAAS,SAAS,OAAkD;AAClE,QAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAS,6BAA6B,OAAyC;AAC7E,QACE,UAAU,UAER,mBAAmB,SAChB,eAAe,SACf,oBAAoB,SACpB,gBAAgB;;;AAMzB,SAAgB,2BACd,SACgC;AAChC,KAAI,CAAC,QAAS,QAAO;AAErB,KAAI,SAAS,QAAQ,KAAK,IAAI,6BAA6B,QAAQ,CACjE,QAAO,QAAQ;AAGjB,KAAI,SAAS,QAAQ,YAAY,CAC/B,QAAO,QAAQ;AAGjB,KAAI,SAAS,QAAQ,WAAW,CAC9B,QAAO,QAAQ;AAGjB,QAAO;;AAGT,SAAS,oBAAoB,QAAiC,SAAmD;AAC/G,KAAI,QAAQ,gBAAiB,QAAO;CACpC,MAAM,UAAU,OAAO,eAAe,OAAO,cAAc,OAAO,QAAQ,OAAO,aAAa,OAAO;CACrG,MAAM,OAAO,OAAO,YAAY,WAAW,QAAQ,aAAa,GAAG;AACnE,QAAO,CAAC,CAAC,SAAS,KAAK,CAAC,SAAS,KAAK;;AAGxC,SAAS,gBAAgB,QAAgD;CACvE,MAAM,aAAa;EACjB,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACR;AAED,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,OAAO,cAAc,YAAY,UAAU,MAAM,CACnD,QAAO;AAET,MAAI,OAAO,cAAc,YAAY,OAAO,SAAS,UAAU,CAC7D,QAAO,OAAO,UAAU;;AAI5B,QAAO;;AAGT,SAAS,gBAAgB,QAAiC,UAA0B;CAClF,MAAM,QAAQ,OAAO,QAAQ,OAAO,SAAS,OAAO,eAAe,OAAO;AAC1E,QAAO,OAAO,UAAU,YAAY,MAAM,MAAM,GAAG,QAAQ;;AAG7D,SAAS,sBAAsB,QAAqD;CAClF,MAAM,QAAQ;EACZ,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACR,CAAC,QAAQ,UACP,OAAO,UAAU,YAAY,MAAM,MAAM,CAAC,SAAS,KACjD,OAAO,UAAU,SACrB;AAED,QAAO,MAAM,SAAS,IAAI,MAAM,IAAI,OAAO,CAAC,KAAK,MAAM,GAAG,KAAA;;AAG5D,SAAS,gBACP,SACA,MACA,OACA,gBACM;AACN,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,UAAU,MAAM,MAAM;AAC5B,MAAI,WAAW,CAAC,KAAK,IAAI,QAAQ,EAAE;AACjC,QAAK,IAAI,QAAQ;AACjB,WAAQ,KAAK;IAAE,OAAO;IAAS,OAAO;IAAS,CAAC;;AAElD;;AAGF,KAAI,CAAC,SAAS,MAAM,IAAI,CAAC,oBAAoB,OAAO,eAAe,CAAE;CAErE,MAAM,cAAc,gBAAgB,MAAM;AAC1C,KAAI,CAAC,eAAe,KAAK,IAAI,YAAY,CAAE;AAE3C,MAAK,IAAI,YAAY;AACrB,SAAQ,KAAK;EACX,OAAO;EACP,OAAO,gBAAgB,OAAO,YAAY;EAC1C,aAAa,sBAAsB,MAAM;EAC1C,CAAC;;AAGJ,SAAS,eACP,SACA,MACA,OACA,gBACM;AACN,KAAI,CAAC,MAAM,QAAQ,MAAM,CAAE;AAC3B,MAAK,MAAM,UAAU,MACnB,iBAAgB,SAAS,MAAM,QAAQ,eAAe;;;AAK1D,SAAgB,mCACd,SACA,UAA2C,EAAE,EACrB;CACxB,MAAM,aAAa,2BAA2B,QAAQ;AACtD,KAAI,CAAC,WAAY,QAAO,EAAE;CAE1B,MAAM,gBAAwC,EAAE;CAChD,MAAM,uBAAO,IAAI,KAAa;AAE9B,gBAAe,eAAe,MAAM,WAAW,SAAS,QAAQ;AAEhE,KAAI,SAAS,WAAW,UAAU,CAChC,MAAK,MAAM,YAAY,OAAO,OAAO,WAAW,UAAU,EAAE;AAC1D,MAAI,CAAC,SAAS,SAAS,CAAE;AAEzB,iBAAe,eAAe,OADT,SAAS,SAAS,KAAK,GAAG,SAAS,OAAO,UACd,SAAS,QAAQ;;AAItE,KAAI,MAAM,QAAQ,WAAW,OAAO,CAClC,MAAK,MAAM,SAAS,WAAW,QAAQ;AACrC,MAAI,CAAC,SAAS,MAAM,CAAE;AACtB,iBAAe,eAAe,MAAM,MAAM,SAAS,QAAQ;;AAI/D,QAAO;;;AAIT,SAAgB,iCACd,SACA,UAA2C,EAAE,EACnC;AACV,QAAO,mCAAmC,SAAS,QAAQ,CAAC,KAAI,WAAU,OAAO,MAAM;;;;AClLzF,SAAS,aAAa,MAAc,YAAoB,KAAe;CACrE,MAAM,SAAmB,EAAE;CAC3B,IAAI,UAAU;CACd,IAAI,WAAW;AACf,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,OAAO,KAAK;AAClB,MAAI,SAAS,KACX,KAAI,YAAY,KAAK,IAAI,OAAO,MAAK;AACnC,cAAW;AACX;QAEA,YAAW,CAAC;WAEL,SAAS,aAAa,CAAC,UAAU;AAC1C,UAAO,KAAK,QAAQ,MAAM,CAAC;AAC3B,aAAU;QAEV,YAAW;;AAGf,QAAO,KAAK,QAAQ,MAAM,CAAC;AAC3B,QAAO;;AAGT,SAAgB,SAAS,MAA6B;CACpD,MAAM,QAAQ,KAAK,MAAM,CAAC,MAAM,KAAK;AACrC,KAAI,MAAM,SAAS,EAAG,OAAM,IAAI,MAAM,mDAAmD;CACzF,MAAM,YAAY,MAAM;CACxB,MAAM,eAAe,UAAU,SAAS,IAAK,GAAG,MAAO;CACvD,MAAM,UAAU,aAAa,WAAW,aAAa;CACrD,MAAM,OAAiC,EAAE;AACzC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,SAAS,aAAa,MAAM,IAAI,aAAa;AACnD,MAAI,OAAO,WAAW,QAAQ,OAAQ;EACtC,MAAM,MAA8B,EAAE;AACtC,UAAQ,SAAS,GAAG,QAAQ;AAAE,OAAI,KAAK,OAAO;IAAO;AACrD,OAAK,KAAK,IAAI;;CAEhB,MAAM,iBAAiB;EAAC;EAAU;EAAQ;EAAM;EAAe;EAAc;EAAa;EAAY;EAAY;AAElH,QAAO;EAAE,SAAS;EAAS;EAAM,cADZ,QAAQ,MAAK,MAAK,eAAe,SAAS,EAAE,aAAa,CAAC,CAAC,IAAI,QAAQ;EAC7C,WAAW;EAAc;;;;ACL1E,IAAI,oBAAoB;AACxB,IAAI,2BAA2B;AAC/B,SAAS,eAAe,KAAa;AACnC,KAAI,kBAAmB;AACvB,KAAI,OAAO,YAAY,eAAA,QAAA,IAAA,aAAyC,aAAc;AAC9E,SAAQ,KACN,kBAAkB,IAAI,kGACvB;AACD,qBAAoB;;AAMtB,SAAgB,eAAe;CAC7B,MAAM,YAAY,IAAe,QAAQ;CACzC,MAAM,UAAU,IAAI,GAAG;CACvB,MAAM,UAAU,IAA0B,KAAK;CAC/C,MAAM,kBAAkB,IAA4B,EAAE,CAAC;CAEvD,MAAM,UAAU,IAAmB,EAAE,CAAC;CACtC,MAAM,UAAU,IAAiC,EAAE,CAAC;CACpD,MAAM,iBAAiB,IAAY,GAAG;CAEtC,MAAM,UAAU,eAAe;AAC7B,MAAI,QAAQ,MACV,QAAO,QAAQ,MAAM,KAAK,KAAI,MAAK,EAAE,QAAQ,MAAO,iBAAiB,GAAG;AAE1E,SAAO,QAAQ,MAAM,MAAM,KAAK,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC,CAAC,QAAO,MAAK,EAAE,SAAS,EAAE;GAC7E;CAEF,MAAM,YAAY,IAAgB,EAAE,CAAC;CACrC,MAAM,YAAY,IAAqB,IAAI;CAK3C,MAAM,cAAc,IAAiC,KAAK;CAE1D,SAAS,aAAa;AACpB,OAAK,UAAU,UAAU,SAAS,UAAU,UAAU,iBAAiB,QAAQ,OAAO;AACpF,sBAAmB;AACnB;;AAEF,uBAAqB;;CAGvB,SAAS,oBAAoB;AAG3B,cAAY,QAAQ;EACpB,MAAM,MAAM,QAAQ;EACpB,MAAM,gBAAgB,IAAI,QAAQ,QAAO,MAAK,MAAM,IAAI,gBAAgB,MAAM,cAAc;AAC5F,MAAI,cAAc,WAAW,GAAG;AAE9B,aAAU,QAAQ,IAAI,KAAK,UAAU,EAAE,CAAC;AACxC,WAAQ,QAAQ,CAAC;IACf,MAAM;IACN,OAAO;IACP,SAAS,IAAI,KAAK,KAAK,GAAG,MAAM,EAAE;IAClC,mBAAmB,EAAE;IACrB,aAAa;IACd,CAAC;AACF,WAAQ,QAAQ,EAAE,SAAS;IAAE,WAAW;IAAW,SAAS,EAAE;IAAE,SAAS,EAAE;IAAE,EAAE;AAC/E,kBAAe,QAAQ;AACvB;;EAEF,MAAM,SAAS,IAAI,KAAK,KAAI,MAAK,cAAc,KAAI,QAAO,EAAE,QAAQ,GAAG,CAAC;AACxE,YAAU,QAAQ;EAQlB,MAAM,WAAW,YAAY,QAAQ,EAAE,iBANzB,IAAI,KAAK,KAAI,MAAK;GAC9B,MAAM,IAAI,OAAO,EAAE,kBAAkB,GAAG,CAAC,aAAa;AACtD,OAAI,MAAM,KAAM,QAAO;AACvB,OAAI,MAAM,QAAS,QAAO;IAE1B,EAC6D,CAAC;AAChE,UAAQ,QAAQ;EAEhB,MAAM,aAA0C,EAAE;AAClD,OAAK,MAAM,OAAO,UAAU;GAE1B,MAAM,SAAS,iBADM,IAAI,QAAQ,KAAI,MAAK,OAAO,GAAG,EACN,IAAI,MAAM,IAAI,SAAS,IAAI,kBAAkB;AAE3F,UAAO,UAAU,OAAO,QAAQ,KAAK,GAAG,OAAO;IAC7C,GAAG;IACH,MAAM,cAAc,MAAM,EAAE;IAC5B,cAAc,cAAc;IAC7B,EAAE;AACH,cAAW,SAAS,IAAI,IAAI;;AAE9B,UAAQ,QAAQ;AAChB,MAAI,SAAS,SAAS,EAAG,gBAAe,QAAQ,SAAS,SAAS,GAAG;;CAGvE,SAAS,sBAAsB;EAC7B,MAAM,QAAQ,QAAQ;AACtB,MAAI,MAAM,WAAW,GAAG;AACtB,WAAQ,QAAQ,EAAE;AAClB,WAAQ,QAAQ,EAAE;AAClB,eAAY,QAAQ;AACpB;;EAOF,MAAM,MAAM,mBAAmB,MAAM;AACrC,cAAY,QAAQ;EAEpB,MAAM,IAAI,qBAAqB,IAAI,UAAU;AAC7C,YAAU,QAAQ;AAClB,YAAU,QAAQ,IAAI,UAAU,KAAI,MAAK,WAAW,GAAG,EAAE,CAAC;EAG1D,MAAM,QAAQ,IAAI,cAAc,KAAI,YAAW,gBAAgB,MAAM,QAAQ,IAAI;EACjF,MAAM,WAAW,YAAY,UAAU,OAAO,EAAE,iBAAiB,OAAO,CAAC;AACzE,UAAQ,QAAQ;EAChB,MAAM,aAA0C,EAAE;AAClD,OAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,eAAe,IAAI,QAAQ,KAAI,MAAK,UAAU,MAAM,GAAG;AAC7D,cAAW,SAAS,IAAI,IAAI,iBAC1B,cACA,IAAI,MACJ,IAAI,SACJ,IAAI,kBACL;;AAEH,UAAQ,QAAQ;AAChB,MAAI,SAAS,SAAS,EACpB,gBAAe,QAAQ,SAAS,SAAS,GAAG;;CAIhD,MAAM,eAAe,eAAmC,QAAQ,MAAM,eAAe,UAAU,KAAK;CAEpG,MAAM,cAAc,eAAkC;EACpD,MAAM,SAAS,aAAa;AAC5B,MAAI,CAAC,OAAQ,QAAO,EAAE;EACtB,MAAM,cAAc,QAAQ,MAAM,MAChC,MAAK,SAAS,EAAE,KAAK,eAAe,MACrC;AACD,MAAI,CAAC,YAAa,QAAO,EAAE;AAE3B,SAAO,WAAW,QADG,YAAY,QAAQ,KAAI,MAAK,UAAU,MAAM,GAAG,CAC9B;GACvC;CAEF,MAAM,SAAS,eAAgC;EAC7C,MAAM,MAAM,YAAY;EACxB,MAAM,WAAW,cAAc;GAC7B,kBAAkB,UAAU;GAI5B,aAAa,MAAM,IAAI,YAAY,QAAQ;GAC3C,SAAS,QAAQ;GACjB,SAAS,QAAQ;GAClB,CAAC;AACF,MAAI,CAAC,IAAK,QAAO;AAIjB,SAAO;GACL,GAAG;GACH,QAAQ,2BAA2B,SAAS,QAAQ,IAAI;GACxD,oBAAoB,SAAS,qBACzB,2BAA2B,SAAS,oBAAoB,IAAI,GAC5D,KAAA;GACJ,UAAU,SAAS,WACf,2BAA2B,SAAS,UAAU,IAAI,GAClD,KAAA;GACL;GACD;CAEF,MAAM,cAAc,eAClB,qBAAqB,OAAO,MAAM,WAAW,EAAE,CAAC,CACjD;CAED,MAAM,SAAS,eAAe,OAAO,MAAM,OAAO;CAClD,MAAM,WAAW,eAAe,OAAO,MAAM,YAAY,EAAE,CAAC;CAC5D,MAAM,WAAW,eAAe,OAAO,MAAM,SAAS;CACtD,MAAM,kBAAkB,eAAe,OAAO,MAAM,gBAAgB;CAIpE,SAAS,oBAAoB,KAAa,aAA+B;EACvE,MAAM,SAAS,QAAQ,MAAM,MAAK,MAAK,SAAS,EAAE,KAAK,IAAI;AAC3D,MAAI,CAAC,UAAU,OAAO,gBAAgB,YAAa;AACnD,UAAQ,QAAQ,QAAQ,MAAM,KAAI,MAChC,SAAS,EAAE,KAAK,MAAM;GAAE,GAAG;GAAG;GAAa,GAAG,EAC/C;;CAGH,SAAS,qBACP,WACA,aACA;EACA,IAAI,UAAU;EACd,MAAM,OAAO,QAAQ,MAAM,KAAI,MAAK;AAClC,OAAI,UAAU,EAAE,IAAI,EAAE,gBAAgB,aAAa;AACjD,cAAU;AACV,WAAO;KAAE,GAAG;KAAG;KAAa;;AAE9B,UAAO;IACP;AACF,MAAI,QAAS,SAAQ,QAAQ;;CAG/B,SAAS,cAAc,KAAa,KAAa;EAC/C,MAAM,SAAS,QAAQ,MAAM;AAC7B,MAAI,CAAC,OAAQ;EACb,MAAM,MAAM,IAAI,IAAI,OAAO,QAAQ;AACnC,MAAI,IAAI,IAAI,IAAI,CAAE,KAAI,OAAO,IAAI;MAC5B,KAAI,IAAI,IAAI;AACjB,UAAQ,QAAQ;GACd,GAAG,QAAQ;IACV,MAAM;IAAE,GAAG;IAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE;IAAE;GAC9D;;CAGH,SAAS,aAAa,KAAa,KAAa,OAA4B;EAC1E,MAAM,SAAS,QAAQ,MAAM;AAC7B,MAAI,CAAC,OAAQ;EACb,MAAM,MAAM,OAAO,QAAQ,MAAK,MAAK,EAAE,UAAU,IAAI;AACrD,MAAI,CAAC,IAAK;EACV,IAAI,UAAU;AACd,OAAK,MAAM,KAAK,OAAO,KAAK,MAAM,CAChC,KAAK,IAA2C,OAAkB,MAA6C,IAAc;AAC3H,aAAU;AACV;;AAGJ,MAAI,CAAC,QAAS;AACd,UAAQ,QAAQ;GACd,GAAG,QAAQ;IACV,MAAM;IACL,GAAG;IACH,SAAS,OAAO,QAAQ,KAAI,MAAM,EAAE,UAAU,MAAM;KAAE,GAAG;KAAG,GAAG;KAAO,GAAG,EAAG;IAC7E;GACF;;CAGH,SAAS,QAAQ,KAAa,KAAa,MAAkB;AAC3D,eAAa,KAAK,KAAK,EAAE,MAAM,CAAC;;CAGlC,SAAS,cAAc,KAAa,KAAa,MAAc;AAC7D,eAAa,KAAK,KAAK,EAAE,aAAa,MAAM,CAAC;;CAG/C,SAAS,YAAY,KAAa,KAAa,KAAe;AAC5D,eAAa,KAAK,KAAK,EAAE,KAAK,CAAC;;CAGjC,SAAS,WAAW,KAAa,KAAa,SAAyB;AACrE,eAAa,KAAK,KAAK,EAAE,SAAS,CAAC;;CAGrC,SAAS,aAAa,KAAa,SAAmB;EACpD,MAAM,SAAS,QAAQ,MAAM;AAC7B,MAAI,CAAC,UAAU,QAAQ,SAAS,EAAG;EACnC,MAAM,SAAS,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE;EACjD,MAAM,QAAQ,OAAO;EACrB,MAAM,SAAqB;GACzB,OAAO;GACP,MAAM,UAAU,OAAO,KAAI,MAAK,IAAI,EAAE,CAAC,KAAK,IAAI;GAChD,eAAe,OAAO,SAAQ,MAAK,OAAO,QAAQ,MAAK,MAAK,EAAE,UAAU,EAAE,EAAE,iBAAiB,CAAC,EAAE,CAAC;GACjG,cAAc,EAAE;GAChB,aAAa;GACb,MAAM;GACP;EACD,MAAM,UAAU,IAAI,IAAI,OAAO;AAC/B,UAAQ,QAAQ;GACd,GAAG,QAAQ;IACV,MAAM;IACL,GAAG;IACH,SAAS,CAAC,QAAQ,GAAG,OAAO,QAAQ,QAAO,MAAK,CAAC,QAAQ,IAAI,EAAE,MAAM,CAAC,CAAC,CACpE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;IACpC,SAAS,OAAO,QAAQ,QAAO,MAAK,CAAC,QAAQ,IAAI,EAAE,IAAI,MAAM,MAAM;IACpE;GACF;;CAGH,SAAS,gBAAgB,IAAuB;EAC9C,MAAM,WAAW,mBAAmB,IAAI,OAAO,OAAO,QAAQ,MAAM,CAAC;EACrE,MAAM,OAAoC,EAAE;AAC5C,OAAK,MAAM,KAAK,SAAU,MAAK,SAAS;GAAE,MAAM,EAAE;GAAW,SAAS,EAAE;GAAS,CAAC,IAAI;AACtF,UAAQ,QAAQ;;CAKlB,MAAM,sBAAsB,eAAe,QAAQ,MAAM,SAAS,EAAE;CAEpE,SAAS,iBAAiB,OAA8B,aAAa,SAAwB,OAAO;EAClG,MAAM,EAAE,SAAS,aAAa,gBAC5B,QAAQ,OACR,SAAS,cAAc,OAAO,OAAO,QAAQ,MAAM,GAAG,MACtD,SAAS,cAAc,QAAQ,QAAQ,MACvC;GAAE;GAAM;GAAQ,kBAAkB,UAAU;GAAO,CACpD;EACD,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,WAAW,QAAQ,8BAA8B,YAAY,CAAC;EACvG,MAAM,MAAM,IAAI,gBAAgB,KAAK;EACrC,MAAM,IAAI,SAAS,cAAc,IAAI;AACrC,IAAE,OAAO;AACT,IAAE,WAAW;AACb,WAAS,KAAK,YAAY,EAAE;AAC5B,IAAE,OAAO;AACT,WAAS,KAAK,YAAY,EAAE;AAC5B,MAAI,gBAAgB,IAAI;;;CAI1B,SAAS,uBAAuB,MAAgD;AAC9E,SAAO,gBACL,QAAQ,OACR,KAAK,SAAS,cAAc,OAAO,OAAO,QAAQ,MAAM,GAAG,MAC3D,KAAK,SAAS,cAAc,QAAQ,QAAQ,MAC5C;GAAE,GAAG;GAAM,kBAAkB,UAAU;GAAO,CAC/C;;CAKH,MAAM,WAAW,IAAmB,EAAE,CAAC;CACvC,MAAM,cAAc,eAAe,SAAS,MAAM,SAAS,EAAE;CAC7D,MAAM,oBAAoB,eAAe,QAAQ,MAAM;CACvD,MAAM,qBAAqB,eAAe,UAAU,MAAM,IAAI,UAAU,EAAE;CAC1E,MAAM,gBAAgB,eAAe;AACnC,MAAI,UAAU,MAAM,WAAW,EAAG,QAAO;AACzC,SAAO,KAAK,IAAI,GAAG,UAAU,MAAM,KAAI,MAAK,EAAE,OAAO,CAAC;GACtD;CACF,MAAM,SAAS,eAAe,aAAa,OAAO,WAAW,EAAE,CAAC;CAChE,MAAM,aAAa,eAAuC;EACxD,MAAM,MAA8B,EAAE;AACtC,OAAK,MAAM,KAAK,OAAO,MAAO,KAAI,EAAE,SAAS,EAAE,eAAe,EAAE;AAChE,SAAO;GACP;CACF,MAAM,gBAAgB,eACpB,aAAa,QAAQ,IAAI,IAAI,aAAa,MAAM,QAAQ,mBAAG,IAAI,KAAK,CACrE;CACD,MAAM,mBAAmB;CACzB,MAAM,gBAAgB,eACd,OAAO,MAAM,SAAS,KAAK,OAAO,MAAM,OAAM,MAAK,EAAE,QAAQ,WAAW,EAAE,CACjF;CAED,SAAS,iBAAiB,GAAW,IAAmB;AACtD,MAAI,CAAC;OACC,OAAO,YAAY,eAAA,QAAA,IAAA,aAAyC,cAAc;AAC5E,YAAQ,KACN,+KAED;AACD,+BAA2B;;;;CAKjC,SAAS,qBAAqB,IAAmB;AAC/C,MAAI,CAAC;OACC,OAAO,YAAY,eAAA,QAAA,IAAA,aAAyC,cAAc;AAC5E,YAAQ,KACN,mLAED;AACD,+BAA2B;;;;CAKjC,SAAS,YAAY,OAAe;AAClC,iBAAe,cAAc;AAC7B,MAAI,eAAe,MAAO,eAAc,eAAe,OAAO,MAAM;;CAGtE,SAAS,YAAY,OAAe,MAAc;AAChD,iBAAe,cAAc;AAC7B,MAAI,eAAe,MAAO,eAAc,eAAe,OAAO,OAAO,KAAK;;CAG5E,SAAS,mBAAmB,SAA2C;EACrE,MAAM,SAAS,6BAA6B,QAAQ;AACpD,MAAI,CAAC,OAAQ,QAAO;AACpB,YAAU,QAAQ;AAClB,UAAQ,QAAQ;AAChB,kBAAgB,QAAQ,OAAO;AAC/B,cAAY;AACZ,SAAO;;CAGT,SAAS,QAAQ;AACf,UAAQ,QAAQ;AAChB,UAAQ,QAAQ;AAChB,UAAQ,QAAQ,EAAE;AAClB,UAAQ,QAAQ,EAAE;AAClB,iBAAe,QAAQ;AACvB,YAAU,QAAQ,EAAE;AACpB,WAAS,QAAQ,EAAE;AACnB,kBAAgB,QAAQ,EAAE;;AAG5B,QAAO;EAEL;EAAW;EAAS;EAAS;EAC7B;EAAS;EAAS;EAClB;EAAW;EAAoB;EAE/B;EAAS;EAAW;EAAc;EAClC;EAAQ;EAAU;EAAU;EAAiB;EAAQ;EACrD;EAAQ;EAAY;EAAe;EACnC;EAAa;EAAU;EAAmB;EAC1C;EAEA;EAAqB;EAAsB;EAAe;EAAS;EAAe;EAAa;EAC/F;EAAc;EAEd;EAAY;EAAkB;EAAwB;EAAoB;EAE1E;EAAkB;EAAsB;EAAa;EACtD;;AAGH,SAAgB,6BACd,SACsE;CACtE,MAAM,aAAa,2BAA2B,QAAQ;AACtD,KAAI,CAAC,WAAY,QAAO;CACxB,MAAM,UAAU,WAAW;AAC3B,KAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,WAAW,EAAG,QAAO;CAC5D,MAAM,mBAA6B,EAAE;CACrC,MAAM,yBAAS,IAAI,KAAa;CAChC,MAAM,kBAA0C,EAAE;AAClD,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,UAAW,OAAmC;AACpD,kBAAgB,KAAK,OAAO,YAAY,WAAW,QAAQ,aAAa,GAAG,KAAA,EAAU;EACrF,MAAM,aAAc,OAAmC;AACvD,MAAI,cAAc,OAAO,eAAe;QACjC,MAAM,OAAO,OAAO,KAAK,WAAW,CACvC,KAAI,CAAC,OAAO,IAAI,IAAI,EAAE;AACpB,WAAO,IAAI,IAAI;AACf,qBAAiB,KAAK,IAAI;;;;AAKlC,KAAI,iBAAiB,WAAW,EAAG,QAAO;AAW1C,QAAO;EAAE,SAVO;GAAC;GAAe,GAAG;GAAkB;GAAc;EAUjD,MATJ,QAAsC,KAAI,MAAK;GAC3D,MAAM,aAAc,EAAE,cAAyC,EAAE;GACjE,MAAM,MAA8B;IAClC,aAAa,OAAO,EAAE,eAAe,GAAG;IACxC,aAAa,OAAO,EAAE,eAAe,SAAS;IAC/C;AACD,QAAK,MAAM,OAAO,iBAAkB,KAAI,OAAO,WAAW,QAAQ;AAClE,UAAO;IACP;EACsB,cAAc;EAAe,WAAW;EAAK;EAAiB;;;;ACjexF,IAAM,SAAS;AAEf,SAAS,MAAM,GAAW,KAAa,KAAqB;AAC1D,QAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,EAAE,CAAC;;AAGxC,SAAgB,SAAS,KAAkB;CACzC,MAAM,QAAQ,OAAO,KAAK,IAAI,MAAM,CAAC;AACrC,KAAI,CAAC,MAAO,QAAO;EAAE,GAAG;EAAG,GAAG;EAAG,GAAG;EAAI;CAExC,IAAI,MAAM,MAAM;AAChB,KAAI,IAAI,WAAW,EAAG,OAAM,IAAI,MAAM,GAAG,CAAC,KAAI,MAAK,IAAI,EAAE,CAAC,KAAK,GAAG;CAElE,MAAM,IAAI,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,GAAG;CAC1C,MAAM,IAAI,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,GAAG;CAC1C,MAAM,IAAI,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,GAAG;CAE1C,MAAM,MAAM,KAAK,IAAI,GAAG,GAAG,EAAE;CAC7B,MAAM,MAAM,KAAK,IAAI,GAAG,GAAG,EAAE;CAC7B,MAAM,QAAQ,MAAM;CAEpB,IAAI,IAAI;AACR,KAAI,UAAU,EACZ,KAAI,QAAQ,EAAG,MAAM,IAAI,KAAK,QAAS;UAC9B,QAAQ,EAAG,MAAK,IAAI,KAAK,QAAQ;KACrC,MAAK,IAAI,KAAK,QAAQ;AAE7B,MAAK,IAAI,KAAK,OAAO;CAErB,MAAM,KAAK,MAAM,OAAO;CACxB,MAAM,IAAI,UAAU,IAAI,IAAI,SAAS,IAAI,KAAK,IAAI,IAAI,IAAI,EAAE;AAE5D,QAAO;EAAE;EAAG,GAAG,IAAI;EAAK,GAAG,IAAI;EAAK;;AAGtC,SAAgB,SAAS,GAAW,GAAW,GAAmB;CAChE,MAAM,QAAQ,MAAM,GAAG,GAAG,IAAI,GAAG;CACjC,MAAM,QAAQ,MAAM,GAAG,GAAG,IAAI,GAAG;CACjC,MAAM,SAAU,IAAI,MAAO,OAAO;CAElC,MAAM,KAAK,IAAI,KAAK,IAAI,IAAI,QAAQ,EAAE,IAAI;CAC1C,MAAM,IAAI,KAAK,IAAI,KAAK,IAAM,QAAQ,KAAM,IAAK,EAAE;CACnD,MAAM,IAAI,QAAQ,IAAI;CAEtB,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI;AACtB,KAAI,QAAQ,GAAI,EAAC,GAAG,GAAG,KAAK;EAAC;EAAG;EAAG;EAAE;UAC5B,QAAQ,IAAK,EAAC,GAAG,GAAG,KAAK;EAAC;EAAG;EAAG;EAAE;UAClC,QAAQ,IAAK,EAAC,GAAG,GAAG,KAAK;EAAC;EAAG;EAAG;EAAE;UAClC,QAAQ,IAAK,EAAC,GAAG,GAAG,KAAK;EAAC;EAAG;EAAG;EAAE;UAClC,QAAQ,IAAK,EAAC,GAAG,GAAG,KAAK;EAAC;EAAG;EAAG;EAAE;KACtC,EAAC,GAAG,GAAG,KAAK;EAAC;EAAG;EAAG;EAAE;CAE1B,MAAM,SAAS,MAAc,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI;AACpF,QAAO,IAAI,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE;;;;;;;;;;;;AAa3C,SAAgB,YAAY,WAAmB,OAAe,OAAuB;AACnF,KAAI,SAAS,EAAG,QAAO;CACvB,MAAM,EAAE,GAAG,GAAG,MAAM,SAAS,UAAU;CAEvC,MAAM,QAAQ;CACd,MAAM,QAAQ;CACd,MAAM,SAAS,KAAK,IAAI,QAAQ,OAAO,GAAG;CAC1C,MAAM,SAAS,MAAM,GAAG,QAAQ,SAAS,GAAG,QAAQ,SAAS,EAAE;CAE/D,MAAM,MAAM,SAAS,SAAS;CAC9B,MAAM,MAAM,SAAS,SAAS;CAE9B,MAAM,SAAS,OADL,UAAU,IAAI,KAAM,SAAS,QAAQ,OACrB,MAAM;AAGhC,QAAO,SAAS,GADD,MAAM,GAAG,IAAI,IAAI,EACL,OAAO;;;;;ACpDpC,SAAgB,gBAAgB,SAAwD;CACtF,MAAM,SAAS,eAAe,CAAC,GAAG,QAAQ,QAAQ,OAAO,CAAC,CAAC;CAC3D,MAAM,UAAU,eAAe,CAAC,GAAI,QAAQ,QAAQ,QAAQ,IAAI,EAAE,CAAE,CAAC;CACrE,MAAM,gBAAgB,eAAe,QAAQ,QAAQ,cAAc,IAAI,UAAU;CACjF,MAAM,YAAY,eAAe;EAC/B,MAAM,WAAW,QAAQ,QAAQ,UAAU;AAC3C,MAAI,SAAU,QAAO;AACrB,SAAO,OAAO,MAAM,MAAK,UAAS,MAAM,KAAK,SAAS,IAAI,CAAC,GAAG,MAAM;GACpE;CAEF,MAAM,qBAAqB,eAAmC;AAC5D,MAAI,OAAO,MAAM,WAAW,EAAG,QAAO,EAAE;EACxC,MAAM,iCAAiB,IAAI,KAA4B;AAEvD,OAAK,MAAM,SAAS,OAAO,OAAO;GAChC,MAAM,YAAY,kBAAkB,MAAM,MAAM,UAAU,MAAM;GAChE,MAAM,WAAW,eAAe,IAAI,UAAU;AAC9C,OAAI,SACF,UAAS,KAAK,MAAM;OAEpB,gBAAe,IAAI,WAAW,CAAC,MAAM,CAAC;;AAI1C,SAAO,CAAC,GAAG,eAAe,SAAS,CAAC,CAAC,KAAK,CAAC,WAAW,eAAe;GACnE,MAAM,QAAQ,UAAU,IAAI,SAAS,cAAc;AAWnD,UAAO;IACL,MAAM;IACN;IACA,WAbkB,UAAU,KAAK,UAAU,UAAU;KACrD,MAAM,eAAe,YAAY,OAAO,OAAO,UAAU,OAAO;AAChE,YAAO;MACL,GAAG;MACH;MACA,WAAW,eAAe;MAC1B,eAAe,eAAe;MAC/B;MACD;IAMA,YAAY,UAAU,SAAQ,UAAS,MAAM,QAAQ;IACtD;IACD;GACF;CAEF,MAAM,gBAAgB,eACpB,mBAAmB,MAAM,MAAK,UAC5B,MAAM,UAAU,SAAS,KACxB,MAAM,UAAU,WAAW,KAAK,MAAM,SAAS,MAAM,UAAU,GAAG,KACpE,CACF;CAED,MAAM,iBAAiB,eACrB,IAAI,IAAI,OAAO,MAAM,SAAQ,UAAS,MAAM,QAAQ,CAAC,CACtD;CAED,MAAM,mBAAmB,eACvB,QAAQ,MAAM,QAAO,WAAU,CAAC,eAAe,MAAM,IAAI,OAAO,CAAC,CAClE;CAED,SAAS,UAAU,WAA4C;AAC7D,SAAO,OAAO,MAAM,MAAK,UAAS,MAAM,SAAS,UAAU;;CAG7D,SAAS,cAAc,WAA2B;AAChD,SAAO,UAAU,UAAU,EAAE,SAAS,cAAc;;AAGtD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,kBAAkB,WAAmB,WAA2B;CACvE,MAAM,QAAQ,UAAU,MAAM,UAAU;AACxC,QAAO,MAAM,SAAS,IAAI,MAAM,KAAK;;;;;AChGvC,SAAgB,gBAAgB,UAAkC,EAAE,EAAyB;CAC3F,MAAM,cAAc,IAAI,IAAI,IAAI,aAAa,QAAQ,QAAQ,WAAW,CAAC,CAAC,CAAC;CAC3E,MAAM,eAAe,eAAe,CAAC,GAAG,YAAY,MAAM,CAAC;AAE3D,KAAI,QAAQ,cAAc,KAAA,EACxB,aACQ,QAAQ,QAAQ,UAAU,GAC/B,oBAAoB;AACnB,MAAI,oBAAoB,KAAA,KAAa,oBAAoB,KAAM;AAC/D,MAAI,gBACF,aAAY,aAAa,QAAQ,QAAQ,OAAO,CAAC,CAAC;MAElD,QAAO;IAGX,EAAE,WAAW,MAAM,CACpB;AAGH,KAAI,QAAQ,WAAW,KAAA,EACrB,aACQ,aAAa,QAAQ,QAAQ,OAAO,CAAC,GAC1C,QAAQ;AACP,MAAI,QAAQ,QAAQ,UAAU,CAC5B,aAAY,IAAI;GAGrB;CAGH,SAAS,WAAW,IAAqB;AACvC,SAAO,YAAY,MAAM,IAAI,GAAG;;CAGlC,SAAS,OAAO,IAAY;AAC1B,MAAI,YAAY,MAAM,IAAI,GAAG,CAAE;AAC/B,cAAY,QAAQ,IAAI,IAAI,CAAC,GAAG,YAAY,OAAO,GAAG,CAAC;;CAGzD,SAAS,SAAS,IAAY;AAC5B,MAAI,CAAC,YAAY,MAAM,IAAI,GAAG,CAAE;EAChC,MAAM,OAAO,IAAI,IAAI,YAAY,MAAM;AACvC,OAAK,OAAO,GAAG;AACf,cAAY,QAAQ;;CAGtB,SAAS,OAAO,IAAqB;AACnC,MAAI,YAAY,MAAM,IAAI,GAAG,EAAE;AAC7B,YAAS,GAAG;AACZ,UAAO;;AAET,SAAO,GAAG;AACV,SAAO;;CAGT,SAAS,WAAW,KAAwB;AAC1C,cAAY,QAAQ,IAAI,IAAI,CAAC,GAAG,YAAY,OAAO,GAAG,aAAa,IAAI,CAAC,CAAC;;CAG3E,SAAS,YAAY,KAAwB;AAC3C,cAAY,QAAQ,IAAI,IAAI,aAAa,IAAI,CAAC;;CAGhD,SAAS,cAAc;AACrB,cAAY,wBAAQ,IAAI,KAAK;;CAG/B,SAAS,QAAQ;AACf,cAAY,aAAa,QAAQ,QAAQ,WAAW,CAAC,CAAC;;AAGxD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,aAAa,KAAqD;AACzE,KAAI,CAAC,IAAK,QAAO,EAAE;AACnB,QAAO,CAAC,GAAG,IAAI,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC;;;;AC1G1C,SAAgB,6BAA0D;AACxE,KAAI,OAAO,WAAW,YAAa,QAAO,KAAA;AAC1C,QAAQ,OAA8D;;AAGxE,SAAgB,+BAA2E;AACzF,QAAO,4BAA4B;;AAGrC,SAAgB,6BACd,UAAsD,8BAA8B,EAC3D;CACzB,MAAM,YAAY,SAAS,qBAAqB,SAAS;AACzD,QAAO,aAAa,OAAO,cAAc,WACrC,YACA,KAAA;;AAGN,SAAgB,+BACd,UAAsD,8BAA8B,EAChE;AACpB,QAAO,kBACL,SAAS,uBACT,SAAS,gBACT,SAAS,qBACT,SAAS,WACV;;AAGH,SAAgB,6BAAiD;AAC/D,KAAI,OAAO,WAAW,YAAa,QAAO,KAAA;CAC1C,MAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,OAAO;AAC1D,QAAO,kBAAkB,OAAO,IAAI,eAAe,IAAI,OAAO,IAAI,gBAAgB,CAAC,IAC9E,qBAAqB,OAAO,SAAS,SAAS,IAC9C,qBAAqB,OAAO,SAAS,KAAK,QAAQ,SAAS,IAAI,CAAC;;AAGvE,SAAgB,2BACd,UAAsD,8BAA8B,EAChE;AACpB,QAAO,+BAA+B,QAAQ,IAAI,4BAA4B;;AAGhF,SAAgB,kBAAkB,OAAoC;AACpE,KAAI,OAAO,UAAU,YAAY,OAAO,SAAS,MAAM,CAAE,QAAO;AAChE,KAAI,OAAO,UAAU,YAAY,MAAM,MAAM,EAAE;EAC7C,MAAM,SAAS,OAAO,MAAM;AAC5B,SAAO,OAAO,SAAS,OAAO,GAAG,SAAS,KAAA;;AAE5C,KAAI,SAAS,OAAO,UAAU,YAAY,QAAQ,MAChD,QAAO,kBAAmB,MAA2B,GAAG;;AAK5D,SAAS,qBAAqB,MAAkC;CAC9D,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;AAChD,MAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG;EAC3D,MAAM,UAAU,SAAS,QAAQ,aAAa;AAC9C,MAAI,YAAY,gBAAgB,YAAY,cAC1C,QAAO,kBAAkB,mBAAmB,SAAS,QAAQ,MAAM,GAAG,CAAC;;;;;;ACxB7E,SAAgB,kBACd,UAAoC,EAAE,EACb;CACzB,MAAM,OAAO,WAA2C,KAAK;CAC7D,MAAM,UAAU,oBAAoB,kCAAkC;CACtE,MAAM,YAAY,QAAQ;CAC1B,MAAM,QAAQ,QAAQ;CACtB,MAAM,eAAe,QAAQ;CAC7B,IAAI,mBAAkC;CACtC,IAAI,MAA2B;CAE/B,SAAS,SAAuB;AAC9B,UAAQ,OAAO,EAAE,SAAS,QAAQ,YAAY,CAAC;AAC/C,SAAO;;CAGT,MAAM,aAAa,eACjB,2BAA2B,KAAK,MAAM,CACvC;CAED,MAAM,cAAc,eAClB,iCAAiC,WAAW,MAAM,CACnD;CAED,MAAM,gBAAgB,eACpB,mCAAmC,WAAW,MAAM,CACrD;CAED,MAAM,WAAW,eAA2B;AAC1C,MAAI,CAAC,WAAW,MAAO,QAAO,EAAE;EAChC,MAAM,OAAO,WAAW,MAAM,aAAa,WAAW,MAAM;AAC5D,SAAO,MAAM,QAAQ,KAAK,GAAG,OAAqB,EAAE;GACpD;CAEF,MAAM,YAAY,eAA0C;AAC1D,MAAI,CAAC,WAAW,MAAO,QAAO,EAAE;EAChC,MAAM,QAAQ,WAAW,MAAM,cAAc,WAAW,MAAM;AAC9D,SAAO,MAAM,QAAQ,MAAM,GAAG,QAAqC,EAAE;GACrE;CAEF,MAAM,cAAc,eAAmC;AACrD,MAAI,CAAC,WAAW,MAAO,QAAO;EAC9B,MAAM,UAAU,WAAW,MAAM,gBAAgB,WAAW,MAAM;AAClE,MAAI,WAAW,OAAO,YAAY,YAAY,cAAe,QAC3D,QAAO;AAET,SAAO;GACP;CAEF,eAAe,UAAU,cAA+D;AACtF,qBAAmB;AACnB,MAAI;AACF,QAAK,QAAQ,MAAM,QAAQ,UACnB,QAAQ,CAAC,IAA6B,gBAAgB,aAAa,OAAO,EAChF;IAAE,SAAS;IAAQ,cAAc;IAAmC,CACrE;AACD,UAAO,KAAK;UACN;AACN,QAAK,QAAQ;AACb,UAAO;;;CAIX,eAAe,UAAyB;AACtC,MAAI,qBAAqB,KACvB,OAAM,UAAU,iBAAiB;;CAIrC,SAAS,QAAc;AACrB,OAAK,QAAQ;AACb,qBAAmB;AACnB,UAAQ,YAAY;;AAGtB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;EACD;;;;;ACzEH,SAAgB,qBACd,UAAuC,EAAE,EACb;CAC5B,MAAM,gBAAgB,QAAQ,sBAAsB,QAChD,KAAA,IACA,OAAO,oBAAoB,KAAA,EAAU;CACzC,MAAM,iBAAiB,kBAAkB,EAAE,YAAY,QAAQ,YAAY,CAAC;CAE5E,MAAM,eAAe,eAAmC;AAEtD,SADmB,kBAAkB,QAAQ,QAAQ,aAAa,CAAC,IAC9C,kBAAkB,eAAe,aAAa,MAAM;GACzE;CAEF,MAAM,qBAAqB,eACzB,2BAA2B,QAAQ,QAAQ,WAAW,IAAI,KAAK,CAChE;CAED,MAAM,aAAa,eACjB,mBAAmB,SAAS,eAAe,WAAW,MACvD;CAED,MAAM,iBAAiB,gBAAiD,EACtE,iBAAiB,QAAQ,iBAC1B,EAAE;CAEH,MAAM,UAAU,eACd,iCAAiC,WAAW,OAAO,eAAe,MAAM,CACzE;CAED,MAAM,gBAAgB,eACpB,mCAAmC,WAAW,OAAO,eAAe,MAAM,CAC3E;CAED,MAAM,UAAU,eAAe,QAAQ,QAAQ,QAAQ,IAAI,KAAK;CAEhE,eAAe,aACb,KAAyC,aAAa,OACb;EACzC,MAAM,WAAW,kBAAkB,GAAG;AACtC,MAAI,aAAa,KAAA,GAAW;AAC1B,kBAAe,OAAO;AACtB,UAAO;;AAET,SAAO,eAAe,MAAM,SAAS;;AAGvC,cACS;EACL,YAAY,mBAAmB;EAC/B,SAAS,QAAQ;EACjB,cAAc,aAAa;EAC5B,IACA,EAAE,YAAY,mBAAmB,SAAS,WAAW,cAAc,0BAA0B;AAC5F,MAAI,QAAQ,cAAc,SAAS,CAAC,UAAW;AAC/C,MAAI,mBAAmB;AACrB,kBAAe,OAAO;AACtB;;AAEF,MAAI,wBAAwB,KAAA,GAAW;AACrC,kBAAe,OAAO;AACtB;;AAEG,iBAAe,MAAM,oBAAoB;IAEhD,EAAE,WAAW,QAAQ,cAAc,OAAO,CAC3C;AAED,QAAO;EACL;EACA,MAAM,eAAe;EACrB;EACA;EACA;EACA,WAAW,eAAe;EAC1B,OAAO,eAAe;EACtB,cAAc,eAAe;EAC7B,OAAO;EACP,SAAS,eAAe;EACxB,OAAO,eAAe;EACvB;;;;;ACzGH,SAAgB,gBAAgB,SAAiC;CAC/D,MAAM,aAAa,IAAI,MAAM;CAC7B,MAAM,YAAY,IAAsB,KAAK;CAE7C,MAAM,QAAQ,eAAkC;AAC9C,MAAI,CAAC,UAAU,MAAO,QAAO;EAC7B,MAAM,QAAQ,UAAU;EACxB,MAAM,QAAQ,QAAQ,WAAW;EACjC,MAAM,UAAU,QAAQ,aAAa;EACrC,MAAM,WAAW,QAAQ,aAAa,QAAQ;AAE9C,MAAI,MAAM,SAAS,UAAU;GAC3B,MAAM,SAAS,MAAM,WAAW,MAAM;GACtC,MAAM,cAAc,MAAM;GAC1B,MAAM,YAAY,cAAc;GAEhC,MAAM,QAAQ,KAAK,IAAI,aAAa,UAAU;GAC9C,MAAM,WAAW,KAAK,IAAI,aAAa,UAAU;GAEjD,MAAM,eAAe,WAAW,KAAK,MAAO,QAAQ,QAAS,QAAQ;GACrE,MAAM,aAAa,WAAW,KAAK,MAAO,WAAW,QAAS,QAAQ;GAEtE,MAAM,eAAe,YAAY,cAAc,QAAQ;GACvD,MAAM,aAAa,KAAK,IAAI,YAAY,YAAY,QAAQ,EAAE,eAAe,QAAQ;GAErF,MAAM,gBAAiB,eAAe,YAAY,UAAW;GAC7D,MAAM,mBAAoB,aAAa,gBAAgB,UAAW;AAElE,UAAO;IACL,OAAO,cAAc,MAAM,WAAW,aAAa;IACnD,KAAK,cAAc,MAAM,WAAW,WAAW;IAC/C,UAAU,MAAM;IAChB,OAAO;KACL,KAAK,GAAG,aAAa;KACrB,QAAQ,GAAG,gBAAgB;KAC5B;IACF;;AAGH,MAAI,MAAM,SAAS,UAAU,MAAM,OAAO;GACxC,MAAM,SAAS,MAAM,WAAW,MAAM;GACtC,MAAM,aAAa,cAAc,IAAI,KAAK,MAAM,MAAM,MAAM,CAAC;GAE7D,MAAM,WADW,cAAc,IAAI,KAAK,MAAM,MAAM,IAAI,CAAC,GAC7B;GAE5B,MAAM,WAAW,YAAY,aADV,KAAK,MAAO,SAAS,QAAS,QAAQ,EACH,QAAQ;GAG9D,MAAM,UAAU,WAAW,UAFZ,WAAW,UAEmB,UAAU,QAAQ,WAAW,QAAQ,GAAG;GACrF,MAAM,SAAU,QAAQ,QAAQ,YAAY,UAAW;GACvD,MAAM,YAAa,QAAQ,MAAM,QAAQ,SAAS,UAAW;AAE7D,UAAO;IACL,OAAO,cAAc,MAAM,WAAW,QAAQ,MAAM;IACpD,KAAK,cAAc,MAAM,WAAW,QAAQ,IAAI;IAChD,UAAU,MAAM;IAChB,OAAO;KACL,KAAK,GAAG,MAAM;KACd,QAAQ,GAAG,SAAS;KACrB;IACF;;AAGH,OAAK,MAAM,SAAS,gBAAgB,MAAM,SAAS,oBAAoB,MAAM,OAAO;GAClF,MAAM,aAAa,cAAc,IAAI,KAAK,MAAM,MAAM,MAAM,CAAC;GAC7D,MAAM,WAAW,cAAc,IAAI,KAAK,MAAM,MAAM,IAAI,CAAC;GACzD,MAAM,SAAS,MAAM,WAAW,MAAM;GACtC,MAAM,aAAa,KAAK,MAAO,SAAS,QAAS,QAAQ;GAEzD,IAAI,WAAW;GACf,IAAI,SAAS;AAEb,OAAI,MAAM,SAAS,cAAc;AAC/B,eAAW,YAAY,aAAa,YAAY,QAAQ;AACxD,eAAW,KAAK,IAAI,UAAU,SAAS,QAAQ;UAC1C;AACL,aAAS,YAAY,WAAW,YAAY,QAAQ;AACpD,aAAS,KAAK,IAAI,QAAQ,WAAW,QAAQ;;GAG/C,MAAM,UAAU,WAAW,UAAU,QAAQ,UAAU,QAAQ,WAAW,QAAQ,GAAG;GACrF,MAAM,SAAU,QAAQ,QAAQ,YAAY,UAAW;GACvD,MAAM,YAAa,QAAQ,MAAM,QAAQ,SAAS,UAAW;AAE7D,UAAO;IACL,OAAO,cAAc,MAAM,WAAW,QAAQ,MAAM;IACpD,KAAK,cAAc,MAAM,WAAW,QAAQ,IAAI;IAChD,UAAU,MAAM;IAChB,OAAO;KACL,KAAK,GAAG,MAAM;KACd,QAAQ,GAAG,SAAS;KACrB;IACF;;AAGH,SAAO;GACP;CAEF,SAAS,YAAY,MAAY,GAAW,UAAkB;AAC5D,MAAI,QAAQ,SAAS,MAAO;AAC5B,aAAW,QAAQ;AACnB,YAAU,QAAQ;GAChB,MAAM;GACN,WAAW;GACX,QAAQ;GACR,UAAU;GACV;GACA,iBAAiB;GAClB;AACD,gBAAc;;CAGhB,SAAS,UAAU,OAAsB,GAAW,UAAkB;AACpE,MAAI,QAAQ,SAAS,SAAS,MAAM,cAAc,MAAO;AACzD,aAAW,QAAQ;AACnB,YAAU,QAAQ;GAChB,MAAM;GACN;GACA,WAAW,IAAI,KAAK,MAAM,MAAM;GAChC,QAAQ;GACR,UAAU;GACV;GACA,iBAAiB;GAClB;AACD,gBAAc;;CAGhB,SAAS,YAAY,OAAsB,MAAwB,GAAW,UAAkB;AAC9F,MAAI,QAAQ,SAAS,SAAS,MAAM,cAAc,MAAO;AACzD,aAAW,QAAQ;AACnB,YAAU,QAAQ;GAChB,MAAM,SAAS,QAAQ,eAAe;GACtC;GACA,WAAW,IAAI,KAAK,MAAM,MAAM;GAChC,QAAQ;GACR,UAAU;GACV;GACA,iBAAiB;GAClB;AACD,gBAAc;;CAGhB,SAAS,cAAc,GAAiB;AACtC,MAAI,CAAC,UAAU,MAAO;AACtB,YAAU,QAAQ;GAAE,GAAG,UAAU;GAAO,UAAU,EAAE;GAAS;;CAG/D,SAAS,cAAc;AACrB,MAAI,CAAC,UAAU,SAAS,CAAC,MAAM,OAAO;AACpC,YAAS;AACT;;EAGF,MAAM,IAAI,MAAM;EAChB,MAAM,QAAQ,UAAU;AAExB,MAAI,MAAM,SAAS,YAAY,QAAQ,iBACrC,SAAQ,iBAAiB,EAAE,OAAO,EAAE,IAAI;WAC/B,MAAM,SAAS,UAAU,MAAM,SAAS,QAAQ,eACzD,SAAQ,eAAe,MAAM,OAAO,EAAE,OAAO,EAAE,IAAI;YACzC,MAAM,SAAS,gBAAgB,MAAM,SAAS,oBAAoB,MAAM,SAAS,QAAQ,iBACnG,SAAQ,iBAAiB,MAAM,OAAO,EAAE,OAAO,EAAE,IAAI;AAGvD,WAAS;;CAGX,SAAS,eAAe;AACtB,WAAS,iBAAiB,eAAe,cAAc;AACvD,WAAS,iBAAiB,aAAa,YAAY;;CAGrD,SAAS,UAAU;AACjB,aAAW,QAAQ;AACnB,YAAU,QAAQ;AAClB,WAAS,oBAAoB,eAAe,cAAc;AAC1D,WAAS,oBAAoB,aAAa,YAAY;;AAGxD,aAAY,QAAQ;AAEpB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,YAAY,SAAiB,MAAsB;AAC1D,QAAO,KAAK,MAAM,UAAU,KAAK,GAAG;;AAGtC,SAAS,cAAc,MAAoB;AACzC,QAAO,KAAK,UAAU,GAAG,KAAK,KAAK,YAAY;;AAGjD,SAAS,cAAc,UAAgB,SAAuB;CAC5D,MAAM,IAAI,IAAI,KAAK,SAAS;AAC5B,GAAE,SAAS,KAAK,MAAM,UAAU,GAAG,EAAE,UAAU,IAAI,GAAG,EAAE;AACxD,QAAO;;AAGT,SAAS,WAAW,OAAe,KAAa,KAAa,KAAa;CACxE,MAAM,IAAI,KAAK,IAAI,OAAO,IAAI;CAC9B,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI;AAC5B,QAAO;EAAE,OAAO;EAAG,KAAK,KAAK,IAAI,GAAG,EAAE;EAAE;;;;;AC1J1C,SAAgB,kBACd,UAAoC,EAAE,EACb;CACzB,MAAM,kBAAkB,4BAA4B;CACpD,MAAM,MAAM,OAAO,EAAE,SAAS,QAAQ,cAAc,iBAAiB,gBAAgB,CAAC;CACtF,MAAM,WAAW,QAAQ,YAAY,iBAAiB,QAAQ;CAC9D,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,sBAAsB,eAAmC;AAC7D,SAAO,2BAA2B,gBAAgB;GAClD;CACF,MAAM,uBAAuB,eAAe,oBAAoB,UAAU,KAAA,EAAU;CAEpF,MAAM,UAAU,oBAAoB,6BAA6B;CACjE,MAAM,WAAW,IAAI,MAAM;CAC3B,MAAM,YAAY,IAAI,MAAM;CAC5B,MAAM,QAAQ,QAAQ;CACtB,MAAM,eAAe,QAAQ;CAC7B,MAAM,cAAc,QAAQ;CAE5B,SAAS,6BAAqC;EAC5C,MAAM,KAAK,oBAAoB;AAC/B,MAAI,OAAO,KAAA,EACT,OAAM,IAAI,MAAM,gDAAgD;AAElE,SAAO;;CAGT,SAAS,2BAA2B,QAAoC;EACtE,MAAM,KAAK,oBAAoB;AAC/B,MAAI,OAAO,KAAA,GAAW;AACpB,WAAQ,SAAS,yCAAyC,SAAS;AACnE;;AAEF,SAAO;;CAGT,eAAe,WACb,cACA,MACkB;AAClB,MAAI,CAAC,UAAU;AACb,WAAQ,SAAS,sCAAsC;AACvD,UAAO;;AAET,WAAS,QAAQ;AACjB,MAAI;AACF,SAAM,QAAQ,IAAI,YAAY;AAC5B,UAAM,IAAI,IAAI,gBAAgB,aAAa,QAAQ;KACjD,WAAW;KACX;KACA,gBAAgB;KACjB,CAAC;MACD,EAAE,SAAS,QAAQ,CAAC;AACvB,UAAO;UACD;AACN,UAAO;YACC;AACR,YAAS,QAAQ;;;CAIrB,eAAe,kBAAkB,MAAiD;EAChF,MAAM,eAAe,2BAA2B,aAAa;AAC7D,SAAO,iBAAiB,KAAA,IAAY,QAAQ,WAAW,cAAc,KAAK;;CAG5E,eAAe,mBACb,cACA,MACA,gBAAiD,EAAE,EACjC;AAElB,SAAO,WAAW,cADC,kCAAkC,MAAM,cAAc,CACQ;;CAGnF,eAAe,0BACb,MACA,gBAAiD,EAAE,EACjC;EAClB,MAAM,eAAe,2BAA2B,qBAAqB;AACrE,SAAO,iBAAiB,KAAA,IAAY,QAAQ,mBAAmB,cAAc,MAAM,cAAc;;CAGnG,eAAe,aACb,cACA,QACkB;AAClB,MAAI,CAAC,UAAU;AACb,WAAQ,SAAS,wCAAwC;AACzD,UAAO;;AAET,WAAS,QAAQ;AACjB,MAAI;AACF,SAAM,QAAQ,IAAI,YAAY;AAC5B,UAAM,IAAI,IAAI,gBAAgB,aAAa,WAAW,YAAY,EAChE,QACD,CAAC;MACD,EAAE,SAAS,QAAQ,CAAC;AACvB,UAAO;UACD;AACN,UAAO;YACC;AACR,YAAS,QAAQ;;;CAIrB,eAAe,oBAAoB,QAAmD;EACpF,MAAM,eAAe,2BAA2B,eAAe;AAC/D,SAAO,iBAAiB,KAAA,IAAY,QAAQ,aAAa,cAAc,OAAO;;CAGhF,eAAe,KACb,cACA,MACkB;AAClB,OAAK,KAAK,UAAU,KAAK,aAAa,CAAC,UAAU;AAC/C,WAAQ,SAAS,gCAAgC;AACjD,UAAO;;AAET,WAAS,QAAQ;AACjB,MAAI;AACF,SAAM,QAAQ,IAAI,YAAY;AAC5B,QAAI,KAAK,OACP,OAAM,IAAI,IAAI,gBAAgB,aAAa,QAAQ;KACjD,WAAW;KACX,MAAM,KAAK;KACX,gBAAgB;KACjB,CAAC;AAEJ,QAAI,KAAK,SACP,OAAM,IAAI,IAAI,gBAAgB,aAAa,WAAW,YAAY,EAChE,QAAQ,KAAK,UACd,CAAC;MAEH,EAAE,SAAS,QAAQ,CAAC;AACvB,UAAO;UACD;AACN,UAAO;YACC;AACR,YAAS,QAAQ;;;CAIrB,eAAe,WACb,cACyC;AACzC,YAAU,QAAQ;AAClB,MAAI;AACF,UAAO,MAAM,QAAQ,UACb,IAAI,IAA6B,gBAAgB,aAAa,OAAO,EAC3E,EAAE,SAAS,QAAQ,CACpB;UACK;AACN,UAAO;YACC;AACR,aAAU,QAAQ;;;CAItB,eAAe,oBAA6D;EAC1E,MAAM,eAAe,2BAA2B,aAAa;AAC7D,SAAO,iBAAiB,KAAA,IAAY,OAAO,WAAW,aAAa;;CAGrE,eAAe,aACb,cACyC;AACzC,MAAI,CAAC,UAAU;AACb,WAAQ,SAAS,wCAAwC;AACzD,UAAO;;AAET,YAAU,QAAQ;AAClB,MAAI;AACF,UAAO,MAAM,QAAQ,UACb,IAAI,IACR,gBAAgB,aAAa,WAAW,WACzC,EACD,EAAE,SAAS,QAAQ,CACpB;UACK;AACN,UAAO;YACC;AACR,aAAU,QAAQ;;;CAItB,eAAe,sBAA+D;EAC5E,MAAM,eAAe,2BAA2B,eAAe;AAC/D,SAAO,iBAAiB,KAAA,IAAY,OAAO,aAAa,aAAa;;CAGvE,eAAe,aAAa,cAAwC;AAClE,YAAU,QAAQ;AAClB,MAAI;AACF,SAAM,QAAQ,UAAU,IAAI,OAAO,gBAAgB,aAAa,OAAO,CAAC;AACxE,UAAO;UACD;AACN,UAAO;YACC;AACR,aAAU,QAAQ;;;CAItB,eAAe,sBAAwC;EACrD,MAAM,eAAe,2BAA2B,eAAe;AAC/D,SAAO,iBAAiB,KAAA,IAAY,QAAQ,aAAa,aAAa;;CAGxE,eAAe,eAAe,cAAwC;AACpE,MAAI,CAAC,UAAU;AACb,WAAQ,SAAS,0CAA0C;AAC3D,UAAO;;AAET,YAAU,QAAQ;AAClB,MAAI;AACF,SAAM,QAAQ,UAAU,IAAI,OAAO,gBAAgB,aAAa,WAAW,WAAW,CAAC;AACvF,UAAO;UACD;AACN,UAAO;YACC;AACR,aAAU,QAAQ;;;CAItB,eAAe,wBAA0C;EACvD,MAAM,eAAe,2BAA2B,iBAAiB;AACjE,SAAO,iBAAiB,KAAA,IAAY,QAAQ,eAAe,aAAa;;AAG1E,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;AC3QH,SAAgB,sBACd,UAAwC,EAAE,EACb;CAC7B,MAAM,iBAAiB,kBAAkB,QAAQ;CACjD,MAAM,qBAAqB,IAAgC,wBAAwB,QAAQ,CAAC;CAC5F,MAAM,YAAY,eAAe,0BAA0B,mBAAmB,MAAM,CAAC;CACrF,MAAM,WAAW,eAAe,mBAAmB,MAAM,YAAY,EAAE,CAAC;CACxE,MAAM,cAAc,eAAe,OAAO,KAAK,UAAU,MAAM,CAAC;CAChE,MAAM,eAAe,eAAe,YAAY,MAAM,SAAS,EAAE;CAEjE,SAAS,MAAM,OAAgB,UAAgE;AAC7F,qBAAmB,QAAQ,4BAA4B,OAAO,SAAS;AACvE,SAAO,mBAAmB;;CAG5B,SAAS,QAAoC;AAC3C,qBAAmB,QAAQ,wBAAwB,QAAQ;AAC3D,SAAO,mBAAmB;;CAG5B,SAAS,YAAY,UAAoE;EACvF,MAAM,gBAAgB;GACpB,GAAG,UAAU;IACZ,SAAS,cAAc;GACzB;AACD,qBAAmB,QAAQ,iCACzB,OAAO,OAAO,cAAc,EAC5B,mBAAmB,MAAM,SAC1B;AACD,SAAO,mBAAmB;;CAG5B,SAAS,eAAe,YAA6B;AACnD,MAAI,CAAC,UAAU,MAAM,YAAa,QAAO;EACzC,MAAM,gBAAgB,EAAE,GAAG,UAAU,OAAO;AAC5C,SAAO,cAAc;AACrB,qBAAmB,QAAQ,iCACzB,OAAO,OAAO,cAAc,EAC5B,mBAAmB,MAAM,SAC1B;AACD,SAAO;;CAGT,SAAS,YAEP,YAAsC;AACtC,SAAQ,UAAU,MAAM,eAAyC;;CAGnE,SAAS,gBAEP,YAA+B;AAC/B,SAAO,6BAAwC,mBAAmB,OAAO,WAAW;;CAGtF,eAAe,KAAK,cAAkE;EACpF,MAAM,OAAO,MAAM,eAAe,WAAW,aAAa;AAC1D,SAAO,SAAS,OAAO,OAAO,MAAM,KAAK;;CAG3C,eAAe,cAA0D;EACvE,MAAM,OAAO,MAAM,eAAe,mBAAmB;AACrD,SAAO,SAAS,OAAO,OAAO,MAAM,KAAK;;CAG3C,eAAe,KAAK,cAAwC;AAC1D,SAAO,eAAe,WACpB,cACA,mBAAmB,MACpB;;CAGH,eAAe,cAAgC;AAC7C,SAAO,eAAe,kBACpB,mBAAmB,MACpB;;AAGH,QAAO;EACL,qBAAqB,eAAe;EACpC,sBAAsB,eAAe;EACrC;EACA;EACA;EACA;EACA;EACA,WAAW,eAAe;EAC1B,UAAU,eAAe;EACzB,OAAO,eAAe;EACtB,cAAc,eAAe;EAC7B,aAAa,eAAe;EAC5B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,4BAA4B,eAAe;EAC5C;;AAGH,SAAS,wBAAwB,SAAmE;CAClG,MAAM,UAAU,OAAO,QAAQ,YAAY,aAAa,QAAQ,SAAS,GAAG,QAAQ;AACpF,KAAI,YAAY,KAAA,EAAW,QAAO,EAAE,WAAW,EAAE,EAAE;AACnD,QAAO,4BAA4B,QAAQ;;AAG7C,SAAS,4BACP,OACA,UAC4B;AAC5B,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,iCAAiC,OAAO,SAAS;AAG1D,QAAO,iCACL,OAAO,OAAO,0BAA0B,MAAM,CAAC,EAC/C,uBAAuB,MAAM,IAAI,SAClC;;AAGH,SAAS,iCACP,WACA,UAC4B;AAC5B,QAAO,aAAa,KAAA,IAChB,yBAAyB,UAAU,GACnC,yBAAyB,WAAW,SAAS;;AAGnD,SAAS,uBAAuB,OAAqD;AACnF,KACE,OAAO,UAAU,YACd,UAAU,QACV,MAAM,QAAQ,MAAM,IACpB,EAAE,cAAc,OAEnB;CAGF,MAAM,WAAY,MAAiC;AACnD,KACE,OAAO,aAAa,YACjB,aAAa,QACb,CAAC,MAAM,QAAQ,SAAS,CAE3B,QAAO;;;;;ACzMX,SAAgB,uBACd,QACA,UAAmC,EAAE,EACa;CAClD,MAAM,EAAE,eAAe,gBAAgB,iBAAiB,GAAG,kBAAkB;CAC7E,MAAM,WAAW,gCAAgC,QAAQ,cAAsC;AAE/F,KAAI,kBAAkB,KAAA,EAAW,QAAO;AAExC,QAAO;EACL,GAAG;EACH,eAAe;GACb,GAAG,SAAS;GACZ,GAAG;GACJ;EACF;;;;;AC+BH,SAAgB,uCACd,QACsC;AACtC,QAAO,yCAAoC,OAAO;;;AAIpD,SAAgB,6BACd,QACA,WACA,UAAkD,EAAE,EACf;AACrC,QAAO,+BAA0B,QAAQ,WAAW,QAAQ;;;AAI9D,SAAgB,yBACd,QACgC;AAChC,QAAO;EACL,UAAU,gCAAgC,OAAO;EACjD,SAAS,8BAA8B,OAAO;EAC9C,mBAAmB,OAAO,WAAW,WAAW,EAAE,GAAG,+BAA+B,OAAO;EAC3F,uBAAuB,OAAO,WAAW,WAAW,EAAE,GAAG,mCAAmC,OAAO;EACnG,gBAAgB,OAAO,WAAW,WAAW,EAAE,GAAG,4BAA4B,OAAO;EACrF,oBAAoB,OAAO,WAAW,WAAW,EAAE,GAAG,gCAAgC,OAAO;EAC7F,2BAA2B,OAAO,WAAW,WAAW,EAAE,GAAG,uCAAuC,OAAO;EAC3G,UAAU,OAAO,WAAW,WAAW,EAAE,GAAG,+BAA+B,OAAO;EAClF,OAAO,OAAO,WAAW,WAAW,OAAO,4BAA4B,OAAO;EAC9E,oBAAoB,WAAW,YAC7B,OAAO,WAAW,WACd,KAAA,IACA,6BAA6B,QAAQ,WAAW,QAAQ;EAC/D;;;;;ACnBH,SAAgB,wBACd,QACA,UAAmC,EAAE,EACN;CAC/B,MAAM,WAAW,uBAAuB,QAAQ,QAAQ;CACxD,MAAM,YAAY,oBAAoB,SAAS,UAAU,QAAQ;CACjE,MAAM,aAAa,yBAAyB,OAAO;CACnD,MAAM,WAAW,uBAAuB,OAAO,GAAG,EAAE,QAAQ,GAAG;CAC/D,MAAM,WAAyC;EAC7C;EACA,MAAM,UAAU,SAAS;EACzB,SAAS,UAAU,SAAS;EAC5B,QAAQ,UAAU,SAAS;EAC3B,gBAAgB,UAAU,SAAS;EACnC,SAAS,UAAU,SAAS;EAC5B,mBAAmB,WAAW;EAC9B,uBAAuB,WAAW;EAClC,gBAAgB,WAAW;EAC3B,oBAAoB,WAAW;EAC/B,2BAA2B,WAAW;EACtC,mBAAmB,WAAW;EAC9B,gBAAgB,WAAW;EAC3B,mBAAmB,WAAW;EAC/B;AAED,QAAO;EACL;EACA;EACA;EACA,eAAe,SAAS;EACxB;EACA,QAAQ,UAAU;EAClB,YAAY,UAAU;EACtB,MAAM,UAAU;EAChB,SAAS,UAAU;EACnB,QAAQ,UAAU;EAClB,SAAS,UAAU;EACnB,gBAAgB,UAAU;EAC1B;EACA;EACA,mBAAmB,WAAW;EAC9B,uBAAuB,WAAW;EAClC,kBAAkB,WAAW;EAC7B,gBAAgB,WAAW;EAC3B,oBAAoB,WAAW;EAC/B,2BAA2B,WAAW;EACtC,mBAAmB,WAAW;EAC9B,gBAAgB,WAAW;EAC3B,mBAAmB,WAAW;EAC/B;;AAGH,SAAS,uBACP,QACqE;AACrE,QAAO,OAAO,WAAW;;;;;ACzD3B,SAAgB,4BACd,QACA,UAA8C,EAAE,EACb;CACnC,MAAM,OAAO,uBAAuB,OAAO;AAC3C,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,0BAA0B,OAAO,IAAI;CAEvD,MAAM,eAAe;CAErB,MAAM,qBAAqB,sBAAsB;EAC/C,GAAG;EACH,SAAS;EACV,CAAC;CACF,MAAM,aAAa,mBAAmB;CACtC,IAAI;CAEJ,SAAS,gBAAgB,OAAkE;AACzF,kBAAgB,MAAM;AACtB,mBAAiB,aAAa;AAC9B,SAAO,eAAe,UAAU,wBAAwB,MAAM,CAAC;;CAGjE,MAAM,eAAe,WAA0C,gBAAgB,WAAW,MAAM,CAAC;CACjG,MAAM,YAAY,eAAe,aAAa,MAAM;CACpD,MAAM,WAAW,gBAA4C,EAAE,QAAQ,WAAW,OAAO,EAAE;CAC3F,MAAM,OAAO,eAAe,UAAU,MAAM,KAAK;CACjD,MAAM,UAAU,eAAe,UAAU,MAAM,QAAQ;CACvD,MAAM,SAAS,eAAe,UAAU,MAAM,OAAO,MAAM;CAC3D,MAAM,UAAU,eAAe,UAAU,MAAM,QAAQ;CACvD,MAAM,iBAAiB,eAAe,UAAU,MAAM,eAAe;CACrE,MAAM,WAAW,eAAe,UAAU,MAAM,SAAS;CACzD,MAAM,oBAAoB,eAAe,UAAU,MAAM,kBAAkB;CAC3E,MAAM,wBAAwB,eAAe,UAAU,MAAM,sBAAsB;CACnF,MAAM,mBAAmB,eAAe,UAAU,MAAM,iBAAiB;CACzE,MAAM,iBAAiB,eAAe,UAAU,MAAM,eAAe;CACrE,MAAM,qBAAqB,eAAe,UAAU,MAAM,mBAAmB;CAC7E,MAAM,4BAA4B,eAAe,UAAU,MAAM,0BAA0B;CAC3F,MAAM,oBAAoB,eAAe,UAAU,MAAM,kBAAkB;CAC3E,MAAM,iBAAiB,eAAe,UAAU,MAAM,eAAe;AAErE,OAAM,aAAY,UAAS;AACzB,eAAa,QAAQ,gBAAgB,MAAM;GAC3C;AAEF,KAAI,iBAAiB,CACnB,sBAAqB,gBAAgB,MAAM,CAAC;CAG9C,SAAS,0BAAsD;AAC7D,SAAO,gCAAgC,aAAa,KAAK;;CAG3D,SAAS,gBACP,OACA,UAC4B;AAC5B,SAAO,mBAAmB,MAAM,OAAO,SAAS;;CAGlD,SAAS,2BAAuD;AAC9D,SAAO,mBAAmB,MAAM,yBAAyB,CAAC;;AAG5D,QAAO;EACL,QAAQ,aAAa;EACrB,MAAM;EACN;EACA,WAAW,mBAAmB;EAC9B,aAAa,mBAAmB;EAChC,qBAAqB,mBAAmB;EACxC,sBAAsB,mBAAmB;EACzC,SAAS,mBAAmB;EAC5B,QAAQ,mBAAmB;EAC3B,OAAO,mBAAmB;EAC1B,cAAc,mBAAmB;EACjC,aAAa,mBAAmB;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,oBAAoB,WAAW,YAAY,UAAU,MAAM,kBAAkB,WAAW,QAAQ;EAChG;EACA;EACA,MAAM,mBAAmB;EACzB,aAAa,mBAAmB;EAChC,MAAM,mBAAmB;EACzB,aAAa,mBAAmB;EAChC,4BAA4B,mBAAmB;EAChD;;;;;AChEH,SAAgB,8BACd,UACA,UAAgD,EAAE,EACb;CACrC,MAAM,EAAE,gBAAgB,eAAe,GAAG,sBAAsB;CAChE,MAAM,yBAAyB,mBAAmB,gBAAgB,cAAc;CAChF,MAAM,kBAAkB,wBAAwB,UAAU,uBAAuB;CACjF,MAAM,aAAa,eAAe,yBAAyB,SAAS,CAAC;CACrE,MAAM,cAAc,eAAe,WAAW,OAAO,SAAS,iBAAiB,SAAS,CAAC;CACzF,MAAM,WAAW,gBAAgB;CACjC,MAAM,gBAAgB,IAA8B,EAAE,GAAG,SAAS,eAAe,CAAC;CAClF,MAAM,iBAAiB,eAAe,SAAS,QAAQ;CACvD,MAAM,mBAAmB,eAAe,SAAS,UAAU;CAC3D,MAAM,oBAAoB,IAAI,SAAS,YAAY;CACnD,MAAM,qBAAqB,sBAAsB;EAC/C,GAAG;EACH,SAAS;EACV,CAAC;CACF,MAAM,aAAa,mBAAmB;CACtC,MAAM,YAAY,eAAe,wBAAwB,WAAW,OAAO,uBAAuB,CAAC;CACnG,MAAM,WAAW,gBAA4C,EAAE,QAAQ,WAAW,OAAO,EAAE;CAC3F,MAAM,oBAAoB,eAAe,UAAU,MAAM,kBAAkB;CAC3E,MAAM,wBAAwB,eAAe,UAAU,MAAM,sBAAsB;CACnF,MAAM,mBAAmB,eAAe,UAAU,MAAM,iBAAiB;CACzE,MAAM,iBAAiB,eAAe,UAAU,MAAM,eAAe;CACrE,MAAM,qBAAqB,eAAe,UAAU,MAAM,mBAAmB;CAC7E,MAAM,4BAA4B,eAAe,UAAU,MAAM,0BAA0B;CAC3F,MAAM,oBAAoB,eAAe,UAAU,MAAM,kBAAkB;CAC3E,MAAM,iBAAiB,eAAe,UAAU,MAAM,eAAe;CACrE,MAAM,OAAO,SAAS;EACpB,GAAG,SAAS;EACZ,YAAY,cAAc;EAC1B,uBAAuB;EACxB,CAAC;CACF,MAAM,UAAU,SAAS;EACvB,GAAG,SAAS;EACZ,YAAY,kBAAkB;EAC9B,YAAY,cAAc;EAC1B,uBAAuB;EACvB,QAAQ,cAAc;EACtB,mBAAmB;EACpB,CAAC;CACF,MAAM,UAAU,SAAS;EACvB,OAAO,SAAS;EAChB,eAAe,kBAAkB;EACjC,WAAW,SAAyB,qBAAqB,KAAK,GAAG;EAClE,CAAC;CACF,MAAM,iBAAiB,SAAS;EAC9B,cAAc;EACd,gBAAgB;GACd,GAAG,SAAS;GACZ,QAAQ,cAAc;GACvB;EACD,wBAAwB;EACzB,CAAC;CACF,MAAM,cAAc,gBAA+C;EACjE,SAAS,QAAQ;EACjB,eAAe,QAAQ;EACvB,cAAc,QAAQ;EACtB,GAAG;EACJ,EAAE;CAEH,SAAS,kBACP,WACA,SACA;AACA,SAAO,UAAU,MAAM,kBAAkB,WAAW,QAAQ;;CAG9D,MAAM,WAA+C;EACnD;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CAED,SAAS,0BAAsD;AAC7D,SAAO,8CAA8C,UAAU,SAAS,cAAc;;AAGxF,OACE,gBACC,WAAW;AACV,2BAAyB,OAAO;AAChC,qBAAmB,MAAM,8CAA8C,UAAU,OAAO,CAAC;IAE3F,EAAE,MAAM,MAAM,CACf;AAED,OAAM,oBAAoB,WAAW;AACnC,UAAQ,aAAa;AACrB,UAAQ,gBAAgB;IACvB,EAAE,OAAO,QAAQ,CAAC;AACrB,aAAY,QAAQ,YAAY,sBAAsB,EAAE,OAAO,QAAQ,CAAC;AACxE,aAAY,QAAQ,eAAe,sBAAsB,EAAE,OAAO,QAAQ,CAAC;CAE3E,SAAS,gBAAgB,OAA+D;AACtF,SAAO,mBAAmB,MAAM,MAAM;;CAGxC,SAAS,qBAAqB,QAAsB;AAClD,MAAI,CAAC,eAAe,MAAM,SAAS,OAAO,CAAE;AAC5C,oBAAkB,QAAQ;;CAG5B,SAAS,iBAAiB,QAA8D;EACtF,MAAM,aAAa,EAAE,GAAG,QAAQ;AAChC,gBAAc,QAAQ;AACtB,2BAAyB,WAAW;AACpC,SAAO,mBAAmB,MAAM,8CAA8C,UAAU,WAAW,CAAC;;CAGtG,SAAS,oBAAoB,QAAuE;AAClG,SAAO,iBAAiB;GACtB,GAAG,cAAc;GACjB,GAAG;GACJ,CAAC;;CAGJ,SAAS,2BAAuD;AAC9D,SAAO,iBAAiB,EAAE,GAAG,SAAS,eAAe,CAAC;;CAGxD,SAAS,yBAAyB,QAAwC;AACxE,OAAK,aAAa;AAClB,UAAQ,aAAa;AACrB,UAAQ,SAAS;AACjB,iBAAe,iBAAiB;GAC9B,GAAG,eAAe;GAClB;GACD;;AAGH,QAAO;EACL;EACA;EACA;EACA;EACA,WAAW,mBAAmB;EAC9B,aAAa,mBAAmB;EAChC,qBAAqB,mBAAmB;EACxC,sBAAsB,mBAAmB;EACzC,QAAQ,mBAAmB;EAC3B,OAAO,mBAAmB;EAC1B,aAAa,mBAAmB;EAChC;EACA,eAAe,SAAS;EACxB;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,MAAM,mBAAmB;EACzB,aAAa,mBAAmB;EACjC;;AAGH,SAAS,iBAAiB,OAAuB;AAC/C,QAAO,MACJ,QAAQ,UAAU,IAAI,CACtB,QAAQ,UAAS,UAAS,MAAM,aAAa,CAAC;;AAGnD,SAAS,mBACP,gBACA,eACqC;AACrC,KAAI,kBAAkB,KAAA,EAAW,QAAO;AACxC,QAAO;EACL,GAAI,kBAAkB,EAAE;EACxB,eAAe;GACb,GAAI,gBAAgB,iBAAiB,EAAE;GACvC,GAAG;GACJ;EACF;;;;ACrSH,IAAM,aAA6B;CAAC;CAAK;CAAK;CAAK;CAAI;AAEvD,IAAM,gBAAyE;CAC7E,GAAG;EAAE,MAAM;EAAG,MAAM;EAAG;CACvB,IAAI;EAAE,MAAM;EAAG,MAAM;EAAG;CACxB,IAAI;EAAE,MAAM;EAAG,MAAM;EAAG;CACxB,IAAI;EAAE,MAAM;EAAG,MAAM;EAAG;CACxB,IAAI;EAAE,MAAM;EAAG,MAAM;EAAG;CACxB,IAAI;EAAE,MAAM;EAAG,MAAM;EAAI;CACzB,KAAK;EAAE,MAAM;EAAI,MAAM;EAAI;CAC5B;AAED,IAAI,kBAAkB;AAEtB,SAAS,aAAqB;AAC5B;AACA,QAAO,QAAQ,KAAK,KAAK,CAAC,GAAG;;;AAI/B,SAAgB,cACd,cACA,SACqB;CACrB,MAAM,gBAAgB,SAAS,iBAAiB;CAChD,MAAM,gBAAgB,SAAS,0BAA0B;CACzD,MAAM,WAAW,SAAS,YAAY;CACtC,MAAM,WAAW,SAAS,YAAY;CAEtC,SAAS,kBAAkB,MAAe,WAA0B;EAClE,MAAM,OAAO,YAAY,aAAa,KAAK,WAAW;AACtD,SAAO;GACL,IAAI,YAAY;GAChB,MAAM,QAAQ,QAAQ,MAAM,MAAM,SAAS;GAC3C,QAAQ;GACR;GACA,iBAAiB;GACjB,OAAO,EAAE;GACV;;CAGH,MAAM,QAAQ,IACZ,gBAAgB,aAAa,SAAS,IAClC,aAAa,KAAI,OAAM,EAAE,GAAG,GAAG,EAAE,GACjC,CAAC,kBAAkB,UAAU,EAAE,CAAC,CACrC;CAED,MAAM,eAAe,IAAY,MAAM,MAAM,IAAI,MAAM,GAAG;CAE1D,MAAM,aAAa,eACjB,MAAM,MAAM,MAAK,MAAK,EAAE,OAAO,aAAa,MAAM,IAAI,MAAM,MAAM,GACnE;CAED,SAAS,QAAQ,MAAqB;AACpC,MAAI,MAAM,MAAM,UAAU,SACxB,QAAO,MAAM,MAAM,MAAM,MAAM,SAAS;EAE1C,MAAM,OAAO,kBAAkB,MAAM,MAAM,MAAM,OAAO;AACxD,QAAM,MAAM,KAAK,KAAK;AACtB,eAAa,QAAQ,KAAK;AAC1B,SAAO;;CAGT,SAAS,WAAW,QAAsB;AACxC,MAAI,MAAM,MAAM,UAAU,SAAU;EACpC,MAAM,QAAQ,MAAM,MAAM,WAAU,MAAK,EAAE,OAAO,OAAO;AACzD,MAAI,UAAU,GAAI;AAClB,QAAM,MAAM,OAAO,OAAO,EAAE;AAC5B,MAAI,aAAa,UAAU,UAAU,MAAM,MAAM,GAC/C,cAAa,QAAQ,MAAM,MAAM,GAAG;;CAIxC,SAAS,aAAa,WAAmB,SAAuB;AAC9D,MAAI,YAAY,KAAK,aAAa,MAAM,MAAM,OAAQ;AACtD,MAAI,UAAU,KAAK,WAAW,MAAM,MAAM,OAAQ;AAClD,MAAI,cAAc,QAAS;EAC3B,MAAM,CAAC,SAAS,MAAM,MAAM,OAAO,WAAW,EAAE;AAChD,MAAI,MACF,OAAM,MAAM,OAAO,SAAS,GAAG,MAAM;;CAIzC,SAAS,WAAW,QAAgB,MAA2B;EAC7D,MAAM,OAAO,MAAM,MAAM,MAAK,MAAK,EAAE,OAAO,OAAO;AACnD,MAAI,CAAC,KAAM;AACX,MAAI,KAAK,SAAS,KAAA,EAAW,MAAK,OAAO,KAAK;AAC9C,MAAI,KAAK,WAAW,KAAA,EAAW,MAAK,SAAS,KAAK;AAClD,MAAI,KAAK,SAAS,KAAA,EAAW,MAAK,OAAO,KAAK;AAC9C,MAAI,KAAK,oBAAoB,KAAA,EAAW,MAAK,kBAAkB,KAAK;AACpE,MAAI,KAAK,UAAU,KAAA,EAAW,MAAK,QAAQ,KAAK;;CAGlD,SAAS,cAAc,QAAsB;AAC3C,MAAI,MAAM,MAAM,MAAK,MAAK,EAAE,OAAO,OAAO,CACxC,cAAa,QAAQ;;CAIzB,SAAS,YAAY,QAAgB,QAAgB,MAA2B;EAC9E,MAAM,OAAO,MAAM,MAAM,MAAK,MAAK,EAAE,OAAO,OAAO;AACnD,MAAI,CAAC,KAAM;AACX,OAAK,MAAM,UAAU;GAAE,GAAG,KAAK,MAAM;GAAS,GAAG;GAAM;;CAGzD,SAAS,UAAU,QAAgB,QAAsB;EACvD,MAAM,OAAO,MAAM,MAAM,MAAK,MAAK,EAAE,OAAO,OAAO;AACnD,MAAI,CAAC,KAAM;AACX,SAAO,KAAK,MAAM;;CAGpB,SAAS,cAAc,QAAsB;EAC3C,MAAM,OAAO,MAAM,MAAM,MAAK,MAAK,EAAE,OAAO,OAAO;AACnD,MAAI,CAAC,KAAM;AACX,OAAK,QAAQ,EAAE;;CAGjB,SAAS,WAAW,QAAgB,SAAiB,KAAW;EAC9D,MAAM,OAAO,MAAM,MAAM,MAAK,MAAK,EAAE,OAAO,OAAO;AACnD,MAAI,CAAC,KAAM;EAEX,MAAM,SAAS,cAAc,KAAK;AAClC,MAAI,CAAC,OAAQ;EAEb,IAAI,UAAU;AACd,OAAK,IAAI,MAAM,GAAG,MAAM,OAAO,MAAM,MACnC,MAAK,IAAI,MAAM,GAAG,MAAM,OAAO,MAAM,OAAO;GAE1C,MAAM,SAAS,GADE,OAAO,aAAa,KAAK,IAAI,GACjB,MAAM;AAEnC,OAAI,CAAC,KAAK,MAAM,SAAS,YAAY;IACnC,MAAM,YAAY,OAAO,QAAQ,CAAC,SAAS,GAAG,IAAI;AAClD,SAAK,MAAM,UAAU;KACnB,IAAI;KACJ;KACA;KACA,OAAO;KACP,YAAY;KACZ,UAAU;MACR,OAAO,GAAG,SAAS;MACnB,iBAAiB,KAAK;MACtB,gBAAgB;MACjB;KACF;;AAEH;;;CAKN,SAAS,cAA8E;EACrF,MAAM,SAAyE,EAAE;AACjF,OAAK,MAAM,QAAQ,MAAM,MACvB,MAAK,MAAM,CAAC,QAAQ,SAAS,OAAO,QAAQ,KAAK,MAAM,CACrD,QAAO,KAAK;GAAE,QAAQ,KAAK;GAAI;GAAQ;GAAM,CAAC;AAGlD,SAAO;;CAGT,MAAM,mBAAmB,eAAe;EACtC,IAAI,QAAQ;AACZ,OAAK,MAAM,QAAQ,MAAM,MACvB,UAAS,OAAO,KAAK,KAAK,MAAM,CAAC;AAEnC,SAAO;GACP;CAEF,SAAS,QAAc;EACrB,MAAM,OAAO,kBAAkB,UAAU,EAAE;AAC3C,QAAM,QAAQ,CAAC,KAAK;AACpB,eAAa,QAAQ,KAAK;;AAG5B,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;ACrLH,SAAgB,mBAAmB,SAA8D;CAC/F,MAAM,SAAS,eAAe,CAAC,GAAG,QAAQ,QAAQ,OAAO,CAAC,CAAC;CAC3D,MAAM,SAAS,eAAe,CAAC,GAAG,QAAQ,QAAQ,OAAO,CAAC,CAAC;CAC3D,MAAM,SAAS,eAAe,CAAC,GAAG,QAAQ,QAAQ,OAAO,CAAC,CAAC;CAC3D,MAAM,SAAS,eAAe,QAAQ,QAAQ,OAAO,IAAI,UAAU;CACnE,MAAM,SAAS,eAAe,QAAQ,QAAQ,OAAO,IAAI,YAAY;CACrE,MAAM,cAAc,eAAe,iBAAiB,QAAQ,QAAQ,YAAY,CAAC,CAAC;CAClF,MAAM,YAAY,eAAe,IAAI,IAAI,OAAO,MAAM,CAAC;CACvD,MAAM,YAAY,eAAe,IAAI,IAAI,OAAO,MAAM,CAAC;CAEvD,MAAM,mBAAmB,eACvB,OAAO,MAAM,QAAO,UAAS,CAAC,UAAU,MAAM,IAAI,MAAM,KAAK,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,KAAK,CAAC,CACnG;CAED,MAAM,cAAc,eAClB,OAAO,MAAM,QAAO,UAAS,UAAU,MAAM,IAAI,MAAM,KAAK,CAAC,CAC9D;CAED,MAAM,cAAc,eAClB,OAAO,MAAM,QAAO,UAAS,UAAU,MAAM,IAAI,MAAM,KAAK,CAAC,CAC9D;CAED,MAAM,aAAa,eACjB,YAAY,MAAM,QAAQ,KAAK,UAAU,MAAM,MAAM,OAAO,EAAE,CAC/D;CAED,MAAM,aAAa,eACjB,YAAY,MAAM,QAAQ,KAAK,UAAU,MAAM,MAAM,OAAO,EAAE,CAC/D;CAED,MAAM,UAAU,eACd,OAAO,MAAM,UAAU,YAAY,SACnC,OAAO,MAAM,UAAU,YAAY,MACpC;CAED,MAAM,oBAAoB,eAAe;AACvC,MAAI,QAAQ,MAAO,QAAO;EAC1B,MAAM,WAAW,KAAK,IAAI,GAAG,YAAY,QAAQ,OAAO,MAAM,OAAO;EACrE,MAAM,WAAW,KAAK,IAAI,GAAG,YAAY,QAAQ,OAAO,MAAM,OAAO;EACrE,MAAM,QAAkB,EAAE;AAC1B,MAAI,WAAW,EAAG,OAAM,KAAK,GAAG,SAAS,WAAW,OAAO,QAAQ;AACnE,MAAI,WAAW,EAAG,OAAM,KAAK,GAAG,SAAS,WAAW,OAAO,QAAQ;AACnE,SAAO,OAAO,MAAM,KAAK,QAAQ;GACjC;CAEF,SAAS,gBAAgB,WAA+C;AACtE,MAAI,UAAU,MAAM,IAAI,UAAU,CAAE,QAAO;AAC3C,MAAI,UAAU,MAAM,IAAI,UAAU,CAAE,QAAO;AAC3C,SAAO;;CAGT,SAAS,aAAa,WAAmB,MAAiD;EACxF,MAAM,OAAO,eAAe,UAAU;AACtC,MAAI,SAAS,QACX,QAAO;GACL,QAAQ,CAAC,GAAG,KAAK,QAAQ,UAAU;GACnC,QAAQ,KAAK;GACd;AAEH,SAAO;GACL,QAAQ,KAAK;GACb,QAAQ,CAAC,GAAG,KAAK,QAAQ,UAAU;GACpC;;CAGH,SAAS,eAAe,WAAmB,MAAiD;AAC1F,MAAI,SAAS,QACX,QAAO;GACL,QAAQ,OAAO,MAAM,QAAO,SAAQ,SAAS,UAAU;GACvD,QAAQ,OAAO;GAChB;AAEH,SAAO;GACL,QAAQ,OAAO;GACf,QAAQ,OAAO,MAAM,QAAO,SAAQ,SAAS,UAAU;GACxD;;CAGH,SAAS,WAAiC;AACxC,SAAO;GAAE,QAAQ,EAAE;GAAE,QAAQ,EAAE;GAAE;;CAGnC,SAAS,eAAe,WAAyC;AAC/D,SAAO;GACL,QAAQ,OAAO,MAAM,QAAO,SAAQ,SAAS,UAAU;GACvD,QAAQ,OAAO,MAAM,QAAO,SAAQ,SAAS,UAAU;GACxD;;AAGH,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,iBAAiB,OAA0C;AAClE,KAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,CAAC,OAAO,SAAS,MAAM,CAAE,QAAO;AAC7E,QAAO,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC;;;;AClIvC,IAAa,kBAAoC;CAC/C;EAAE,OAAO;EAAM,QAAQ;EAAG;CAC1B;EAAE,OAAO;EAAM,QAAQ;EAAG;CAC1B;EAAE,OAAO;EAAS,QAAQ;EAAY;CACtC;EAAE,OAAO;EAAO,QAAQ;EAAI;CAC7B;AAED,IAAa,gBAAgB;CAAC;CAAM;CAAM;CAAM;CAAK;AAErD,SAAS,cAAc,QAAqC;AAC1D,QAAO,WAAW,aAAa,KAAK,KAAK,GAAG,GAAG;;AAGjD,SAAS,mBAAmB,OAAe,SAAiB,GAAW;AACrE,KAAI,UAAU,EAAG,QAAO;CACxB,MAAM,YAAY,KAAK,MAAM,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG;CAC5D,MAAM,SAAS,KAAK,IAAI,IAAI,SAAS,UAAU;AAC/C,QAAO,KAAK,MAAM,QAAQ,OAAO,GAAG;;AAGtC,SAAgB,uBACd,oBACA,OACA,QACA,oBAA4B,GACd;AACd,KAAI,QAAQ,KAAK,sBAAsB,EAAG,QAAO,EAAE;CAEnD,MAAM,UAAU,cAAc,OAAO;CACrC,MAAM,SAAuB,EAAE;AAE/B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,IACzB,QAAO,KAAK;EACV,OAAO,mBAAmB,qBAAqB,KAAK,IAAI,SAAS,EAAE,CAAC;EACpE,YAAY;EACb,CAAC;AAGJ,QAAO;;;AAeT,SAAgB,mBAA2C;CACzD,SAAS,eACP,OACA,OACA,QACA,oBAA4B,GACd;AACd,SAAO,uBAAuB,OAAO,OAAO,QAAQ,kBAAkB;;CAGxE,SAAS,WAAW,QAAsB,QAAwB,QAAsB;AACtF,SAAO,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,MAC1B,UAAU,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MACpD;;CAGH,SAAS,eAAe,QAA8B;AACpD,SAAO,OAAO,QAAQ,KAAK,MAAM,MAAM,EAAE,YAAY,EAAE;;AAGzD,QAAO;EAAE;EAAgB;EAAY;EAAgB;;;;AC9DvD,IAAa,8BAA8C;CACzD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,aAAa;EACb,iBAAiB;EACjB,WAAW;EACX,YAAY;GACV;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACL,KAAK;IACN;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACN;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACL,KAAK;IACN;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACL,KAAK;IACN;GACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,aAAa;EACb,iBAAiB;EACjB,WAAW;EACX,YAAY;GACV;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,UAAU;IACV,SAAS;IACT,SAAS;KACP;MAAE,OAAO;MAAO,OAAO;MAAO;KAC9B;MAAE,OAAO;MAAQ,OAAO;MAAQ;KAChC;MAAE,OAAO;MAAO,OAAO;MAAO;KAC9B;MAAE,OAAO;MAAQ,OAAO;MAAQ;KAChC;MAAE,OAAO;MAAS,OAAO;MAAS;KAClC;MAAE,OAAO;MAAS,OAAO;MAAS;KACnC;IACF;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACN;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACL,KAAK;IACN;GACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,aAAa;EACb,iBAAiB;EACjB,WAAW;EACX,YAAY;GACV;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACN;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,UAAU;IACX;GACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,aAAa;EACb,iBAAiB;EACjB,WAAW;EACX,YAAY;GACV;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,UAAU;IACV,SAAS;KACP;MAAE,OAAO;MAAgB,OAAO;MAAgB;KAChD;MAAE,OAAO;MAAc,OAAO;MAAc;KAC5C;MAAE,OAAO;MAAkB,OAAO;MAAkB;KACpD;MAAE,OAAO;MAAqB,OAAO;MAAqB;KAC1D;MAAE,OAAO;MAAS,OAAO;MAAS;KACnC;IACF;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,aAAa;EACb,iBAAiB;EACjB,WAAW;EACX,YAAY;GACV;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACL,KAAK;IACN;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACN;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,KAAK;IACL,KAAK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"}
|